*/
final class ObjectEncoder implements Feature\ElementAware, XmlEncoder
{
/**
* @param class-string $className
*/
public function __construct(
private readonly string $className
) {
}
/**
* @return Iso
*/
public function iso(Context $context): Iso
{
$objectAccess = lazy(static fn (): ObjectAccess => ObjectAccess::forContext($context));
return new Iso(
/**
* @param TObj|array $value
* @return non-empty-string
*/
function (object|array $value) use ($context, $objectAccess) : string {
return $this->to($context, $objectAccess(), $value);
},
/**
* @param non-empty-string|Element $value
* @return TObj
*/
function (string|Element $value) use ($context, $objectAccess) : object {
return $this->from(
$context,
$objectAccess(),
($value instanceof Element ? $value : Element::fromString($value))
);
}
);
}
/**
* @param TObj|array $data
*
* @return non-empty-string
*/
private function to(Context $context, ObjectAccess $objectAccess, object|array $data): string
{
/** @var object $data */
$data = is_array($data) ? (object) $data : $data;
$defaultAction = writeChildren([]);
$children = [
XsiAttributeBuilder::forEncodedValue(
$context,
$this,
$data,
forceIncludeXsiTargetNamespace: !$objectAccess->isAnyPropertyQualified,
),
];
foreach ($objectAccess->properties as $normalizePropertyName => $property) {
/** @var mixed $value */
$value = $objectAccess->encodeReaders[$normalizePropertyName]($data);
if ($value === null && match ($objectAccess->onNull[$normalizePropertyName]) {
NullValueHandling::Write => false,
NullValueHandling::Skip => true,
NullValueHandling::SkipWhenAbsent => !property_is_initialized($data, $normalizePropertyName),
}) {
continue;
}
$iso = $objectAccess->isos[$normalizePropertyName];
$children[] = match(true) {
$objectAccess->isAttribute[$normalizePropertyName] => $value !== null ? (new AttributeBuilder(
$property->getType(),
(string) $iso->to($value)
))(...) : $defaultAction,
$property->getName() === '_' => $value !== null
? buildValue((string) $iso->to($value))
: (new NilAttributeBuilder())(...),
default => raw((string) $iso->to($value))
};
}
return (new XsdTypeXmlElementWriter())(
$context,
writeChildren($children)
);
}
/**
* @return TObj
*/
private function from(Context $context, ObjectAccess $objectAccess, Element $data): object
{
$lookup = ElementLookup::fromElement($data);
/** @var Iso, array> $objectData */
$objectData = object_data($this->className);
$result = [];
foreach ($objectAccess->isos as $normalizePropertyName => $iso) {
/** @var Element|ElementList|string|null $value */
$value = $objectAccess->decodeReaders[$normalizePropertyName]($lookup);
/**
* @var mixed
* @psalm-suppress PossiblyNullArgument
*/
$result[$normalizePropertyName] = match(true) {
$objectAccess->isAttribute[$normalizePropertyName] => $iso->from($value),
$value !== null => $iso->from($value),
default => $objectAccess->decodeDefaultValues[$normalizePropertyName],
};
}
return $objectData->from($result);
}
}