Parser
Parser copied to clipboard
Bug or feature? XML reply with 1 entry behaves differently with n entries
Good morning,
I am not sure whether this is a bug or a feature or if I am using Parser in a wrong way.
Take the following XML:
<?xml version="1.0"?>
<GetFeedSubmissionListResponse xmlns="http://mws.amazonaws.com/doc/2009-01-01/">
<GetFeedSubmissionListResult>
<HasNext>false</HasNext>
<FeedSubmissionInfo>
<FeedProcessingStatus>_IN_PROGRESS_</FeedProcessingStatus>
<FeedType>_POST_FBA_INBOUND_CARTON_CONTENTS_</FeedType>
<FeedSubmissionId>40737016817</FeedSubmissionId>
<StartedProcessingDate>2016-01-17T21:13:27+00:00</StartedProcessingDate>
<SubmittedDate>2016-01-17T21:12:53+00:00</SubmittedDate>
</FeedSubmissionInfo>
<FeedSubmissionInfo>
<FeedProcessingStatus>_DONE_</FeedProcessingStatus>
<FeedType>_POST_FBA_INBOUND_CARTON_CONTENTS_</FeedType>
<FeedSubmissionId>40736016817</FeedSubmissionId>
<StartedProcessingDate>2016-01-17T21:12:38+00:00</StartedProcessingDate>
<SubmittedDate>2016-01-17T21:12:06+00:00</SubmittedDate>
<CompletedProcessingDate>2016-01-17T21:13:13+00:00</CompletedProcessingDate>
</FeedSubmissionInfo>
<FeedSubmissionInfo>
<FeedProcessingStatus>_DONE_</FeedProcessingStatus>
<FeedType>_POST_FBA_INBOUND_CARTON_CONTENTS_</FeedType>
<FeedSubmissionId>40735016817</FeedSubmissionId>
<StartedProcessingDate>2016-01-17T21:07:07+00:00</StartedProcessingDate>
<SubmittedDate>2016-01-17T21:07:01+00:00</SubmittedDate>
<CompletedProcessingDate>2016-01-17T21:07:53+00:00</CompletedProcessingDate>
</FeedSubmissionInfo>
<FeedSubmissionInfo>
<FeedProcessingStatus>_DONE_</FeedProcessingStatus>
<FeedType>_POST_FBA_INBOUND_CARTON_CONTENTS_</FeedType>
<FeedSubmissionId>40734016817</FeedSubmissionId>
<StartedProcessingDate>2016-01-17T19:04:13+00:00</StartedProcessingDate>
<SubmittedDate>2016-01-17T19:03:55+00:00</SubmittedDate>
<CompletedProcessingDate>2016-01-17T19:04:54+00:00</CompletedProcessingDate>
</FeedSubmissionInfo>
</GetFeedSubmissionListResult>
<ResponseMetadata>
<RequestId>608dc493-ca8a-4b4d-99a5-7775269e2ac6</RequestId>
</ResponseMetadata>
</GetFeedSubmissionListResponse>
If I parse this using
$responseItems = $requestStatus->parsedResponse['GetFeedSubmissionListResult']['FeedSubmissionInfo'];
my $responseItems contain an array of four items from $responseItems[0] to $responseItems[3] where I can access all attributes like FeedSubmissionId for example like $responseItems[1]['FeedSubmissionId'] - perfect.
However, if the XML response only has ONE FeedSubmissionInfo item, like this:
<?xml version="1.0"?>
<GetFeedSubmissionListResponse xmlns="http://mws.amazonaws.com/doc/2009-01-01/">
<GetFeedSubmissionListResult>
<HasNext>false</HasNext>
<FeedSubmissionInfo>
<FeedProcessingStatus>_IN_PROGRESS_</FeedProcessingStatus>
<FeedType>_POST_FBA_INBOUND_CARTON_CONTENTS_</FeedType>
<FeedSubmissionId>40737016817</FeedSubmissionId>
<StartedProcessingDate>2016-01-17T21:13:27+00:00</StartedProcessingDate>
<SubmittedDate>2016-01-17T21:12:53+00:00</SubmittedDate>
</FeedSubmissionInfo>
</GetFeedSubmissionListResult>
<ResponseMetadata>
<RequestId>608dc493-ca8a-4b4d-99a5-7775269e2ac6</RequestId>
</ResponseMetadata>
</GetFeedSubmissionListResponse>
Then $responseItems[0]['FeedSubmissionId'] throws an illegal offset error, since the parser (of course?) parsed the response not like $responseItems[0]['FeedSubmissionId'] but like this: $responseItems['FeedSubmissionId'].
Now I am wondering if this is a bug or works as designed?
And what would be the workaround? Convert the whole xml to a collection and then work with that?
Thanks Andreas
The parser has no way of telling whether a value is meant to be an array unless it has multiple elements with the same names in the XML document. The workaround (if you haven't found it already) is to cast the value to an array when you retrieve it:
$responseItems = (array) $requestStatus->parsedResponse['GetFeedSubmissionListResult']['FeedSubmissionInfo'];
I haven't found a sensible solution to this. Casting to an array is pointless if there is more than one element in the child node, because it already is itself an array.
@andreasba - I know your post was a long time ago but did you find a workaround?
The only workaround I could think about is testing it yourself:
$responseItems = $requestStatus->parsedResponse['GetFeedSubmissionListResult']['FeedSubmissionInfo'];
if(!isset($responseItems[0])) {
$responseItems[0] = $responseItems;
}
Here is my workaround. Not sure how safe this is, but it's working for the cases I've tested.
/**
* Helper function. The XML Parser doesn't handle cases where there
* can be one or more elements of the same name in the structure.
* In the case of a single element, it returns an array of child keys and in the case of multiple elements, it returns an indexed array of arrays of child keys.
* This function forces an indexed array of arrays of child keys, even if there's only one element.
*
* @param mixed $nodes The parent element containing one or more elements of the same name
* @param string $key The name of the child element
* @return mixed
*/
private static function forceIndexedArray($nodes, $key)
{
if(!isset($nodes[$key][0])) {
return array($key => array(0 => $nodes[$key]));
}
return $nodes;
}
For the example in the OP, call it like:
$listResults = static::forceIndexedArray($requestStatus->parsedResponse['GetFeedSubmissionListResult'], 'FeedSubmissionInfo');
You can then (fairly) safely...
foreach ( $listResults['FeedSubmissionInfo'] as $submissionInfo) {
...
}
@wallacio - the workarounds above are pretty similar to mine :)
@andreasba I would bet that there isn't another option. :/ And I have to agree with @danhunsaker that there is no way for the package to know if it has to be a wrapped array or not.