-
-
Notifications
You must be signed in to change notification settings - Fork 62
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
XSData cant handle elements order inside a repeating choice #296
Comments
Both instances the original and xsdata's output pass validation from lxml and xmlschema and a couple more online tools. But I won't deny something is funky here:
Should it be a wildcard? I don't know yet, I haven't seen that before, maybe I need to check the w3c test suite for something similar, homework 2.Your instance is completely out of order but it passes validation. An unbounded sequence of choice with maxOccurs=1 which is the default it's still a sequence of elements. <ns0:order xmlns:ns0="http://test.de/order">
<ns0:elem1/>
<ns0:elem1/>
<ns0:elem1base/>
<ns0:elem2/>
<ns0:elem2/>
<ns0:elem2base/>
</ns0:order> But it passes the validation, which means the order of the fields in this case doesn't matter... Now I am super curious what library gave you that output @hcw70, |
I used xjc from java to cheat a bit */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "TOrder", propOrder = {
"elem1OrElem1BaseOrElem2"
})
public class TOrder {
@XmlElements({
@XmlElement(name = "elem1", type = TElem1 .class),
@XmlElement(name = "elem1base", type = TElem1Base.class),
@XmlElement(name = "elem2", type = TElem2 .class),
@XmlElement(name = "elem2base", type = TElem2Base.class)
})
protected List<Object> elem1OrElem1BaseOrElem2;
} How do we convert that to dataclasses 😳 It has wrapped the whole sequence as a list of objects that can accept those 4 elements. The closest thing in xsdata implementations is the wildcard content: List[object] = field(
default_factory=list,
metadata=dict(
type="Wildcard",
namespace="##any",
min_occurs=0,
max_occurs=9223372036854775807,
),
) |
One strategy would be to actually convert it to a wildcard and finally start working on trying to match defined classes in the target namespace (I am postponing this for along time) edit Or a wildcard with a predefined set of accepted classes like xjc content: List[object] = field(
default_factory=list,
metadata=dict(
type="Wildcard",
elements=set(xxx, yyy, zzz),
namespace="##any",
min_occurs=0,
max_occurs=9223372036854775807,
),
) |
The actual problem i am having is that the order of elements is not preserved during iterating in the memory representation. I have simplified the type structure towards this example, so having empty complex types may look weird. If i omit the intermediate complex types (see #262) it works well (after your fixed it 8-> ). |
Sequences were an easy fix, here we want to group fields with mixed types in the same list with different qualified names. I have a prototype solution ready but I am facing various issues the most hard to crack is having multiple elements that refer to the same type, there is a need for a Generic wrapper with the tag name. dress_size_or_medium_dress_size_or_small_dress_size_or_smlx_size_or_xsmlx_size: List[Union[XsmlxsizeType, str, SmlxsizeType]] = field(
default_factory=list,
metadata={
"name": "",
"type": "Elements",
"choices": (
{
"name": "dressSize",
"type": Type[str],
"min_inclusive": "2",
"max_inclusive": "18",
"pattern": r"\d{1,2}",
},
{
"name": "mediumDressSize",
"type": Type[str],
"min_inclusive": "8",
"max_inclusive": "12",
"pattern": r"\d{1,2}",
},
{
"name": "smallDressSize",
"type": Type[str],
"min_inclusive": "2",
"max_inclusive": "6",
"pattern": r"\d{1}",
},
{
"name": "smlxSize",
"type": Type[SmlxsizeType],
}, <sizes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="chapter09.xsd">
<dressSize>06</dressSize>
<mediumDressSize>12</mediumDressSize>
<smallDressSize>6</smallDressSize>
<smlxSize>extra
large</smlxSize>
<xsmlxSize>extra small</xsmlxSize>
</sizes> The resulting model, is quite ugly and the necessary logic in the parser/serialization quite complex, I am also looking at smarter solutions to maintain the order somehow but so far I haven't come up with a solution that doesn't break the no1 rule of xsdata, which is to generate models with no dependencies. I am working on it 😄 , it will take some time as this is affected by some old design decisions. |
What about storing the document order in a special attribute ("documentOrder"), then iterate over a sort by that attribute on output? Or keeping the elements in a linear list upon parsing? |
Hmm. Now had time to try this out. The approach looks ok to lump different stuff into a single property looks ok. What however is puzzling me is that the XSD-well defined names like "datatypes" / "packets" / "networks" are no longer present So from that i would conclude that one needs acutally both approaches:
The name of the syntetic attribute "datatypesOrpacketsOrnetworks" does not matter much (i would have called it maybe So maybe a "magic" attribute which is called datatype[] and returns the first instance of type "TDataType" from datatypesOrpacketsOrnetworks when called via |
I want xsdata and particularly the binding models to remain as simple as possible, simple dataclasses with strict types and some metadata with no dependencies, not even xsdata itself. A lot of inspiration comes from jaxb. That's why I had to go with the compound field solution, (list). We can't have them both I am afraid, it's either order or separate fields, jaxbreference Since it's a list of mixed type objects, we needed a wrapper to maintain the association between element and value, because in a repeating choice more than one element can have the same type. <xs:choice maxOccurs="10">
<xs:element name="a" type="xs:string" />
<xs:element name="b" type="xs:string" />
</xs:choice> I still want to work on that aspect and use the wrapper only when it's really necessary and not every time, that's an improvement already in progress. So in your case if your choice elements all have unique types you wont have to deal with the wrapper. #307 About the name yes the generator will try to concatenate them if the number of choices is up to three xsdata/xsdata/codegen/sanitizer.py Lines 80 to 92 in 28ad8c1
About the "magic" attribute I am afraid is out of the question it doesn't fit the philosophy of simple binding dataclasses. |
Ok, agreed, but maybe we can agree to have the name "choice" (or similar) per default (or make an config option for it) so i can inject my "magic" attribute and always stick to the same container attribute name to lookup my type? With current solution to concat several names means that i get api changes if i add another option to an existing choice, which IMHO is not necessary, since from the XSD that would mean an upward compatible change. |
The name will default to You can always add an alias in your config, (Note to self maybe the aliases should be case insensitive)
If the api changes it's normal to expect the models to change as well. Personally I try not to add logic in domain models, it's easier to maintain integrations and with mypy inspections you can always quickly detect issues or breaking changes. |
Similair to #262
XSD:
Instance:
gives:
The text was updated successfully, but these errors were encountered: