Skip to content

anyType encoder with xsi type #25

@rauanmayemir

Description

@rauanmayemir

Support Question

I tried to follow the examples and set up custom anyType encoder that could inject 'non-string' XML.

$registry->addSimpleTypeConverter(
    namespace: Xmlns::xsd()->value(),
    name: 'anyType',
    encoder: new AnyTypeEncoder(new MyObjectTypeEncoder()),
);

AnyTypeEncoder is pretty much a proxy into MyObjectTypeEncoder:

/**
 * @template T of GetMyObjectRequestType|GetMyObjectResponseType
 * @implements XmlEncoder<T, string>
 */
final class MyObjectTypeEncoder implements XmlEncoder, XsiTypeCalculator
{
    private SerializerInterface $serializer;

    public function __construct()
    {
        $this->serializer = MyObjectSerializerFactory::createSerializer();
    }

    /**
     * @return Iso<T, string>
     */
    public function iso(Context $context): Iso
    {
        return new Iso(
            fn(mixed $data): string => $this->to($context, $data),
            fn(string $data): mixed => match ($context->type->getName()) {
                GetMyObjectRequestType::class => $this->serializer->deserialize($data, GetMyObjectRequestType::class, 'xml'),
                GetMyObjectResponseType::class => $this->serializer->deserialize($data, GetMyObjectResponseType::class, 'xml'),
                default => throw RestrictionException::unsupportedValueType($context->type, $data),
            },
        );
    }

    /**
     * @param T $data
     * @return non-empty-string
     */
    private function to(Context $context, mixed $data): string
    {
        $wrapped = $this->serializer->serialize(
            data: $data,
            format: 'xml',
        );

        return str_replace(['<result>', '</result>'], '', trim_xml_header($wrapped));
    }

    public function resolveXsiTypeForValue(Context $context, mixed $value): string
    {
        return match (true) {
            $value instanceof GetMyObjectRequestType => 'getMyObjectRequestType',
            $value instanceof GetMyObjectResponseType => 'getMyObjectResponseType',
            default => ElementValueBuilder::resolveXsiTypeForValue($context, $value),
        };
    }

    public function shouldIncludeXsiTargetNamespace(Context $context): bool
    {
        return true;
    }
}

My first problem is that the resulting output is escaped and looks like this:

<data xsi:type="getMyObjectRequestType" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  &lt;ns-001db19f:field1 xmlns:ns-001db19f=&quot;http://example.com/ns/my/object&quot;&gt;value1&lt;/ns-001db19f:field1&gt;
  &lt;ns-001db19f:field2 xmlns:ns-001db19f=&quot;http://example.com/ns/my/object&quot;&gt;value2&lt;/ns-001db19f:field2&gt;
  &lt;ns-001db19f:field3 xmlns:ns-001db19f=&quot;http://example.com/ns/my/object&quot;&gt;value3&lt;/ns-001db19f:field3&gt;
</data>

whereas I want it to be a proper XML:

<data xsi:type="getUserDataRequestType" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ns-001db19f:field1 xmlns:ns-001db19f="http://example.com/ns/my/object">value1</ns-001db19f:field1>
  <ns-001db19f:field2 xmlns:ns-001db19f="http://example.com/ns/my/object">value2</ns-001db19f:field2>
  <ns-001db19f:field3 xmlns:ns-001db19f="http://example.com/ns/my/object">value3</ns-001db19f:field3>
</data>

My second problem is that I'm using a JMS Serializer for an XSD schema that only defines types, i.e the there are no elements, so my serializer have not types with xml_root_name. Because of that serialized output will wrap the XML with <result> root.

What would be my best course of action to fake a root object that could be recognized as anyType mixed element?

On ext-soap-engine, I used to hack the input by wrapping it like $data = new \SoapVar(trim_xml_header($xml), XSD_ANYXML);.

While writing this, I realized that the xsd type was supposed to be anyxml. But from what I've seen in the wild, anyType should also support non-escaped output when xsi:type is explicitly not a string.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions