3

After learning I can't serialize private properties in a class, I started using DataContract and DataMember annotations.

I have a private string property in my class that I would like to serialize as an attribute and not as an element.

[DataContract]
public class Channel
{
    private string m_Name = string.Empty;
    private DateTime? m_TxTime = null;

    [DataMember, XmlAttribute("name")]
    public string Name
    {
        get
        {
            return m_Name;
        }
        set
        {
            m_Name = value;
        }
    }

    public DateTime? TxTime
    {
        get
        {
            return m_TxTime;
        }
        set
        {
            m_TxTime = value;
        }
    }

    [DataMember, XmlAttribute("txTime")]
    private string txTimeForSerialization
    {
        get
        {
            if (TxTime.HasValue)
            {
                return TxTime.Value.ToString("o");
            }
            else
            {
                return null;
            }
        }
    }
}

Trying to use the DataMember and XmlAttribute annotations together gave me this output:

<channel name="NAME">
   <TxTime>2017-03-26T13:15:56.8042989+03:00</TxTime>
</channel>

The result I want is this -

<channel name="NAME" txTime="2017-03-26T13:15:56.8042989+03:00" />

The way I serialized is by using the XmlSerializer -

using (FileStream fs = new FileStream("channels.txt", FileMode.Create))
{
    XmlSerializer serializer = new XmlSerializer(typeof(Channel));
    serializer.Serialize(fs, objectName);
}
Shay Lugassy
  • 119
  • 1
  • 2
  • 6
  • `XmlSerializer` only serializes public fields and properties. Also, if you want to use it, you don't need `DataContract` and `DataMember` attributes since they are used by `DataContractSerializer`. – Darjan Bogdan Mar 26 '17 at 10:54
  • `DataContractSerializer` does not use XML attributes. See [How can you control .NET DataContract serialization so it uses XML attributes instead of elements?](http://stackoverflow.com/q/591907/3744182). Implementing `IXmlSerializable` as shown below should work for both serializers. – dbc Mar 28 '17 at 17:13

2 Answers2

0

Create "wrapper" for your class which will be responsible for correct deserialization format (some kind of Facade pattern)

[DataContract]
public class ChannelData
{
    [DataMember, XmlAttribute("name")]
    public string Name { get; set; }

    [DataMember, XmlAttribute("txTime")]
    public string txTimeForSerialization { get; set; }
}

You can put logic of creating serialization instance in Channel class, then responsibility will serialization logic will remain in Channel class - which keep encapsulation.

public class Channel
{
    public string Name { get; set; }
    public DateTime? TxTime { get; set; }

    public Channel()
    {
        Name = string.Empty;
        TxtTime = null;
    }

    public ChannelData ToSerialization()
    {
        var data = new ChannelData();
        data.Name = Name;
        data.txTimeForSerialization = TxTime.HasValue ? TxTime.Value.ToString("o") : null;
    }
}

With wrapper you don't need think about different "workarounds" for different formats, your "business layer" classes will stay "clear" and wrappers will handle formatting.

Fabio
  • 31,528
  • 4
  • 33
  • 72
0

If you want to stick with your current Channel class, you need to implement IXmlSerializer interface and its WriteXml method. That's the only way you can serialize private properties via XmlSerializer.

public class Channel : IXmlSerializable
{
    private string m_Name = string.Empty;
    private DateTime? m_TxTime = null;

    public string Name
    {
        get
        {
            return m_Name;
        }
        set
        {
            m_Name = value;
        }
    }

    public DateTime? TxTime
    {
        get
        {
            return m_TxTime;
        }
        set
        {
            m_TxTime = value;
        }
    }

    private string txtTimeForSerialization
    {
        get
        {
            if (TxTime.HasValue)
            {
                return TxTime.Value.ToString("o");
            }
            else
            {
                return null;
            }
        }
    }

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        //implement reader if needed...
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteAttributeString("name", this.Name);
        writer.WriteAttributeString("txtTime", this.txtTimeForSerialization);
    }
}

By implementing IXmlSerializable you don't need to use attributes anymore and your code for writing into channels.txt will remain the same:

using (FileStream fs = new FileStream("channels.txt", FileMode.Create))
{
    XmlSerializer serializer = new XmlSerializer(typeof(Channel));
    serializer.Serialize(fs, objectName);
}

Here is the output:

<?xml version="1.0"?>
<Channel name="NAME" txtTime="2017-03-26T12:57:25.6780078Z" />
Darjan Bogdan
  • 3,780
  • 1
  • 22
  • 31