82

I have the following class:

[Serializable]
public class SomeModel
{
    [XmlElement("SomeStringElementName")]
    public string SomeString { get; set; }

    [XmlElement("SomeInfoElementName")]
    public int SomeInfo { get; set; }
}

Which (when populated with some test data) and Serialized using XmlSerializer.Serialize() results in the following XML:

<SomeModel>
  <SomeStringElementName>testData</SomeStringElementName>
  <SomeInfoElementName>5</SomeInfoElementName>
</SomeModel>

What I need to have is:

<SomeModel>
  <SomeStringElementName Value="testData" />
  <SomeInfoElementName Value="5" />
</SomeModel>

Is there a way to specify this as attributes without writing my own custom serialization code?

IUnknown
  • 2,596
  • 4
  • 35
  • 52

2 Answers2

115

You will need wrapper classes:

public class SomeIntInfo
{
    [XmlAttribute]
    public int Value { get; set; }
}

public class SomeStringInfo
{
    [XmlAttribute]
    public string Value { get; set; }
}

public class SomeModel
{
    [XmlElement("SomeStringElementName")]
    public SomeStringInfo SomeString { get; set; }

    [XmlElement("SomeInfoElementName")]
    public SomeIntInfo SomeInfo { get; set; }
}

or a more generic approach if you prefer:

public class SomeInfo<T>
{
    [XmlAttribute]
    public T Value { get; set; }
}

public class SomeModel
{
    [XmlElement("SomeStringElementName")]
    public SomeInfo<string> SomeString { get; set; }

    [XmlElement("SomeInfoElementName")]
    public SomeInfo<int> SomeInfo { get; set; }
}

And then:

class Program
{
    static void Main()
    {
        var model = new SomeModel
        {
            SomeString = new SomeInfo<string> { Value = "testData" },
            SomeInfo = new SomeInfo<int> { Value = 5 }
        };
        var serializer = new XmlSerializer(model.GetType());
        serializer.Serialize(Console.Out, model);
    }
}

will produce:

<?xml version="1.0" encoding="ibm850"?>
<SomeModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <SomeStringElementName Value="testData" />
  <SomeInfoElementName Value="5" />
</SomeModel>
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 5
    Yes, that gives me the XML schema I need but it adds an unnecessary level of indirection and complexity to the model. Now rather than just Object.SomeProperty, I have Object.SomeProperty.Value. I realize that I might not be able to have it both ways using built in serialization - but that is the essence of the question. – IUnknown Jul 04 '12 at 14:21
  • 2
    I know this is somewhat old, but I would add implicit cast operations from `T` to `SomeInfo` and vice versa. – Shago Apr 25 '17 at 13:35
  • It's distressing that we have to hack the XmlSerializer this way for such a basic treatment, but it did give me a usable option. – Suncat2000 Mar 09 '20 at 16:41
9

Kind of, use the XmlAttribute instead of XmlElement, but it won't look like what you want. It will look like the following:

<SomeModel SomeStringElementName="testData"> 
</SomeModel> 

The only way I can think of to achieve what you want (natively) would be to have properties pointing to objects named SomeStringElementName and SomeInfoElementName where the class contained a single getter named "value". You could take this one step further and use DataContractSerializer so that the wrapper classes can be private. XmlSerializer won't read private properties.

// TODO: make the class generic so that an int or string can be used.
[Serializable]  
public class SerializationClass
{
    public SerializationClass(string value)
    {
        this.Value = value;
    }

    [XmlAttribute("value")]
    public string Value { get; }
}


[Serializable]                     
public class SomeModel                     
{                     
    [XmlIgnore]                     
    public string SomeString { get; set; }                     

    [XmlIgnore]                      
    public int SomeInfo { get; set; }  

    [XmlElement]
    public SerializationClass SomeStringElementName
    {
        get { return new SerializationClass(this.SomeString); }
    }               
}
Chris Gessler
  • 22,727
  • 7
  • 57
  • 83
  • Hey Chris, Yes, I could use XmlAttribute but as you said it does not give the result I need. I was hoping to be able to get what I needed without writing my own serialization code. – IUnknown Jul 04 '12 at 14:16
  • 1
    @IUnknown - I threw together one possibly way of achieving what you want. It's not pretty, creating classes to get a node, so if someone else knows of a better way, I would be interested as well. – Chris Gessler Jul 04 '12 at 14:36