I have a class named Node and inside that i have Property of Type Document Class.
When I serialize it into XML, I get the output as
<Node>
<DocumentType>
<File></File>
</DoumentType>
<Node>
But I want the output as
<Node>
<File></File>
<Node>
Object Code
public class Document
{
[XmlElement(ElementName = "file")]
public string File { get; set; }
}
public class Node
{
public Document NodeDocument
{
get;
set;
}
}
How can I do that using C# xml Serialization?
Following Kami's suggestion, here is the code for your reference. All credit goes to Kami.
public class Node : IXmlSerializable {
public Node() {
NodeDocument = new Document();
}
public Document NodeDocument { get; set; }
public System.Xml.Schema.XmlSchema GetSchema() {
return null;
}
public void ReadXml(XmlReader reader) {
reader.ReadStartElement();
NodeDocument.File = reader.ReadString();
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer) {
writer.WriteStartElement("file");
writer.WriteString(NodeDocument.File);
writer.WriteEndElement();
}
}
public class Document {
public String File { get; set; }
}
class Program {
static void Main(string[] args) {
var node = new Node();
node.NodeDocument.File = "bbb.txt";
Serialize<Node>("a.xml", node);
node = Deserialize<Node>("a.xml");
Console.WriteLine(node.NodeDocument.File);
Console.Read();
}
static T Deserialize<T>(String xmlFilePath) where T : class {
using (var textReader = File.OpenText(xmlFilePath)) {
using (var xmlTextReader = new XmlTextReader(textReader)) {
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(xmlTextReader);
}
}
}
static void Serialize<T>(String xmlFilePath, T obj) where T : class {
using (var textWriter = File.CreateText(xmlFilePath)) {
using (var xmlTextWriter = new XmlTextWriter(textWriter)) {
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(xmlTextWriter, obj);
}
}
}
}
Have you considered implementing IXmlSerializable interface - http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx.
You should then be able to write custom serialization/deserialization to facilitate the above.
Related
I have the following class I want to serialise:
public class UpdateDoorCommand : IXmlSerializable
{
// string such as D1
public string DoorId { get; }
public string Name { get; }
public string Notes { get; }
public UpdateDoorCommand(string doorId, string name, string notes)
{
DoorId = doorId;
Name = name;
Notes = notes;
}
public UpdateDoorCommand()
{
}
public XmlSchema GetSchema()
{
throw new NotImplementedException();
}
public void ReadXml(XmlReader reader)
{
throw new NotImplementedException();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteStartElement("Door");
writer.WriteAttributeString("Address", "D1");
writer.WriteElementString("Name", Name);
writer.WriteElementString("Notes", Notes);
writer.WriteEndElement();
}
}
I want the output to look like this:
<Door Address="D1">
<Name>Name1</Name>
<Notes>Notes1</Notes>
</Door>
I use the following code to serialise the object:
[TestMethod]
public async Task XmlSerialisationTest()
{
var model = new UpdateDoorCommand("D1", "Name1", "Notes1");
var mediaTypeFormatters = new MediaTypeFormatterCollection();
mediaTypeFormatters.XmlFormatter.UseXmlSerializer = true;
mediaTypeFormatters.XmlFormatter.WriterSettings.OmitXmlDeclaration = true;
var content = new ObjectContent<UpdateDoorCommand>(model, mediaTypeFormatters.XmlFormatter);
// this does not look like the type
var str = await content.ReadAsStringAsync();
}
}
However the the output of the serialisation does not give the desired results.
The xml is wrapped in an element with the classname of the object.
How can I get desired xml output using the ObjectContent class?
Note that the code needs a reference to System.Net.Http.Formatting in order to run.
I'm not sure if the two ways are compatible but try this:
[XmlRoot(ElementName = "Door", DataType = "string")]
public class UpdateDoorCommand : IXmlSerializable
{
// *snip*
public void WriteXml(XmlWriter writer)
{
//writer.WriteStartElement("Door");
writer.WriteAttributeString("Address", "D1");
writer.WriteElementString("Name", Name);
writer.WriteElementString("Notes", Notes);
//writer.WriteEndElement();
}
}
Simple with Xml Linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
UpdateDoorCommand updateDoorCommand = new UpdateDoorCommand("D1","Name1","Note1");
updateDoorCommand.WriteXml();
}
}
public class UpdateDoorCommand
{
// string such as D1
public string DoorId { get; set; }
public string Name { get; set; }
public string Notes { get; set; }
public UpdateDoorCommand(string doorId, string name, string notes)
{
DoorId = doorId;
Name = name;
Notes = notes;
}
public UpdateDoorCommand()
{
}
public void ReadXml(XmlReader reader)
{
throw new NotImplementedException();
}
public void WriteXml()
{
XElement doorCommand = new XElement("Door", new object[] {
new XAttribute("Address", DoorId),
new XElement("Name", Name),
new XElement("Notes1", Notes)
});
}
}
}
Just tried to serialize and deserialize several whitespace with additional xml attribute to preserve them as described in msdn (see remarks). Here is simple C# example.
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace XmlTextTest
{
[Serializable]
public class WhitespaceTest
{
[XmlAttribute("space", Namespace = "http://www.w3.org/XML/1998/namespace")]
public string Space { get; set; } = "preserve";
[XmlText]
public string Value { get; set; }
}
class Program
{
static void Main(string[] args)
{
var testValue = " ";
var test = new WhitespaceTest { Value = testValue };
var serializer = new XmlSerializer(typeof(WhitespaceTest));
using (var stream = new MemoryStream())
{
serializer.Serialize(stream, test);
stream.Seek(0, SeekOrigin.Begin);
Console.WriteLine(new StreamReader(stream).ReadToEnd());
stream.Seek(0, SeekOrigin.Begin);
var deserialized = serializer.Deserialize(stream) as WhitespaceTest;
if (deserialized.Value != testValue)
throw new Exception();
}
}
}
}
Result xml
<WhitespaceTest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xml:space="preserve"> </WhitespaceTest>
May be it's my mistake but I expect that whitespaces will be deserialized as is (according to standard), not "null". How to mark original Type property to prevent replacing whitespaces with "null" into XmlText region.
Modified example to use IgnoreWhiteSpace property:
var str = "<WhitespaceTest xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xml:space='preserve'> </WhitespaceTest>";
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreWhitespace = false;
var reader = XmlReader.Create(new StringReader(str), settings);
var deserialized2 = serializer.Deserialize(reader) as WhitespaceTest;
if (deserialized2.Value != testValue)
throw new Exception();
Still no effect.
Updated:
Created implementation of IXmlSerializable and added "xml:space=preserve" attribute manually. See example:
public class Sample : IXmlSerializable
{
#region Properties
[XmlText]
public string Text { get; set; }
#endregion
#region IXmlSerializable members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
ReadXml(this, reader);
}
public void WriteXml(System.Xml.XmlWriter writer)
{
WriteXml(this, writer);
}
#endregion IXmlSerializable members
internal static void ReadXml(ILocalizedMappable serializable, System.Xml.XmlReader reader)
{
var node = new XmlDocument().ReadNode(reader);
if (node != null)
{
serializable.Text = node.InnerText;
}
}
internal static void WriteXml(ILocalizedMappable serializable, System.Xml.XmlWriter writer)
{
writer.WriteAttributeString("xml", "space", string.Empty, "preserve");
writer.WriteString(serializable.Text);
}
}
I have a Model populated and I wish to serlise to an xml document.
Due to naming conventions I have to over ride the class names for my XML document,
This is my Model(s):
[Serializable]
[XmlRoot("preferences")]
public class PreferencesModel
{
[XmlIgnore]
public string MessageToUser { get; set; }
[XmlElement(ElementName = "sectiondivider")]
public List<SectionDivider> SectionDivider { get; set; }
}
[Serializable]
[XmlRoot(ElementName = "sectiondivider")]
public class SectionDivider
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlElement("preference")]
public List<PreferenceModel> PreferenceModel { get; set; }
}
[Serializable]
[XmlRoot("preference")]
public class PreferenceModel
{
[XmlAttribute("type")]
public string Type { get; set; }
public string Name { get; set; }
[XmlAttribute("value")]
public string Value { get; set; }
[XmlElement("options")]
public List<Option> Options { get; set; }
}
this is how I serialize:
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(ObjectToXmlString(obj, includeNameSpace, includeStartDocument, rootAttribute));
return xDoc;
public static string ObjectToXmlString(Object obj, bool includeNameSpace, bool includeStartDocument, XmlRootAttribute rootAttribute)
{
SpecialXmlWriter stWriter = null;
XmlSerializer xmlSerializer = default(XmlSerializer);
string buffer = null;
try
{
if (rootAttribute == null)
{
xmlSerializer = new XmlSerializer(obj.GetType());
}
else
{
xmlSerializer = new XmlSerializer(obj.GetType(), rootAttribute);
}
MemoryStream memStream = new MemoryStream();
StringWriter writer = new StringWriter();
stWriter = new SpecialXmlWriter(memStream, new UTF8Encoding(false), includeStartDocument);
if (!includeNameSpace)
{
System.Xml.Serialization.XmlSerializerNamespaces xs = new XmlSerializerNamespaces();
//To remove namespace and any other inline
//information tag
xs.Add("", "");
xmlSerializer.Serialize(stWriter, obj, xs);
}
else
{
xmlSerializer.Serialize(stWriter, obj);
}
buffer = Encoding.UTF8.GetString(memStream.ToArray());
}
catch (Exception e)
{
string msg = e.Message;
throw;
}
finally
{
if (stWriter != null)
stWriter.Close();
}
return buffer;
}
I call it like this:
XmlDocument preferencesxml = Codec.ObjectToXml(m.SectionDivider,false,
false, new XmlRootAttribute("preferences"));
My m value is:
and my resulting XML is this:
XmlRootAttribute, as the name suggests, only applies to the root element of the XML being serialised.
You need to use XmlTypeAttribute in this context:
[XmlType("sectiondivider")]`
public class SectionDivider
{
//...
}
As an aside, the [Serializable] attribute is not relevant to XmlSerializer - it can be removed unless you need it for some other purpose.
If I have the following:
public class A
{
public B b {get;set;}
}
public class B
{
public string Name {get;set;}
public string Address {get;set;
}
what I want is the xml as:
<A Name="some data" Address="address..." />
So I am trying to flatten my referenced object as attributes.
Is this possible with an XmlSerializer?
yeah, you can do this by using the IXmlSerializable interface:
[Serializable]
public class MyClass : IXmlSerializable
{
public MySubClass SubClass { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
throw new NotImplementedException();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteStartAttribute("Name");
writer.WriteString(SubClass.Name);
writer.WriteEndAttribute();
writer.WriteStartAttribute("Phone");
writer.WriteString(SubClass.Phone);
writer.WriteEndAttribute();
}
}
[Serializable]
public class MySubClass
{
public string Name { get; set; }
public string Phone { get; set; }
}
and then call it like this
var serializer = new XmlSerializer(typeof(MyClass));
using (var writer = new StringWriter())
{
var myClass = new MyClass() {SubClass = new MySubClass() {Name = "Test", Phone = "1234"}};
serializer.Serialize(writer, myClass);
string xml = writer.ToString();
}
this is the xml result:
<?xml version="1.0" encoding="utf-16"?>
<MyClass Name="Test" Phone="1234" />
see msdn too: http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx
or you could just specify the attributes that #Morpheus named ;)
I'm trying to serialize an object to meet another systems requirements.
It need to look like this:
<custom-attribute name="Colour" dt:dt="string">blue</custom-attribute>
but instead is looking like this:
<custom-attribute>blue</custom-attribute>
So far I have this:
[XmlElement("custom-attribute")]
public String Colour{ get; set; }
I'm not really sure what metadata I need to achieve this.
You could implement IXmlSerializable:
public class Root
{
[XmlElement("custom-attribute")]
public Colour Colour { get; set; }
}
public class Colour : IXmlSerializable
{
[XmlText]
public string Value { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
throw new NotImplementedException();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("dt:dt", "", "string");
writer.WriteAttributeString("name", "Colour");
writer.WriteString(Value);
}
}
class Program
{
static void Main()
{
var serializer = new XmlSerializer(typeof(Root));
var root = new Root
{
Colour = new Colour
{
Value = "blue"
}
};
serializer.Serialize(Console.Out, root);
}
}