Serialize Property as Xml Attribute in Element

Serialize Property as Xml Attribute in Element

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>

Serialize Property as Xml Attribute in new Element

It seems you have to convert your xml to custom format so you have to implement IXmlSerializable interface in your Person class object like.

public class Person : IXmlSerializable
{
public int Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }

public XmlSchema GetSchema() { throw new NotImplementedException(); }

public void ReadXml(XmlReader reader)
{
while (reader.Read())
{
if (reader.Name == "attr" && reader.GetAttribute("n") == "id")
Id = Convert.ToInt32(reader.ReadInnerXml());

if (reader.Name == "attr" && reader.GetAttribute("n") == "name")
Name = reader.ReadInnerXml();

if (reader.Name == "attr" && reader.GetAttribute("n") == "street")
Address = new Address { Street = reader.ReadInnerXml() };
}
}

public void WriteXml(XmlWriter writer)
{
//Start "struct"
writer.WriteStartElement("struct");
writer.WriteAttributeString("n", "People");

writer.WriteStartElement("attr");
writer.WriteAttributeString("n", "id");
writer.WriteString(Id.ToString());
writer.WriteEndElement();

writer.WriteStartElement("attr");
writer.WriteAttributeString("n", "name");
writer.WriteString(Name.ToString());
writer.WriteEndElement();

//Start inner "struct"
writer.WriteStartElement("struct");
writer.WriteAttributeString("n", "Address");

writer.WriteStartElement("attr");
writer.WriteAttributeString("n", "street");
writer.WriteString(Address.Street.ToString());
writer.WriteEndElement();

//End inner "struct"
writer.WriteEndElement();

//End "struct"
writer.WriteEndElement();
}
}

And you can serialize and deserialize your xml like.

Person person = new Person
{
Id = 2,
Name = "FIO FIO IFO",
Address = new Address { Street = "Golden Gate" }
};

//--------------------Serialization----------------------------

XmlSerializer serializer = new XmlSerializer(person.GetType());
using (StreamWriter writer = new StreamWriter(@"Path to xml file"))
{
serializer.Serialize(writer, person);
}

//--------------------Deserialization----------------------------

using (StringReader stringReader = new StringReader(File.ReadAllText(@"Path to same xml file that generated by above serializer")))
{
person = (Person)serializer.Deserialize(stringReader);

Console.WriteLine("Id: " + person.Id);
Console.WriteLine("Name: " + person.Name);
Console.WriteLine("Street: " + person.Address.Street);

Console.ReadLine();
}

Output for Serialization:

Sample Image

Output for Deserialization:

Sample Image


Alternative

You can also create a custom methods to read and write xml.

1) Write

public static void WriteXml(Person person, string path)
{
using (XmlWriter writer = XmlWriter.Create(path))
{
//Start "struct"
writer.WriteStartElement("struct");
writer.WriteAttributeString("n", "People");

writer.WriteStartElement("attr");
writer.WriteAttributeString("n", "id");
writer.WriteString(person.Id.ToString());
writer.WriteEndElement();

writer.WriteStartElement("attr");
writer.WriteAttributeString("n", "name");
writer.WriteString(person.Name.ToString());
writer.WriteEndElement();

//Start inner "struct"
writer.WriteStartElement("struct");
writer.WriteAttributeString("n", "Address");

writer.WriteStartElement("attr");
writer.WriteAttributeString("n", "street");
writer.WriteString(person.Address.Street.ToString());
writer.WriteEndElement();

//End inner "struct"
writer.WriteEndElement();

//End "struct"
writer.WriteEndElement();
}
}

Usage:

Person person = new Person
{
Id = 2,
Name = "FIO FIO IFO",
Address = new Address { Street = "Golden Gate" }
};

WriteXml(person, @"Path to xml file");

2) Read

public static Person ReadXml(string path)
{
Person person = new Person();

using (XmlReader reader = XmlReader.Create(path))
{
while (reader.Read())
{
if (reader.Name == "attr" && reader.GetAttribute("n") == "id")
person.Id = Convert.ToInt32(reader.ReadInnerXml());

if (reader.Name == "attr" && reader.GetAttribute("n") == "name")
person.Name = reader.ReadInnerXml();

if (reader.Name == "attr" && reader.GetAttribute("n") == "street")
person.Address = new Address { Street = reader.ReadInnerXml() };
}
}

return person;
}

Usage:

Person result = ReadXml(@"Path to same xml file that generated by above serializer");

How to serialize object's property to XML element's attribute?

What you need is a property for the string representation that gets serialized:

