Clean XML serializing a hierarchical, recursive da

2020-02-29 10:33发布

问题:

I have this class:

[XmlRoot("menuItem")]
public class MenuItem
{
    [XmlAttribute("text")]
    public string Text { get; set; }

    [XmlAttribute("isLink")]
    public bool IsLink { get; set; }

    [XmlAttribute("url")]
    public string Url { get; set; }

    [XmlArray("items", IsNullable = true)]
    public List<MenuItem> Items { get; set; }
}

Which defines a menu hierarchy. Now, on serializing this class, the output XML for a 3-level menu is:

<menuItem xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
text="Tools" isLink="false">
  <items>
    <MenuItem text="Market" isLink="false">
      <items>
        <MenuItem text="Market Analyzer" isLink="true" url="/tools/market/analyzer">
          <items xsi:nil="true" />
        </MenuItem>
      </items>
    </MenuItem>
    <MenuItem text="Banking" isLink="false">
      <items>
        <MenuItem text="Purchase" isLink="true" url="/buy?type=good">
          <items xsi:nil="true" />
        </MenuItem>
      </items>
    </MenuItem>
    <MenuItem text="General" isLink="false">
      <items>
        <MenuItem text="Forecasts" isLink="true" url="/wheather-forcasts?city=la">
          <items xsi:nil="true" />
        </MenuItem>
      </items>
    </MenuItem>
  </items>
</menuItem>

So, MenuItem is both the root and the child-element. As the root, it's serialized as menuItem with proper casing. However, as child elements, it's capitalization is not correct. How can I make the serializer create menuItem and not MenuItem in the output for child items. Case sensitivity matters to me here.

I tried to put [XmlElement] attribute on the class itself, but got the following error:

Attribute 'XmlArrayItem' is not valid on this declaration type. It is only valid on 'property, indexer, field, param, return' declarations.

Also, I don't want those default namespaces there, and I don't want the child items to be created as empty elements. The ultimate XML file should be as clean as this XML example:

<menuItem text='Tools' isLink='false'>
  <items>
    <menuItem text='Market' isLink='false'>
      <items>
        <menuItem text='Market Analyzer' isLink='true' url='/tools/market/analyzer' />
      </items>
    </menuItem>
    <menuItem text='Banking' isLink='false'>
      <items>
        <menuItem text='Purchase' isLink='true' url='/buy?type=good' />
      </items>
    </menuItem>
    <menuItem text='General' isLink='false'>
      <items>
        <menuItem text='Forecasts' isLink='true' url='/wheather-forcasts?city=la' />
      </items>
    </menuItem>
  </items>
</menuItem>

What attributes should I use?

回答1:

Add XmlArrayItemAttribute and take away the IsNullable:

[XmlArray("items"), XmlArrayItem("menuItem")]
public List<MenuItem> Items { get; set; }

To get rid of the extra namespaces, you need to use XmlSerializerNamespaces:

var ns = new XmlSerializerNamespaces();
ns.Add("","");
var ser = new XmlSerializer(typeof (MenuItem));
ser.Serialize(Console.Out, obj, ns);