Ugly XmlSerializer bug that generates invalid XML for required+fixed attributes.
Note: this entry has moved.
One common way of signaling the version of a configuration file that the processor of that file understands, is having an attribute (i.e. SchemaVersion) with the attributes use="required" fixed="[required version]". This way, all config files need to include such an attribute. When you evolve the schema, you can allow say "2.0" and most probably have XSLTs that automatically upgrades older versions, based on the version number you receive.
But this will not work with the XmlSerializer, not in Everett, neither in Whidbey so far.
The XSD 2 classes conversion process transforms an attribute that is both required and has a fixed value into the following class member (will be just a public field in v1.x):
[System.ComponentModel.DefaultValueAttribute("1.0")]
public string SchemaVersion {
get {
return this.schemaVersionField;
}
set {
this.schemaVersionField = value;
}
}
The class constructor will initialize the field value to the fixed value specified in the XSD (and the serialization attribute).
When the XmlSerializer finds such a property, it generates the following code:
if (((global::System.String)o.@SchemaVersion) != @"1.0") {
WriteAttribute(@"SchemaVersion", @"", ((global::System.String)o.@SchemaVersion));
}
This means that it will only serialize the attribute to the XML output if it was given a value other than the required one. This rule doesn't make sense. This means it will generate XML that doesn't validate against the schema it was created from.
So if you create an instance of the class (or deserialize from a valid XML file), assign the required property to the required value (which isn't necessary because it will already have the initialized value), that property it will never get serialized, no matter what.
This effectively means that you not roundtrip: valid XML -> Object Model -> valid XML. The third step will always be invalid, as the attribute is not emited.
Repro:
- Create an XSD file with an element that has an attribute required and with a fixed value.
- Call XSD.EXE /c with it to generate the class.
- Create an XML that is valid according to the schema.
- Create a console app that uses the XmlSerializer to deserialize the XML into the classes, using XSD validating reader
- Use the serializer to serialize the in-memory representation of the XML into a stream again
- Try to deserialize again with this new stream, using an XSD validating reader.
Solution to the problem may be to have a new XML Serialization attribute which signals the attribute has a fixed value (so it should always be emited, unlike the DefaultValue one).
Please vote for this bug, I think it's an important one.