C # - Serializing Private Property as Attribute (Data Contract)
After learning I can't serialize private properties in class, I started using DataContract and DataMember annotations .
I have my own 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 DataMember and XmlAttribute annotations together gave me this result:
<channel name="NAME">
<TxTime>2017-03-26T13:15:56.8042989+03:00</TxTime>
</channel>
As a result, I want:
<channel name="NAME" txTime="2017-03-26T13:15:56.8042989+03:00" />
How do I serialize using XmlSerializer -
using (FileStream fs = new FileStream("channels.txt", FileMode.Create))
{
XmlSerializer serializer = new XmlSerializer(typeof(Channel));
serializer.Serialize(fs, objectName);
}
Create a "wrapper" for your class, which will be responsible for the correct deserialization format (some kind of facade template)
[DataContract]
public class ChannelData
{
[DataMember, XmlAttribute("name")]
public string Name { get; set; }
[DataMember, XmlAttribute("txTime")]
public string txTimeForSerialization { get; set; }
}
You can put the serialization instantiation logic in a class Channel
, then responsibility for the serialization logic will remain in the channel class - which preserve 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 a wrapper, you don't have to think about different "workarounds" for different formats, your "business-level" classes will remain "clear" and the wrappers will handle the formatting.
If you want to stick with your current class Channel
, you need to implement the interface IXmlSerializer
and its WriteXml method. This is the only way to serialize private properties through 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);
}
}
Implementing IXmlSerializable
, you no longer need to use attributes, and your code for writing to channel.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's the result:
<?xml version="1.0"?>
<Channel name="NAME" txtTime="2017-03-26T12:57:25.6780078Z" />