This is a follow up to #1846000: Add serializer support for JSON and AJAX.

That issue originally had support using Symfony's XmlEncoder class BUT as this implements NormalizationAwareInterface, Serializer does not normalize the data, the class does. Because of this it's not feasible for us to use this directly for entities.

Quoting from Lin in that issue, as a suggested path forward:

I took a closer look at the XMLEncoder. It turns out NormalizationAwareInterface works differently than I assumed from the name. It actually skips the normalizer and handles all normalization internally. If we want to use any of the functions from the TypedData API, we shouldn't register XmlEncoder as the encoder or inherit from it.

I believe we could use composition to get the desired functionality. We would implement a custom encoder (which would not be NormalizationAware). The data would go through a normalizer to be turned into the proper array structure. Then it would be passed into our custom encoder, which would instantiate an instance of XmlEncoder and shunt all the work off to that.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

damiankloip’s picture

Status: Active » Needs review
FileSize
5.19 KB

Here is an initial patch for this. I think the idea is right and this would work except adding the serializer as an argument in the container doesn't work atm (it might, I'm just not sure how to get it to). There is a circular dep issue as there are various passes to add normalizers/encoders to the serializer too.

Status: Needs review » Needs work

The last submitted patch, 1854874.patch, failed testing.

damiankloip’s picture

Status: Needs work » Needs review
FileSize
4.2 KB
5.09 KB

I think this might be better.

damiankloip’s picture

FileSize
5.04 KB

Don't need the use statement for SerializerInterface

damiankloip’s picture

Example XML string, as returned by the test:

<?xml version="1.0"?>
<response>
  <id>
    <value>1</value>
  </id>
  <revision_id>
    <value>1</value>
  </revision_id>
  <uuid>
    <value>c84e1b09-da08-4f96-9403-3b965ccdd6c9</value>
  </uuid>
  <langcode>
    <value>und</value>
  </langcode>
  <default_langcode>
    <value/>
  </default_langcode>
  <name>
    <value>nGCbib3H</value>
  </name>
  <user_id>
    <value>1</value>
  </user_id>
  <field_test_text>
    <value>bUWBFGXZ</value>
    <format>full_html</format>
  </field_test_text>
</response>
damiankloip’s picture

FileSize
5.02 KB

Just updated the docs a little.

Status: Needs review » Needs work
Issue tags: -Web services, -VDC

The last submitted patch, 1854874-4.patch, failed testing.

damiankloip’s picture

Status: Needs work » Needs review

#6: 1854874-4.patch queued for re-testing.

Status: Needs review » Needs work
Issue tags: +Web services, +VDC

The last submitted patch, 1854874-4.patch, failed testing.

damiankloip’s picture

Come on!

damiankloip’s picture

Status: Needs work » Needs review

#6: 1854874-4.patch queued for re-testing.

dawehner’s picture

Good work!

+++ b/core/lib/Drupal/Core/Serialization/XmlEncoder.phpundefined
@@ -0,0 +1,72 @@
+   * @var array
...
+  static protected $format = 'xml';

Is it an array or a string :)

+++ b/core/lib/Drupal/Core/Serialization/XmlEncoder.phpundefined
@@ -0,0 +1,72 @@
+    $this->baseEncoder = new BaseXmlEncoder();

Maybe we could inject the base xml encoder in there? Not sure though whether this makes sense.

What about adding a test which take sure that our workaround actually works? You just compare the serialized output,
though we want to be able to use another normalizer.

damiankloip’s picture

FileSize
1.32 KB
5.05 KB

Ok, I changed the $format to be an array, this is then consistent with all our other encoders. Which I think makes sense, even though there is only one format.

Injecting this sounds like a good idea, but would we ever need this? We could change the name to EncoderWrapper or something? Then pass in the XmlEncoder as the base encoder?

I'm not sure what else to test, because this is not about the normalizers. The serlizer deals with that.

dawehner’s picture

What about casting the value to an array then?

damiankloip’s picture

FileSize
2.22 KB
6.02 KB

How about we test serializing the normalized array as well as the entity like this?

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

This looks perfect as it tests both the serialization and the normalization.!

webchick’s picture

Assigned: Unassigned » linclark

Damien said it'd be good for Lin to look at this first, so assigning to her.

Anonymous’s picture

Status: Reviewed & tested by the community » Needs work

Right now, the tests just check that the XML goes through the correct normalizer and encoder. Instead, it should check whether the expected XML is generated. The normalizer might not generate the array structure required to get the expected XML.

Anonymous’s picture

Assigned: linclark » Unassigned
damiankloip’s picture

Status: Needs work » Needs review
FileSize
1.48 KB
6.11 KB

We talked about this on IRC briefly; How about this?

Anonymous’s picture

This seems a little confusing to me. We create an expected value and then test that it is the expected value. I think it would make more sense to simply use the string as the expected value.

damiankloip’s picture

FileSize
1.81 KB
5.73 KB

Ok, let's get rid of that and so our string is the expected.

Anonymous’s picture

Status: Needs review » Reviewed & tested by the community

Great, I think that makes it clearer that it's doing what we want it to do. As far as I'm concerned, this is back to RTBC.

catch’s picture

Status: Reviewed & tested by the community » Fixed

Looks good.

I really wonder about registering individual services for this in core bundle but it's only a couple on there at the moment for this, so won't complain too much atm. Committed/pushed to 8.x.

