28

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.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
  • 6
    Doesn't seem so hacky to me (I used to write a lot of CodeDom, and it was all about hacks) – Simon Mourier Aug 10 '16 at 06:31
  • 1
    My only real suggestion is to rewrite slightly like this: var attr = member.CustomAttributes.Cast().FirstOrDefault(x => x.Name == typeof(XmlElementAttribute).FullName); – Christian Findlay Sep 28 '16 at 02:38
  • 1
    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. – Christian Findlay Sep 28 '16 at 02:40
  • 1
    @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. – MakePeaceGreatAgain Sep 28 '16 at 07:14
  • 1
    wow it works like a charm . thank you –  Sep 28 '16 at 07:37
  • 1
    @HimBromBeere you may want to look into System.Xml.Serialization.Advanced.SchemaImporterExtension, at https://msdn.microsoft.com/en-us/library/system.xml.serialization.advanced.schemaimporterextension(v=vs.110).aspx – YSharp Sep 30 '16 at 20:12
  • 1
    @YSharp 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. – MakePeaceGreatAgain Oct 04 '16 at 15:44
  • 1
    @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. – YSharp Oct 04 '16 at 20:31
  • 1
    @YSharp Okay, I see. Thanks for the link. – MakePeaceGreatAgain Oct 05 '16 at 07:18

1 Answers1

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! :)

Ivelin Ivanov
  • 148
  • 3
  • 14