Skip to content

Commit

Permalink
Allow to rename XML nested structure elements on a loader level (#1203)
Browse files Browse the repository at this point in the history
* Allow to rename XML nested structure elements on a loader level

* Allow to pass xml attributes to XML Loader
  • Loading branch information
norberttech authored Aug 31, 2024
1 parent 7fcbbef commit 45c1ad8
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, int>
*/
private array $writes = [];

/**
* @var array<string, string>
*/
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
) {
}
Expand All @@ -48,16 +65,88 @@ 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
);

$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<string, string> $xmlAttributes
*/
public function withXMLAttributes(array $xmlAttributes) : self
{
$this->xmlAttributes = $xmlAttributes;

return $this;
}

/**
* @param array<Partition> $partitions
*/
Expand All @@ -72,7 +161,9 @@ public function write(Rows $nextRows, array $partitions, FlowContext $context, R
$this->writes[$stream->path()->path()] = 0;
}

$stream->append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\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('<?xml ' . $xmlAttributes . "?>\n<" . $this->rootElementName . ">\n");
} else {
$stream = $streams->writeTo($this->path, $partitions);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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()),
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
) {

}
Expand All @@ -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;
Expand All @@ -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))
);
}

Expand Down
23 changes: 15 additions & 8 deletions src/adapter/etl-adapter-xml/src/Flow/ETL/Adapter/XML/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}

0 comments on commit 45c1ad8

Please sign in to comment.