webchick’s picture

Status: Fixed » Needs work

For whatever reason, this broke testbot with the errors:

Non-pass
Test name	Pass	Fail	Exception
Drupal\system\Tests\Serialization\EntitySerializationTest	15	2	0
Message	Group	Filename	Line	Function	Status
Entity serializes to XML when "xml" is requested.	Other	EntitySerializationTest.php	137	Drupal\system\Tests\Serialization\EntitySerializationTest->testSerialize()	
A normalized array serializes to XML when "xml" is requested	Other	EntitySerializationTest.php	139	Drupal\system\Tests\Serialization\EntitySerializationTest->testSerialize()	

Rolled back 4aaa72526 for now so we can fix it up.

alexpott’s picture

FileSize
3.27 KB
6.15 KB

The regressions occurred because #1833334: EntityNG: integrate a dynamic property data table handling resulted in a new order of properties in the entity_test_mulrev entity and therefore the resulting xml.

The patch attached makes less assumptions about the order by using the normalised array to sort the xml. Basically we're making up for the fact that there is not PHP equivalent for json_encode() for xml. I've also removed some of the assumptions about the entity values to make ir a little but easier to change the test.

alexpott’s picture

Status: Needs work » Needs review

Let's test :)

Anonymous’s picture

I'll wait for the test to run, but the code involved is getting too complicated for a test, IMHO, particularly the multiple array manipulations.

damiankloip’s picture

Status: Needs review » Needs work

Oh dear... :( Good catch Alex. I guess this shows that it was a bit fragile to have the whole string in the test? And these changes DO make it easier to modify down the line. Maybe we can make it easier:

+++ b/core/modules/system/lib/Drupal/system/Tests/Serialization/EntitySerializationTest.phpundefined
@@ -130,12 +129,33 @@
+    $expected = array_merge(array_flip(array_keys($normalized)), $expected);

I can see how it is quite confusing, but I quite like this. If we want something more readable, I guess we can just use a foreach here and iterate over the array_keys OR we could just use array_merge (and drop the other two array methods) really, as the normalized values will get overwritten by the expected array values anyway, so we don't need an array of empty values.

+++ b/core/modules/system/lib/Drupal/system/Tests/Serialization/EntitySerializationTest.phpundefined
@@ -130,12 +129,33 @@
+    $expected = array_reduce($expected, function($result, $item) {
+      $result .= $item;
+      return $result;
+    });

I think just implode('', $expected) would be fine here? array_reduce seems OTT here when it's just concatenating strings.

+++ b/core/modules/system/lib/Drupal/system/Tests/Serialization/EntitySerializationTest.phpundefined
@@ -130,12 +129,33 @@
+    $this->assertIdentical($actual, $expected);
...
+    $this->assertIdentical($actual, $expected);

the assertion message haz gone :)

Alex I will let you action this, if you don't have time, let me know and I will re roll.

alexpott’s picture

Go ahead Damian... Dunno why but array_reduce was on my mind :) doh! Of course implode should be used. And reducing to just a merge makes sense too... Nice one.

I've removed the assertion messages since the default message actually makes debugging easier since it gives us the full values we're actually comparing.

Anonymous’s picture

How about we use the base encoder to deserialize and then check that against the normalized array?

damiankloip’s picture

Hmm, When the Symfony XmlEncoder decodes the xml it won't match the the original normalized data array, as it seems to remove a level of empty arrays (I think). So we could check the keys, but that's not ideal?

damiankloip’s picture

Status: Needs work » Needs review
FileSize
1.36 KB
6.04 KB

Ok, here is a new version of the patch from #26, but with some parts simplified. I'm not sure the de-serialization method will work, so for now, this is the way to go I think.

Anonymous’s picture

Which values does it remove? Just the ones that are empty to start with? In that case, we could prepare a normalized array to test against that also removes those values.

damiankloip’s picture

For example:

'id' => array(
  array('value' => 1),
)

would become...

'id' => array(
  'value' => 1,
)

Status: Needs review » Needs work
Issue tags: -Web services, -VDC

The last submitted patch, 1854874-33.patch, failed testing.

damiankloip’s picture

Status: Needs work » Needs review

#33: 1854874-33.patch queued for re-testing.

Status: Needs review » Needs work
Issue tags: +Web services, +VDC

The last submitted patch, 1854874-33.patch, failed testing.

alexpott’s picture

Drupal\user\Tests\UserAutocompleteTest failed :(

alexpott’s picture

Status: Needs review » Needs work
Issue tags: +Web services, +VDC

Retesting

alexpott’s picture

Status: Needs work » Needs review
Issue tags: -Web services, -VDC

#33: 1854874-33.patch queued for re-testing.

Issue tags: -Web services, -VDC

The last submitted patch, 1854874-33.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
Issue tags: +Web services, +VDC

#33: 1854874-33.patch queued for re-testing.

Anonymous’s picture

Status: Needs review » Reviewed & tested by the community

The new version of the test looks simple enough to me, RTBC.

Anonymous’s picture

If #1894508: Update symfony/serializer gets in before this, this patch will need to be rerolled.

webchick’s picture

Status: Reviewed & tested by the community » Fixed

Ok, re-committed and pushed to 8.x, since that issue needs a re-roll anyway.

Automatically closed -- issue fixed for 2 weeks with no activity.