Preserve serialization-order of members in CodeDOM
Asked Answered
P

1

27

I know we can enforce generating the members within the classes in the same order as within the members-collection as stated on MSDN. However I look for some code that also adds a serialization-attribute providing the order of those members. So this is what I want the generator to create:

class MyClass
{
    [XmlElement("TheProperty", Order = 0]
    public int MyProperty { get; set; }
    [XmlElement("AnotherProperty", Order = 1]
    public int AnotherProperty { get; set; }
}

Currently I have an approach that loops the members of all types within my DOM and appends the attribute to the CustomAttributes-member of the current (public) property or field.

var types = code.Types.Cast<CodeTypeDeclaration>().Where(x => !x.IsEnum);
foreach (var members in types.Select(x => x.Members.Cast<CodeTypeMember>()))
{
    int i = 0;
    var propertiesAndFields = members.Where(x => (
            x.Attributes & MemberAttributes.Private) != MemberAttributes.Private
            && (x is CodeMemberField || x is CodeMemberProperty));
    foreach (CodeTypeMember member in propertiesAndFields)
    {
        var attr = member.CustomAttributes.Cast<CodeAttributeDeclaration>().FirstOrDefault(x => x.Name == "System.Xml.Serialization.XmlElementAttribute");
        if (attr == null)
        {
            attr = new CodeAttributeDeclaration("System.Xml.Serialization.XmlElementAttribute");
            member.CustomAttributes.Add(attr);
        }
        attr.Arguments.Add(new CodeAttributeArgument("Order", new CodePrimitiveExpression(i++)));

    }
}

However this seems quite hacky to me and I wonder if there were a member built into CodeDOM that creates the Order-attributes. I remember the Xsd-tool (which I want to extent with custom behaviour using CodeDOM and which uses the same classes and interfaces) is able to append those attributes.

EDIT: The codeDOM is created using the XmlSchemaImporter- and XmlCodeExporter-class as mentioned on MSDN:

XmlSchemas schemas = new XmlSchemas();
schemas.Add(schema);            
// Create the importer for these schemas.
XmlSchemaImporter importer = new XmlSchemaImporter(schemas);
// System.CodeDom namespace for the XmlCodeExporter to put classes in.
CodeNamespace code = new CodeNamespace(targetNamespace);
XmlCodeExporter exporter = new XmlCodeExporter(code);
// Iterate schema top-level elements and export code for each.
foreach (XmlSchemaElement element in schema.Elements.Values)
{
    // Import the mapping first.
    XmlTypeMapping mapping = importer.ImportTypeMapping(element.QualifiedName);
    // Export the code finally
    exporter.ExportTypeMapping(mapping);
}

I can´t see a way to provide the order-attributes here, this is why I want to set them after the DOM has already been created.

Passel answered 2/8, 2016 at 8:17 Comment(9)
Doesn't seem so hacky to me (I used to write a lot of CodeDom, and it was all about hacks)Reinsure
My only real suggestion is to rewrite slightly like this: var attr = member.CustomAttributes.Cast<CodeAttributeDeclaration>().FirstOrDefault(x => x.Name == typeof(XmlElementAttribute).FullName);Santalaceous
At the same time, I would assume that you've got access to the code where the actual CodeDOM model is generated in the first place - i.e. where the members are generated in the object model. I would look at changing the code in that area rather than post processing the model after it has been generated, and hence the hackiness that you are expressing.Santalaceous
@MelbourneDeveloper Thanks for the suggestion. Your second tip sounds good, however I have no idea, how to do this when using an XmlCodeExporter and XmlSchemaImporter to create classes from an xsd. This is why I mentioned the xsd-tool, which I want to extend. However I can´t see any option within those two classes that allow me to refine the class-creation.Passel
wow it works like a charm . thank youRiordan
@HimBromBeere you may want to look into System.Xml.Serialization.Advanced.SchemaImporterExtension, at msdn.microsoft.com/en-us/library/…Cy
@Cy I can´t see how this may simplify this approach as I will still manipulate the DOM by adding the attributes. Only difference I can see is that with the extension I´m doing this before the Codenamespace is created, while with my appraoch I´d manipulate the code after it has already been created.Passel
@HimBromBeere I hear you. I was just pointing out it's seemingly what Microsoft themselves are using to organize their extension of what this old and rather inflexible CodeDom is able to model. In vast majority, CodeDom types don't allow easy extension. Most methods are not virtual, although the enclosing class types aren't sealed. ShemaImportExtension is used internally in a few places where the xsd / wsdl code generators need to factor their work around the CodeDom model they prior obtained. Your approach boils down to doing a similar augmentation, just organized differently. Was mostly FYI.Cy
@Cy Okay, I see. Thanks for the link.Passel
C
1

There is no build in way in CodeDOM, the right way is to add XmlAttributes, but there is problem with them, because they don't gather along with inheritance. So is better to emit the properties in right order, then the xml serializer will serialize it in the right order(not guaranteed but I have tested it). Hope it works! :)

Cortie answered 20/6, 2018 at 6:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.