Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XML deserialization with inner XmlNamespaces does not work #994

Closed
GonocraM opened this issue Sep 5, 2018 · 9 comments
Closed

XML deserialization with inner XmlNamespaces does not work #994

GonocraM opened this issue Sep 5, 2018 · 9 comments
Labels

Comments

@GonocraM
Copy link
Contributor

GonocraM commented Sep 5, 2018

Q A
Bug report? yes
Feature request? no
BC Break report? no
RFC? no

Steps required to reproduce the problem

When we have an XML with a child node with a xml namespace the deserialization doesn't work as intended. I've seen this error only if the xmlns doesn't have prefix and it's in a child element, when it's in the root node works well in all of its forms.

For Example

use JMS\Serializer\SerializerBuilder;
use JMS\Serializer\Annotation as JMS;

require_once 'vendor/autoload.php';

/**
 * Class Simple
 *
 * @JMS\XmlNamespace(uri="http://www.example.org/abc")
 *
 * @package Demo
 */
class Simple
{
    /**
     * @JMS\Type("string")
     */
    public $property1;

    /**
     * @JMS\Type("string")
     */
    public $property2;

}

/**
 * Class Envelope
 * @JMS\XmlRoot("Envelope")
 * @package Demo
 */
class Envelope
{
    /**
     * @JMS\Type("Simple")
     * @JMS\SerializedName("Simple")
     */
    public $simple;
}

$xmlWithEnvelope = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
    <Simple xmlns="http://www.example.com/abc" >
        <property1>bar</property1>
        <property2>foo</property2>
    </Simple>
</Envelope>
XML;

$serializer = SerializerBuilder::create()->build();

$objectWithEnvelope = $serializer->deserialize($xmlWithEnvelope, Envelope::class, 'xml');

var_dump($objectWithEnvelope);

Expected Result

object(Envelope)#61 (1) {
  ["simple"]=>
  object(Simple)#71 (2) {
    ["property1"]=>
    string(3) "bar"
    ["property2"]=>
    string(3) "foo"
  }
}

Actual Result

As we can see the object with the namespace is not deserialized

object(Envelope)#61 (1) {
  ["simple"]=>
  NULL
}
@goetas
Copy link
Collaborator

goetas commented Sep 10, 2018

attentiuon that <property1> is in the http://www.example.com/abc namespace, so you should use @XmlElement(namespace="http://www.example.com/abc") on your $property1

see example in http://jmsyst.com/libs/serializer/master/reference/annotations#xmlnamespace

@GonocraM
Copy link
Contributor Author

Thanks for your response.

But unfortunately the solution provided didn't work. The result is the same as before.

Here is the code changed and the actual results:

use JMS\Serializer\SerializerBuilder;
use JMS\Serializer\Annotation as JMS;

require_once 'vendor/autoload.php';

/**
 * @JMS\XmlNamespace(uri="http://www.example.org/abc")
 * @JMS\XmlRoot("Simple")
 */
class Simple
{
    /**
     * @JMS\Type("string")
     * @JMS\XmlElement(namespace="http://www.example.com/abc", cdata=false)
     */
    public $property1;

    /**
     * @JMS\Type("string")
     * @JMS\XmlElement(namespace="http://www.example.com/abc", cdata=false)
     */
    public $property2;

}

/**
 * @JMS\XmlRoot("Envelope")
 */
class Envelope
{
    /**
     * @JMS\Type("Simple")
     * @JMS\SerializedName("Simple")
     */
    public $simple;
}

$serializer = SerializerBuilder::create()->build();

$xmlWithEnvelope = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
    <Simple xmlns="http://www.example.com/abc">
        <property1>bar</property1>
        <property2>foo</property2>
    </Simple>
</Envelope>
XML;

