diff --git a/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/Loader/XMLLoader.php b/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/Loader/XMLLoader.php index 4f7a672e2..f3cebcfe1 100644 --- a/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/Loader/XMLLoader.php +++ b/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/Loader/XMLLoader.php @@ -13,17 +13,34 @@ final class XMLLoader implements Closure, Loader, Loader\FileLoader { + private string $attributePrefix = '_'; + + private string $dateTimeFormat = 'Y-m-d\TH:i:s.uP'; + + private string $listElementName = 'element'; + + private string $mapElementKeyName = 'key'; + + private string $mapElementName = 'element'; + + private string $mapElementValueName = 'value'; + + private string $rootElementName = 'root'; + + private string $rowElementName = 'row'; + /** * @var array */ private array $writes = []; + /** + * @var array + */ + private array $xmlAttributes = ['version' => '1.0', 'encoding' => 'UTF-8']; + public function __construct( private readonly Path $path, - private readonly string $rootElementName, - private readonly string $rowElementName, - private readonly string $attributePrefix, - private readonly string $dateTimeFormat, private readonly XMLWriter $xmlWriter ) { } @@ -48,9 +65,15 @@ public function load(Rows $rows, FlowContext $context) : void { $normalizer = new RowsNormalizer( new EntryNormalizer( - new PHPValueNormalizer($context->config->caster(), $this->attributePrefix, $this->dateTimeFormat), - $this->attributePrefix, - $this->dateTimeFormat + new PHPValueNormalizer( + $context->config->caster(), + $this->attributePrefix, + $this->dateTimeFormat, + $this->listElementName, + $this->mapElementName, + $this->mapElementKeyName, + $this->mapElementValueName + ), ), $this->rowElementName ); @@ -58,6 +81,72 @@ public function load(Rows $rows, FlowContext $context) : void $this->write($rows, $rows->partitions()->toArray(), $context, $normalizer); } + public function withAttributePrefix(string $attributePrefix) : self + { + $this->attributePrefix = $attributePrefix; + + return $this; + } + + public function withDateTimeFormat(string $dateTimeFormat) : self + { + $this->dateTimeFormat = $dateTimeFormat; + + return $this; + } + + public function withListElementName(string $listElementName) : self + { + $this->listElementName = $listElementName; + + return $this; + } + + public function withMapElementKeyName(string $mapElementKeyName) : self + { + $this->mapElementKeyName = $mapElementKeyName; + + return $this; + } + + public function withMapElementName(string $mapElementName) : self + { + $this->mapElementName = $mapElementName; + + return $this; + } + + public function withMapElementValueName(string $mapElementValueName) : self + { + $this->mapElementValueName = $mapElementValueName; + + return $this; + } + + public function withRootElementName(string $rootElementName) : self + { + $this->rootElementName = $rootElementName; + + return $this; + } + + public function withRowElementName(string $rowElementName) : self + { + $this->rowElementName = $rowElementName; + + return $this; + } + + /** + * @param array $xmlAttributes + */ + public function withXMLAttributes(array $xmlAttributes) : self + { + $this->xmlAttributes = $xmlAttributes; + + return $this; + } + /** * @param array $partitions */ @@ -72,7 +161,9 @@ public function write(Rows $nextRows, array $partitions, FlowContext $context, R $this->writes[$stream->path()->path()] = 0; } - $stream->append("\n<" . $this->rootElementName . ">\n"); + $xmlAttributes = \implode(' ', \array_map(fn (string $key, string $value) => $key . '="' . $value . '"', \array_keys($this->xmlAttributes), \array_values($this->xmlAttributes))); + + $stream->append('\n<" . $this->rootElementName . ">\n"); } else { $stream = $streams->writeTo($this->path, $partitions); } diff --git a/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/RowsNormalizer/EntryNormalizer.php b/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/RowsNormalizer/EntryNormalizer.php index e6204adac..3fe1716a0 100644 --- a/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/RowsNormalizer/EntryNormalizer.php +++ b/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/RowsNormalizer/EntryNormalizer.php @@ -16,16 +16,14 @@ final class EntryNormalizer { public function __construct( private readonly PHPValueNormalizer $valueNormalizer, - private readonly string $attributePrefix = '_', - private readonly string $dateTimeFormat = PHPValueNormalizer::DATE_TIME_FORMAT ) { } public function normalize(Entry $entry) : XMLNode|XMLAttribute { - if (\str_starts_with($entry->name(), $this->attributePrefix)) { - return new XMLAttribute(\substr($entry->name(), \strlen($this->attributePrefix)), $entry->toString()); + if (\str_starts_with($entry->name(), $this->valueNormalizer->attributePrefix)) { + return new XMLAttribute(\substr($entry->name(), \strlen($this->valueNormalizer->attributePrefix)), $entry->toString()); } if ($entry instanceof ListEntry) { @@ -45,7 +43,7 @@ public function normalize(Entry $entry) : XMLNode|XMLAttribute IntegerEntry::class => XMLNode::flatNode($entry->name(), (string) $entry->value()), FloatEntry::class => XMLNode::flatNode($entry->name(), (string) $entry->value()), BooleanEntry::class => XMLNode::flatNode($entry->name(), $entry->value() ? 'true' : 'false'), - DateTimeEntry::class => XMLNode::flatNode($entry->name(), $entry->value()?->format($this->dateTimeFormat)), + DateTimeEntry::class => XMLNode::flatNode($entry->name(), $entry->value()?->format($this->valueNormalizer->dateTimeFormat)), EnumEntry::class => XMLNode::flatNode($entry->name(), $entry->toString()), JsonEntry::class => XMLNode::flatNode($entry->name(), $entry->value()), UuidEntry::class => XMLNode::flatNode($entry->name(), $entry->toString()), @@ -74,7 +72,7 @@ private function listToNode(ListEntry $entry) : XMLNode } foreach ($listValue as $value) { - $node = $node->append($this->valueNormalizer->normalize('element', $type->element()->type(), $value)); + $node = $node->append($this->valueNormalizer->normalize($this->valueNormalizer->listElementName, $type->element()->type(), $value)); } return $node; @@ -125,8 +123,8 @@ private function mapToNode(MapEntry $entry) : XMLNode $type = $entry->type(); foreach ($mapValue as $key => $value) { - $node = $node->append($this->valueNormalizer->normalize('key', $type->key()->type(), $key)); - $node = $node->append($this->valueNormalizer->normalize('value', $type->value()->type(), $value)); + $node = $node->append($this->valueNormalizer->normalize($this->valueNormalizer->mapElementKeyName, $type->key()->type(), $key)); + $node = $node->append($this->valueNormalizer->normalize($this->valueNormalizer->mapElementValueName, $type->value()->type(), $value)); } return $node; diff --git a/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/RowsNormalizer/EntryNormalizer/PHPValueNormalizer.php b/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/RowsNormalizer/EntryNormalizer/PHPValueNormalizer.php index 35cf63936..437d67002 100644 --- a/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/RowsNormalizer/EntryNormalizer/PHPValueNormalizer.php +++ b/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/RowsNormalizer/EntryNormalizer/PHPValueNormalizer.php @@ -14,12 +14,14 @@ final class PHPValueNormalizer { - public const DATE_TIME_FORMAT = 'Y-m-d\TH:i:s.uP'; - public function __construct( private readonly Caster $caster, - private readonly string $attributePrefix = '_', - private readonly string $dateTimeFormat = self::DATE_TIME_FORMAT + public readonly string $attributePrefix = '_', + public readonly string $dateTimeFormat = 'Y-m-d\TH:i:s.uP', + public readonly string $listElementName = 'element', + public readonly string $mapElementName = 'element', + public readonly string $mapElementKeyName = 'key', + public readonly string $mapElementValueName = 'value', ) { } @@ -42,7 +44,7 @@ public function normalize(string $name, Type $type, mixed $value) : XMLNode|XMLA } foreach ($value as $elementValue) { - $listNode = $listNode->append($this->normalize('element', $type->element()->type(), $elementValue)); + $listNode = $listNode->append($this->normalize($this->listElementName, $type->element()->type(), $elementValue)); } return $listNode; @@ -57,9 +59,9 @@ public function normalize(string $name, Type $type, mixed $value) : XMLNode|XMLA foreach ($value as $key => $elementValue) { $mapNode = $mapNode->append( - XMLNode::nestedNode('element') - ->append($this->normalize('key', $type->key()->type(), $key)) - ->append($this->normalize('value', $type->value()->type(), $elementValue)) + XMLNode::nestedNode($this->mapElementName) + ->append($this->normalize($this->mapElementKeyName, $type->key()->type(), $key)) + ->append($this->normalize($this->mapElementValueName, $type->value()->type(), $elementValue)) ); } diff --git a/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/functions.php b/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/functions.php index d97696377..4c68ebead 100644 --- a/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/functions.php +++ b/src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/functions.php @@ -6,7 +6,6 @@ use function Flow\ETL\DSL\from_all; use Flow\ETL\{Adapter\XML\Loader\XMLLoader, - Adapter\XML\RowsNormalizer\EntryNormalizer\PHPValueNormalizer, Adapter\XML\XMLWriter\DOMDocumentWriter, Attribute\DocumentationDSL, Attribute\Module, @@ -42,21 +41,29 @@ function from_xml( ); } +/** + * @param Path|string $path + * @param string $root_element_name - @deprecated use `withRootElementName()` method instead + * @param string $row_element_name - @deprecated use `withRowElementName()` method instead + * @param string $attribute_prefix - @deprecated use `withAttributePrefix()` method instead + * @param string $date_time_format - @deprecated use `withDateTimeFormat()` method instead + * @param DOMDocumentWriter $xml_writer + */ #[DocumentationDSL(module: Module::XML, type: DSLType::LOADER)] function to_xml( string|Path $path, string $root_element_name = 'rows', string $row_element_name = 'row', string $attribute_prefix = '_', - string $date_time_format = PHPValueNormalizer::DATE_TIME_FORMAT, + string $date_time_format = 'Y-m-d\TH:i:s.uP', XMLWriter $xml_writer = new DOMDocumentWriter() ) : XMLLoader { - return new XMLLoader( + return (new XMLLoader( \is_string($path) ? Path::realpath($path) : $path, - $root_element_name, - $row_element_name, - $attribute_prefix, - $date_time_format, $xml_writer - ); + )) + ->withRootElementName($root_element_name) + ->withRowElementName($row_element_name) + ->withAttributePrefix($attribute_prefix) + ->withDateTimeFormat($date_time_format); }