-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Serializer] Add an option to the XmlEncoder
allowing to decode tags as collection
#58604
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
base: 7.3
Are you sure you want to change the base?
Conversation
Hey! I see that this is your first PR. That is great! Welcome! Symfony has a contribution guide which I suggest you to read. In short:
Review the GitHub status checks of your pull request and try to solve the reported issues. If some tests are failing, try to see if they are failing because of this change. When two Symfony core team members approve this change, it will be merged and you will become an official Symfony contributor! I am going to sit back now and wait for the reviews. Cheers! Carsonbot |
590ad7f
to
46e9197
Compare
93e0e76
to
dff07d4
Compare
XmlEncoder
allowing to decode tags as collection
Thanks @nicolas-grekas for the reviews. I applied the suggested changes. I also realized it uses Also could you share your thoughts about the goal of this PR please? 🙏 I'm quite unsure this is the right approach for this problem. I'm also unsure whether this should be handled by the decoder (right now we have a workaround with a custom normalizer looking at specific keys). |
So you figured out a way that doesn't require changing the component? then it might be the best way, because as you spotter, defining which tags in the xml tree would need this arrayification is tricky. A normalizer looks like a better place to me. |
We found a workaround, but that's not exactly what I would expect from the component. Here's the workaround we ended with, just as information: public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): mixed
{
// If the first element is not an array, wrap it in an array
if (! is_array(reset($data['plans']))) {
$data['plans'] = [$data['plans']];
}
// The return thing
} It works in our specific use case (which is why we didn't make a generic normalizer with context option and all), but I find it odd that there are no ways of telling "this XML tag should always be treated as a collection". I mean, it makes more sense to me to be handled by a decoder option, as it is (at least it's what I understand for now) directly related to the XML format. I might be wrong still. However if I'm right (that it should be handled by the decoder), I can also understand that it would be too tricky to be handled in the decoder and that the maintainers refuses to maintain such a feature. On the other hand, if you consider it can be added, I'd gladly work on a better implementation and look for more edge cases etc. EDIT: What I'm trying to say is that, as I user, I'd like to have an easy option for this when dealing with XML. But I also understand that, for maintaining reason, it would be better handled in userland code as it could lead to some unwanted behavior. |
I was also looking for something like this, great 👍 |
Not related to Laravel. However you can still use it as a dependency as the component is just like any other library. |
I know, Indeed this is my case 👍 |
if (\is_array($value) | ||
&& ($childNodeName = $node->firstChild?->nodeName) | ||
&& 1 === \count($value) | ||
&& \in_array($nodeName, $context[self::FORCE_COLLECTION] ?? $this->defaultContext[self::FORCE_COLLECTION], true) | ||
) { | ||
return [$childNodeName => [$value[$childNodeName]]]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition can go after the !\is_array($value)
if.
Then, you'll be able to drop the is_array
check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually it can't according to the test suite 👀
Looks like it actually passes the block below:
if (!\count($data)) {
return $value;
}
if (\is_array($value) | ||
&& ($childNodeName = $node->firstChild?->nodeName) | ||
&& 1 === \count($value) | ||
&& \in_array($nodeName, $context[self::FORCE_COLLECTION] ?? $this->defaultContext[self::FORCE_COLLECTION], true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it could be great to accept the value *
as an "every tag" value, WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, however I'm still unsure how this PR would be welcome. I'm okay to continue working on it, but I'd like to get a green light before investing too much time on it 😅
But if it get a positive feedback (which doesn't seem to be the case for the moment) I can consider adding a test case for this, maybe using xpath also 🤔
You are using only the encode/decode functions, right? Because for my project when i got something like <order_rows>
<order_row></order_row>
</order_rows> I use |
@Hanmac what if your XML ends up looking like this: <order_rows>
</order_rows> Does it handle it well? 🤔 |
That should be skipped the only problem i have with this approach, (or in general) are empty collections when normalizing/encoding: #[SerializedPath("[order_rows][order_row]")]
protected array $rows = []; if for some reason, the rows are empty, than current AbstractObjectNormalizer does normalize it to this:
which then the Encoder does turn into this: <order_rows>
<order_row />
</order_rows> for this to work better, i added a Normalizer that turns empty arrays into i think i mentioned them in another ticket before, might need to create a new one => #58768 |
Hey |
Context
I was working on a project where the Serializer component really makes our live easier: we have to work with an XML file as source of truth. The serializer allows us to work on PHP objects really easily and that's a real pleasure to work with it.
However, we have a specific part that is not doing exactly what we would like, and I didn't find a proper solution for it.
The issue
Given this XML content:
When we decode it to an array, we have this:
And that is what we actually expect. However, if we have only one item (one
order_row
here) we end up with this:And the PHP array:
A possible solution
I thought I could use the
AS_COLLECTION
option with the context to do what we expected but it seems that it is not an option for this use case after all. So I was thinking about introducing an optionFORCE_COLLECTION
that would actually fix it for the given fields. At this point I'm not sure what should be the possible values of this option, so for the moment I made it quite simple by just using the tag name (order_rows
here).I might missed something while I was doing my research as I didn't get a lot of results (maybe be wrong keywords or vocabulary).
It would be usable like this:
Possible improvements
My proposal is quite simple for the moment as I don't think I understand the context of the serializer well enough and I would like to get some feedbacks from maintainers, contributors or anyone with some knowledge about the serializer component.
It might lack good support, for nested properties etc maybe (might be better to support xpath instead of a tag name and so on).
Also I might just have the wrong approach of the problem, I'm open to any feedback ahah