$objectWithEnvelope = $serializer->deserialize($xmlWithEnvelope, Envelope::class, 'xml');
echo "initial XML:\n";var_dump($xmlWithEnvelope);
echo "\ndeserialized object:\n";var_dump($objectWithEnvelope);
echo "\nserialized XML:\n";var_dump($serializer->serialize($objectWithEnvelope, 'xml'));

Result:

initial XML:
string(193) "<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
    <Simple xmlns="http://www.example.com/abc">
        <property1>bar</property1>
        <property2>foo</property2>
    </Simple>
</Envelope>"

deserialized object:
object(Envelope)#61 (1) {
  ["simple"]=>
  NULL
}

serialized XML:
string(51) "<?xml version="1.0" encoding="UTF-8"?>
<Envelope/>
"

@goetas
Copy link
Collaborator

goetas commented Sep 11, 2018

you forgot to add the NS on $simple prop

@GonocraM
Copy link
Contributor Author

Thank you! I have added the NS on $simple prop and now it's deserializing well, but the serialization doesn't return the initial XML.

Is there anything else that i'm missing?

Here is the current output:

initial XML:
string(193) "<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
    <Simple xmlns="http://www.example.com/abc">
        <property1>bar</property1>
        <property2>foo</property2>
    </Simple>
</Envelope>"

deserialized object:
object(Envelope)#64 (1) {
  ["simple"]=>
  object(Simple)#82 (2) {
    ["property1"]=>
    string(3) "bar"
    ["property2"]=>
    string(3) "foo"
  }
}

serialized XML:
string(301) "<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
  <ns-e6e3af3d:Simple xmlns:ns-e6e3af3d="http://www.example.com/abc" xmlns="http://www.example.org/abc">
    <ns-e6e3af3d:property1>bar</ns-e6e3af3d:property1>
    <ns-e6e3af3d:property2>foo</ns-e6e3af3d:property2>
  </ns-e6e3af3d:Simple>
</Envelope>
"

Thanks again for your support, appreciated.

@goetas
Copy link
Collaborator

goetas commented Sep 11, 2018

you can put @XmlNamespace(uri="http://www.example.com/ab", prefix="my") on $simple prop to decide the prefix to use

(EDIT: probably you will have to put it on the Envelope class (not on the prop) )

@GonocraM
Copy link
Contributor Author

Thanks for the suggestion but the initial XML is not generated from me. It's fetched from an external webservice.

The idea is, given a PHP object like this

object(Envelope)#64 (1) {
  ["simple"]=>
  object(Simple)#82 (2) {
    ["property1"]=>
    string(3) "bar"
    ["property2"]=>
    string(3) "foo"
  }
}

Obtain a serialized XML with this exact string value:

<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
  <Simple xmlns="http://www.example.com/abc">
    <property1>bar</property1>
    <property2>foo</property2>
  </Simple>
</Envelope>

I mean, with a nested namespace without any prefix, and not getting the default namespace prefix.

is this kind of serialization possible?

Sorry for the inconvenience! Thank you so much :)

@goetas
Copy link
Collaborator

goetas commented Sep 11, 2018

Semantically

<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
  <Simple xmlns="http://www.example.com/abc">
    <property1>bar</property1>
    <property2>foo</property2>
  </Simple>
</Envelope>

and

<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
  <p:Simple xmlns:p="http://www.example.com/abc">
    <p:property1>bar</p:property1>
    <p:property2>foo</p:property2>
  </p:Simple>
</Envelope>

are equivalent

@GonocraM
Copy link
Contributor Author

You're right, again :)

Your last comment helped me to get a nice solution with my related issue.

Thank you so much for your support and your work with this excellent library!

@philippeamar
Copy link

I have a similar problem but mine is not solved. The only difference in my case is that xmlns attribute is defined in the root node "Envelope". @goetas I'm not sure to have well apply your suggestion #994 (comment). Could you please show the modification you've made ? @GonocraM

deserialized object:
object(Envelope)#61 (1) {
["simple"]=>
array(0) {
}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants