ahagman
ahagman

Reputation: 73

Custom XML Serialization adding extra attributes

I have the following class.

public class ConfigurationItem
{
    public String Type { get; set; }
    public String Value { get; set; }
}

This code performs the serialization.

static void Main(string[] args)
{
    List<ConfigurationItem> cis = new List<ConfigurationItem>();
    cis.Add(new ConfigurationItem() { Type = "Car", Value = "Car Value" });
    cis.Add(new ConfigurationItem() { Type = "Bike", Value = "Bike Value" });

    System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(cis.GetType());
    x.Serialize(Console.Out, cis);
}

The actual output is below.

<?xml version="1.0" encoding="IBM437"?>
<ArrayOfConfigurationItem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <ConfigurationItem>
        <Type>Car</Type>
        <Value>Car Value</Value>
    </ConfigurationItem>
    <ConfigurationItem>
        <Type>Bike</Type>
        <Value>Bike Value</Value>        
    </ConfigurationItem>
</ArrayOfConfigurationItem>

I would like to produce the following XML.

<?xml version="1.0" encoding="IBM437"?>
<ArrayOfConfigurationItem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <ConfigurationItem>
        <Type>Car</Type>
        <Value Label="Car Label">Car Value</Value>
    </ConfigurationItem>
    <ConfigurationItem>
        <Type>Bike</Type>
        <Value Label="Bike Label">Bike Value</Value>        
    </ConfigurationItem>
</ArrayOfConfigurationItem>

I have following type to Label mapping table available

Dictionary<String, String> ValueLabels = new Dictionary<string, string>()
{
    {"Car","Car Label"},
    {"Bike","Bike Label"}
};

I can't touch the ConfigurationItem class. Is it possible to use System.Xml.Serialization.XmlAttributeOverrides or something similar?

Edit 1 I have an ugly solution that I'm using now. I'm using normal serialization and adding data to the XmlDocument manually.

static void Main(string[] args)
{
    List<ConfigurationItem> cis = new List<ConfigurationItem>();
    cis.Add(new ConfigurationItem(){Type = "Car", Value = "Car Value"});
    cis.Add(new ConfigurationItem(){Type = "Bike", Value = "Bike Value"});

    Dictionary<String, String> valueLabels = new Dictionary<string, string>()
    {
        {"Car","Car Label"},
        {"Bike","Bike Label"}
    };

    var detailDocument = new System.Xml.XmlDocument();
    var nav = detailDocument.CreateNavigator();

    if (nav != null)
    {
        using (System.Xml.XmlWriter w = nav.AppendChild())
        {
            var ser = new System.Xml.Serialization.XmlSerializer(cis.GetType());
            ser.Serialize(w, cis);
        }
    }
    var nodeList = detailDocument.DocumentElement.SelectNodes("//ConfigurationItem");
    foreach (System.Xml.XmlNode node in nodeList)
    {
        String type = ((System.Xml.XmlElement)node.SelectNodes("Type")[0]).InnerText;
        ((System.Xml.XmlElement)node.SelectNodes("Value")[0]).SetAttribute("Label", valueLabels[type]);
    }

    System.Xml.XmlTextWriter writer = new System.Xml.XmlTextWriter(Console.Out);
    writer.Formatting = System.Xml.Formatting.Indented;
    detailDocument.WriteTo(writer);            

    Console.ReadLine();
}

Still looking for better solution...

Upvotes: 4

Views: 4372

Answers (2)

gerdogdu
gerdogdu

Reputation: 44

It's been a long time since the issue. But I hope it will be a solution for those who have similar problem.

For the "Value" xml node;

[XmlRoot("Value")]
public class ValueNode
{
    public ValueNode()
    {
    }

    public ValueNode(string label, string value)
    {
        Label = label;
        Value = value;
    }

    [XmlAttribute]
    public string Label { get; set; }

    [XmlText]
    public string Value { get; set; }
}

For the "ConfigurationItem" xml node;

[XmlRoot("ConfigurationItem")]
public class ConfigurationItemWrapper
{
    private static readonly Dictionary<string, string> ValueLabels = new Dictionary<string, string>()
    {
        {"Car","Car Label"},
        {"Bike","Bike Label"}
    };

    public ConfigurationItemWrapper()
    {
    }

    public ConfigurationItemWrapper(ConfigurationItem item)
    {
        if (item is null)
        {
            throw new ArgumentNullException(nameof(item));
        }

        Type = item.Type;
        Value = new ValueNode(
                ValueLabels.ContainsKey(item.Type)
            ? ValueLabels[item.Type]
            : null,
                item.Value);
    }

    public string Type { get; set; }
    public ValueNode Value { get; set; }

    public static explicit operator ConfigurationItem(ConfigurationItemWrapper item)
    {
        return (item is null)
            ? null
            : new ConfigurationItem() { Type = item.Type, Value = item.Value?.Value };
    }

    public static explicit operator ConfigurationItemWrapper(ConfigurationItem item)
    {
        return (item is null)
            ? null
            : new ConfigurationItemWrapper(item);
    }
}

To serialize the "List<ConfigurationItem>" list in the expected format;

[XmlRoot("ArrayOfConfigurationItem")]
public class ConfigurationItemListWrapper
{
    public ConfigurationItemListWrapper()
    {
        Items = new List<ConfigurationItemWrapper>();
    }

    public ConfigurationItemListWrapper(IEnumerable<ConfigurationItem> items)
    {
        Items = new List<ConfigurationItemWrapper>(items.Select(x => (ConfigurationItemWrapper)x));
    }

    [XmlElement("ConfigurationItem")]
    public List<ConfigurationItemWrapper> Items { get; set; }
}

Use of;

static void Main(string[] args)
{
    List<ConfigurationItem> cis = new List<ConfigurationItem>();
    cis.Add(new ConfigurationItem() { Type = "Car", Value = "Car Value" });
    cis.Add(new ConfigurationItem() { Type = "Bike", Value = "Bike Value" });

    System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(ConfigurationItemListWrapper));
    x.Serialize(Console.Out, new ConfigurationItemListWrapper(cis));

    Console.ReadLine();
}

The expected console output will be as follows.

<?xml version="1.0" encoding="IBM437"?>
<ArrayOfConfigurationItem xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ConfigurationItem>
    <Type>Car</Type>
    <Value Label="Car Label">Car Value</Value>
  </ConfigurationItem>
  <ConfigurationItem>
    <Type>Bike</Type>
    <Value Label="Bike Label">Bike Value</Value>
  </ConfigurationItem>
</ArrayOfConfigurationItem>

I hope it will be a solution for those who have similar problem.

Upvotes: 0

John Saunders
John Saunders

Reputation: 161773

If you want to output an attribute, you'll need a property which will be serialized as the attribute. Try the following:

public class ConfigurationItem
{
    public String Type { get; set; }
    public String Value { get; set; }
    [XmlAttribute("Label")]
    public string Label
    {
        get {return Value;}
        set {Value = value;}
    }
}

Upvotes: 3

Related Questions