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

Deserialize unconsistent XML to a normalized Array #188

Open
jzfgo opened this issue Jun 25, 2020 · 5 comments
Open

Deserialize unconsistent XML to a normalized Array #188

jzfgo opened this issue Jun 25, 2020 · 5 comments

Comments

@jzfgo
Copy link

jzfgo commented Jun 25, 2020

Hi,

I have this weird looking XML and I'm trying to convert it to a simple array.

So far I've managed to do most of it with keyValue and repeatingElements deserializers and a custom one for the <Child/> element.

My problem is with the <Context/> elements. Since they aren't wrapped in a parent element, I can't treat them like repeating elements and if I parse <Query/>’s children like key-value elements, I only get the first <Context/> element.

Any suggestions?

This an example of the XML that I'm trying to parse:

<Query>
	<Checkin>2020-08-01</Checkin>
	<Nights>4</Nights>
	<PropertyList>
		<Property>my-hotel</Property>
	</PropertyList>
	<DeadlineMs>500</DeadlineMs>
	<Context>
		<Occupancy>3</Occupancy>
		<UserCountry>CA</UserCountry>
		<UserDevice>tablet</UserDevice>
	</Context>
	<Context>
		<Occupancy>4</Occupancy>
		<OccupancyDetails>
			<NumAdults>2</NumAdults>
			<Children>
				<Child age="8"/>
				<Child age="5"/>
			</Children>
		</OccupancyDetails>
		<UserCountry>US</UserCountry>
		<UserDevice>mobile</UserDevice>
	</Context>
	<Context>
		<Occupancy>6</Occupancy>
		<OccupancyDetails>
			<NumAdults>4</NumAdults>
			<Children>
				<Child age="6"/>
				<Child age="10"/>
			</Children>
		</OccupancyDetails>
		<UserCountry>FR</UserCountry>
		<UserDevice>desktop</UserDevice>
	</Context>
</Query>

And this is the result that I would like to achieve:

[
	'Checkin' => '2020-08-01',
	'Nights' => 4,
	'PropertyList' => [
		'my-hotel',
	],
	'DeadlineMs' => 500,
	'Contexts' => [
		[
			'Occupancy' => 3,
			'UserCountry' => 'CA',
			'UserDevice' => 'tablet',
		],
		[
			'Occupancy' => 4,
			'OccupancyDetails' => [
				'NumAdults' => 2,
				'Children' => [
					8,
					5,
				],
			]
			'UserCountry' => 'US',
			'UserDevice' => 'mobile',
		],
		[
			'Occupancy' => 6,
			'OccupancyDetails' => [
				'NumAdults' => 4,
				'Children' => [
					6,
					10,
				],
			]
			'UserCountry' => 'FR',
			'UserDevice' => 'desktop',
		],
	],
];

Best,
Javier.

@evert
Copy link
Member

evert commented Jun 25, 2020

To achieve this, you will also need a custom deserializer for Query, or if you are willing to switch from arrays to classes for Query, you could use the class mapper, which has a feature to deserialize properties to single properties, or properties for which there can be more than 1

@jzfgo
Copy link
Author

jzfgo commented Jun 29, 2020

Hi,

Thanks for your response.

I've tried making a custom deserializer for Query but what I need is to create a new node (Contexts) to wrap the Context elements and I don't know how to do that.

I thought about using classes too, but it looks like overcomplicating things to just parse a simple request.

Best,
Javier.

@jzfgo
Copy link
Author

jzfgo commented Aug 10, 2020

Hi @evert,

As per your suggestion, I've switched to classes and, since I also intend write XML responses, I've decided to go all in and make a full blown library (its purpose is to handle updating pricing and availability in the Google Hotel Ads service):

https://github.com/bahiazul/google-hotel-ads-xml

However, I'm in a similar situation as before:

  • When parsing repeating elements that are mixed with others at the same level I end up with only the last one of them
  • When writing repeating elements, I end up with just one that combining all their values or attributes.

I've added an examples/ folder in the repo that I'm currently using for debugging. If you run php examples/query.php you can see the issues with the <Context> element when parsing and the <Property> and <Child> elements when writing.

For more info, all element classes inherit from a Base class that implements the xmlSerialize() and xmlDeserialize() methods.

There is also a Service class that inherits from the Sabre one where I'm defining all the mappings (VOs for simple elements and additional mappings for elements with attributes).

What am I doing wrong? I've read the docs and the source code from top to bottom and I still can't figure it out. Any help would be appreciated.

Best,
Javier.

@evert
Copy link
Member

evert commented Aug 10, 2020

I think in the case of $Context, the issue is that that property on the Query class is initialized as null. sabre/xml will try to figure out if something should be treated as an array by looking at it's value, and null implies to sabre/xml that it isn't.

It looks like that's an issue at at least a couple of places, so start there

@jzfgo
Copy link
Author

jzfgo commented Aug 11, 2020

Thanks @evert , I've made the modifications that you suggested but unfortunately I haven't seen any changes.

However, I've observed that, when turning off mapping to Query in Service, saber/xml does interpret multiple <Context> elements (as it should), which makes me think that the problem is with my custom deserializer.

Sadly this puts me in the same situation as before switching to classes. On the bright side, I now know where the problems exactly are. I just need to find how to fix them 😅.

Best,
Javier.

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

No branches or pull requests

2 participants