[Serializable]
public class ProgramInfo
{
[XmlAttribute]
public string Name { get; set; }

[XmlIgnore]
public Version Version { get; set; }

[XmlAttribute("Version")
public string VersionString
{
get { return this.Version.ToString(); }
set{ this.Version = Parse(value);}
}
}

XML serialisation for class properties with additional meta data

You can use XElement from System.Xml.Linq to achieve this. As your data is static you can assign them easily. Sample code below -

XElement data= new XElement("Vehicle",
new XElement("VehicleId", new XAttribute("AppliesTo", "C1"),"1244"),
new XElement("Make", new XAttribute("AppliesTo", "Common"), "HXV"),
new XElement("Model", new XAttribute("AppliesTo", "C2"), "34 - 34")
);
//OUTPUT
<Vehicle>
<VehicleId AppliesTo="C1">1244</VehicleId>
<Make AppliesTo="Common">HXV</Make>
<Model AppliesTo="C2">34 - 34</Model>
</Vehicle>

If you are not interested in System.Xml.Linq then you have another option of XmlSerializer class. For that you need yo define separate classes for each property of vehicle. Below is the sample code and you can extend the same for Make and Model -

[XmlRoot(ElementName = "VehicleId")]
public class VehicleId
{
[XmlAttribute(AttributeName = "AppliesTo")]
public string AppliesTo { get; set; }
[XmlText]
public string Text { get; set; }
}

[XmlRoot(ElementName = "Vehicle")]
public class Vehicle
{
[XmlElement(ElementName = "VehicleId")]
public VehicleId VehicleId { get; set; }
//Add other properties here
}

Then create test data and use XmlSerializer class to construct XML -

Vehicle vehicle = new Vehicle
{
VehicleId = new VehicleId
{
Text = "1244",
AppliesTo = "C1",
}
};

XmlSerializer testData = new XmlSerializer(typeof(Vehicle));
var xml = "";

using (var sww = new StringWriter())
{
using (XmlWriter writer = XmlWriter.Create(sww))
{
testData.Serialize(writer, vehicle);
xml = sww.ToString(); // XML
}
}

Is there a way to retain XML Attributes when serializing part of a class?

is there a way to specify tag names when serializing a simple List.

In general, depending on the exact scenario, it may be possible to get this to work. See MSDN's How to: Specify an Alternate Element Name for an XML Stream. The example there involves overriding serialization of a specific field, but it may be possible to use the same technique to override whole type names as well.

But it seems like an awful lot of trouble to me. Instead, why not just handle the serialization explicitly:

private static string SerializeByLinqAndToString<T>(
List<T> data, string rootName, string elementName)
{
XDocument document = new XDocument(
new XElement(rootName, data.Select(s => new XElement(elementName, s))));

return SaveXmlToString(document);
}

private static string SaveXmlToString(XDocument document)
{
StringBuilder sb = new StringBuilder();

using (XmlWriter xmlWriter = XmlWriter.Create(sb,
new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }))
{
document.Save(xmlWriter);
}

return sb.ToString();
}

Call like this:

SomeConfiguration config = ...; // initialize as desired

string result = SerializeByLinq(config.Bugs, "bug", "bugs");

The above works only with a list of strings, or a list of types where the element content can be simply the result of calling ToString() on the instance of the type.

Using the full-blown serialization features in .NET might be worthwhile when dealing with complex types, but if all you've got is a simple list of strings, the LINQ-to-XML feature is very handy.

If you do have more complex types, you can transform each list element into an XElement for the DOM and serialize that:

private static string SerializeByLinq<T>(
List<T> data, string rootName, string elementName = null)
{
XDocument document = new XDocument(
new XElement(rootName, data.Select(t =>
ElementFromText(SerializeObject(t), elementName)
)));

return SaveXmlToString(document);
}

private static XElement ElementFromText(string xml, string name = null)
{
StringReader reader = new StringReader(xml);
XElement result = XElement.Load(reader);

if (!string.IsNullOrEmpty(name))
{
result.Name = name;
}

return result;
}

private static string SerializeObject<T>(T o)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
StringWriter textWriter = new StringWriter();

using (XmlWriter writer = XmlWriter.Create(textWriter,
new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }))
{
xmlSerializer.Serialize(writer, o,
new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty}));
}

return textWriter.ToString();
}

In this second example, you can omit the element name for the child, and it will just use whatever the type's set up to use already (e.g. the type name, or whatever [XmlRoot] is set to).

How to add attributes for C# XML Serialization

Where do you have the type stored?

Normally you could have something like:

class Document {
[XmlAttribute("type")]
public string Type { get; set; }
[XmlText]
public string Name { get; set; }
}

public class _Filter
{
[XmlElement("Times")]
public _Times Times;
[XmlElement("Document")]
public Document Document;
}


Related Topics



Leave a reply



Submit