Serializing with namespace declaration on childnode with XmlSerializer - c#

I'm trying to serialize using XMLSerializer and achieve the following result:
Desired xml:
<?xml version="1.0" encoding="utf-8"?>
<RootElem xmlns="http://www.example.com/rootns">
<Elem1>
<Foo>bar</Foo>
</Elem1>
<Elem2>
<x:ElemX xmlns:x="http://www.sample.net/otherns">
<x:Bar>foo</x:Bar>
</x:ElemX>
</Elem2>
</RootElem>
The content in <elem3> will be parsed alone by a system on the other end, so the xmlns:x specification will have to be on the <elemx> node, and not on the rootnode as below.
Current serialized xml, not desired:
<?xml version="1.0" encoding="utf-8"?>
<RootElem xmlns:x="http://www.sample.net/otherns" xmlns="http://www.example.com/rootns">
<Elem1>
<Foo>bar</Foo>
</Elem1>
<Elem2>
<x:ElemX>
<x:Bar>foo</x:Bar>
</x:ElemX>
</Elem2>
</RootElem>
Anyone know a standard way to instruct XmlSerializer to achieve this?
Here's the code used to generate this sample (simple c# winform application)
private void Form1_Load(object sender, EventArgs e)
{
RootElem root = new RootElem();
root.Elem1 = new NormalElement() { Foo = "bar"};
ChildClass c = new ChildClass() { Bar = "foo"};
root.Elem2 = new ElementWithChildClass() { ElemX = c };
MemoryStream ms = new MemoryStream();
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("x", "http://www.sample.net/otherns");
XmlSerializer xser = new XmlSerializer(typeof(RootElem));
XmlTextWriter tw = new XmlTextWriter(ms, Encoding.UTF8);
tw.Formatting = Formatting.Indented;
xser.Serialize(tw, root, ns);
ms.Seek(0, SeekOrigin.Begin);
StreamReader sr = new StreamReader(ms, Encoding.UTF8);
textBox1.Text = sr.ReadToEnd();
}
[XmlRoot(Namespace = "http://www.example.com/rootns")]
public class RootElem
{
public NormalElement Elem1 { get; set; }
public ElementWithChildClass Elem2 { get; set; }
}
public class NormalElement
{
public string Foo { get; set; }
}
public class ElementWithChildClass
{
[XmlElement(Namespace = "http://www.sample.net/otherns")]
public ChildClass ElemX { get; set; }
}
[XmlRoot("RefDoc", Namespace = "http://www.sample.net/otherns")]
public class ChildClass
{
public string Bar { get; set; }
}

Related

Serializing class adds prefixes automatically to XML elements

I have the following class that needs to be serialized:
[XmlRoot("Login", Namespace = "http://tempuri.org/Logon"), Serializable()]
public class Login
{
[XmlElement("programCode")]
public string ProgramCode { get; set; }
[XmlElement("contactType")]
public string ContactType { get; set; }
[XmlElement("email")]
public string Email { get; set; }
[XmlElement("password")]
public string Password { get; set; }
[XmlElement("projectName")]
public string ProjectName { get; set; }
}
When I serialize this class, I obtain the following XML:
<q1:Login xmlns:q1="http://tempuri.org/Logon"><q1:programCode>abc</q1:programCode><q1:contactType>P</q1:contactType><q1:email>ws#abc.com</q1:email><q1:password>abc</q1:password><q1:projectName>abc</q1:projectName></q1:Login>
I do not know where the prefix q1 is getting generated from. I want an XML like this:
<Login xmlns="http://tempuri.org/Logon">
<programCode>abc</programCode>
<contactType>P</contactType>
<email>ws#abc.com</email>
<password>abc</password>
<projectName>abc</projectName>
</Login>
Can anyone please help me out with this? Thank you.
Update:
Serialization code:
public string GetObjectInXML(object obj)
{
StringWriter sw = new StringWriter();
StringBuilder sb = new StringBuilder(_soapEnvelope);
XmlSerializer serializer = new XmlSerializer(obj.GetType());
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
XmlWriterSettings settings = new XmlWriterSettings
{
OmitXmlDeclaration = true
};
XmlWriter writer = XmlWriter.Create(sw, settings);
ns.Add(string.Empty, string.Empty);
serializer.Serialize(writer, obj, ns);
var str = sw.ToString();
return str;
}
For now this is a method which returns string just to check if my XML is built properly.
The XMLSerializer supports providing a default namespace e.g.
string defaultNamespace = "http://tempuri.org/Logon";
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add(string.Empty, defaultNamespace);
XmlSerializer xs = new XmlSerializer(typeof(T), defaultNamespace);
Can you remove the name space?
[XmlRoot("Login", Namespace = ""), Serializable()]
public class Login {
[XmlElement("programCode")]
public string ProgramCode { get; set; }
[XmlElement("contactType")]
public string ContactType { get; set; }
[XmlElement("email")]
public string Email { get; set; }
[XmlElement("password")]
public string Password { get; set; }
[XmlElement("projectName")]
public string ProjectName { get; set; }
}
public static string SerializeXml<T>(T value)
{
var settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
var namespaces = new XmlSerializerNamespaces();
namespaces.Add("q1", "http://tempuri.org/Logon");
var xmlserializer = new XmlSerializer(typeof(T));
var stringWriter = new StringWriter();
using (var writer = XmlWriter.Create(stringWriter, settings))
{
xmlserializer.Serialize(writer, value, namespaces);
return stringWriter.ToString();
}
}
public static void Main(string[] args)
{
var login = new Login();
login.ContactType = "XMLType";
login.Email = "x#x.com";
var a = SerializeXml(login);
Console.WriteLine(a);
Console.ReadLine();
}
Result
<Login xmlns:q1="http://tempuri.org/Logon">
<contactType>XMLType</contactType>
<email>x#x.com</email>
</Login>

Circular reference using XML serialization

I have found a solution for handling circular reference when using xml serialization. But in my case I have a List:
public class Category
{
public Category()
{
Items = new List<CategoryItem>();
}
public string CategoryName { get; set; }
public List<CategoryItem> Items { get; set; }
}
and:
public class CategoryItem
{
public string Link { get; set; }
public Category Category { get; set; }
}
Program:
private static void Main(string[] args)
{
var programmingCategory = new Category {CategoryName = "Programming"};
var ciProgramming = new CategoryItem
{
Link = "www.stackoverflow.com",
Category = programmingCategory
};
var fooCategory = new CategoryItem
{
Category = programmingCategory,
Link = "www.foo.com"
};
programmingCategory.Items.Add(ciProgramming);
programmingCategory.Items.Add(fooCategory);
var serializer = new XmlSerializer(typeof (Category));
var file = new FileStream(FILENAME, FileMode.Create);
serializer.Serialize(file, programmingCategory);
file.Close();
}
I always get an
InvalidOperationException
How can I solve this?
You just change CategoryItem model
public class CategoryItem
{
public string Link { get; set; }
}
Modify this code :
private static void Main(string[] args)
{
var programmingCategory = new Category {CategoryName = "Programming"};
var ciProgramming = new CategoryItem
{
Link = "www.stackoverflow.com"
};
var fooCategory = new CategoryItem
{
Link = "www.foo.com"
};
programmingCategory.Items.Add(ciProgramming);
programmingCategory.Items.Add(fooCategory);
var serializer = new XmlSerializer(typeof (Category));
var file = new FileStream(FILENAME, FileMode.Create);
serializer.Serialize(file, programmingCategory);
file.Close();
}
i think it's work fine.Please try this code. I just change your model and get this output.
<?xml version="1.0"?>
-<Category xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<CategoryName>Programming</CategoryName>
-<Items>
-<CategoryItem>
<Link>www.stackoverflow.com</Link>
</CategoryItem>
-<CategoryItem>
<Link>www.foo.com</Link>
</CategoryItem>
</Items>
</Category>

Error when deserializing

I'm trying to write some code to deserialize an XML file. I've looked around a bit and found something which has led me to the following method:
public static void Deserialize(string filePath)
{
RootObject ro = null;
string path = filePath;
XmlSerializer serializer = new XmlSerializer(typeof(RootObject));
StreamReader reader = new StreamReader(path);
ro = (RootObject) serializer.Deserialize(reader);
reader.Close();
}
But all I get is this error and I'm not sure what's causing it:
There is an error in XML document (2, 2).
The RootObject you see in Deserialize() is this one: I'm new to XMl serializing/deserializing so I'm not sure if I've defined it 100% correctly.
public class RootObject
{
public Services Services { get; set; }
}
public class Services
{
public Service TileMapService { get; set; }
}
public class Service
{
public string Title { get; set; }
public string href { get; set; }
}
I'm using this method to create the XML files in the first place and it seems to work fine:
public static void WriteToXmlFile<T>(string filePath, T objectToWrite) where T : new()
{
TextWriter writer = null;
try
{
var serializer = new XmlSerializer(typeof (T));
writer = new StreamWriter(filePath);
serializer.Serialize(writer, objectToWrite);
}
finally
{
if (writer != null)
{
writer.Close();
}
}
}
It gets me an XML file that looks like this:
<RootObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Services>
<TileMapService>
<Title>Some title</Title>
<href>http://something</href>
</TileMapService>
</Services>
</RootObject>
Your code works fine. The "There is an error in XML document (2, 2)." might be because your actual file (not the one created by WriteToXmlFile<T>) has whitespace at the start, or is using the wrong namespace. Check the .InnerException for more detail, or (perhaps simpler) please post the actual xml file contents. Example of it working fine (along with some recommended tweaks to the two key methods):
static void Main()
{
RootObject obj = new RootObject
{
Services = new Services
{
TileMapService = new Service
{
Title = "abc",
href = "def"
}
}
};
WriteToXmlFile("foo.xml", obj);
var loaded = Deserialize<RootObject>("foo.xml");
var svc = loaded.Services.TileMapService;
System.Console.WriteLine(svc.Title); // abc
System.Console.WriteLine(svc.href); // def
}
public static void WriteToXmlFile<T>(string filePath, T objectToWrite)
{
var serializer = new XmlSerializer(typeof(T));
using (var writer = new StreamWriter(filePath))
{
serializer.Serialize(writer, objectToWrite);
}
}
public static T Deserialize<T>(string filePath)
{
var serializer = new XmlSerializer(typeof(T));
using (var reader = new StreamReader(filePath))
{
return (T)serializer.Deserialize(reader);
}
}

XmlSerializer add attribute

I have this item Class :
public class Movie
{
public string VideoId { get; set; }
public string Title { get; set; }
}
And i have List<Movie> of this items and i use this code to Serialize to xml file:
string fileName = index + ".xml";
string serializationFile = Path.Combine(dir, fileName);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
using (var writer = XmlWriter.Create(serializationFile, settings))
{
var serializer = new XmlSerializer(typeof(List<Movie>));
serializer.Serialize(writer, tmpList);
}
And this is the result:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMovie xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Movie>
<VideoId>MyId</VideoId>
<Title>MyTitle</Title>
</Movie>
<Movie>
<VideoId>MyId1</VideoId>
<Title>MyTitle1</Title>
</Movie>
<Movie>
<VideoId>MyId2</VideoId>
<Title>MyTitle2</Title>
</Movie>
<Movie>
<VideoId>MyId3</VideoId>
<Title>MyTitle3</Title>
</Movie>
</ArrayOfMovie>
And it this possible to add attribute to the ArrayOfMovie node,something like this:
<ArrayOfMovie xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" customattribute='Yes'>
Yes, you can do this using the XmlAttribute attribute. In order to do this, you need to define your custom attribute. It comes with the price of one more class that represents the array (nested in the root node). If you have no problem with this addition, then the solution can look like this:
public class ArrayOfMovie
{
// define the custom attribute
[XmlAttribute(AttributeName="CustomAttribute")]
public String Custom { get; set; }
// define the collection description
[XmlArray(ElementName="Items")]
public List<Movie> Items { get; set; }
}
public class Movie
{
public string VideoId { get; set; }
public string Title { get; set; }
}
Then create, fill and serialize as you already do - the one new thing is to fill your custom attribute:
// create and fill the list
var tmpList = new List<Movie>();
tmpList.Add(new Movie { VideoId = "1", Title = "Movie 1" });
tmpList.Add(new Movie { VideoId = "2", Title = "Movie 2" });
// create the collection
var movies = new ArrayOfMovie
{
Items = tmpList,
Custom = "yes" // fill the custom attribute
};
// serialize
using (var writer = XmlWriter.Create(serializationFile, settings))
{
var serializer = new XmlSerializer(typeof(ArrayOfMovie));
serializer.Serialize(writer, movies);
}
The XML output looks like this:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMovie xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
CustomAttribute="yes">
<Items>
<Movie>
<VideoId>1</VideoId>
<Title>Movie 1</Title>
</Movie>
<Movie>
<VideoId>2</VideoId>
<Title>Movie 2</Title>
</Movie>
</Items>
</ArrayOfMovie>
You could do it after serialization. The code skeleton looks like:
using (MemoryStream ms = new MemoryStream())
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
using (var writer = XmlWriter.Create(ms, settings))
{
var serializer = new XmlSerializer(typeof(List<Movie>));
serializer.Serialize(writer, tmpList);
}
ms.Position = 0;
XDocument doc = XDocument.Load(new XmlTextReader(ms));
doc.Root.Add(new XAttribute("customAttribute", "Yes"));
doc.Save(filename);
}
You would want to wrap the List<Movie> inside a class and then serialize that.
Something like the following
class Program
{
static void Main(string[] args)
{
string fileName = "abcd2.xml";
string serializationFile = Path.Combine(#"C:\", fileName);
List<Movie> tmpList = new List<Movie>();
tmpList.Add(new Movie() { VideoId = "1", Title = "Hello" });
tmpList.Add(new Movie() { VideoId = "2", Title = "ABCD" });
MovieList list = new MovieList("Yes", tmpList);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
using (var writer = XmlWriter.Create(serializationFile, settings))
{
var serializer = new XmlSerializer(typeof(MovieList));
serializer.Serialize(writer, list);
}
}
}
public class MovieList
{
private string custom;
private List<Movie> movies;
public MovieList() { }
public MovieList(string custom, List<Movie> movies)
{
this.movies = movies;
this.custom = custom;
}
[XmlAttribute]
public string CustomAttribute
{
get { return this.custom; }
set { this.custom = value; }
}
public List<Movie> Movies
{
get
{
return movies;
}
set
{
this.movies = value;
}
}
}
public class Movie
{
public string VideoId { get; set; }
public string Title { get; set; }
}
The code can be improved a lot. This is just an example snippet. Check out the following MSDN link: http://msdn.microsoft.com/en-us/library/58a18dwa(v=vs.110).aspx

PaymentDetails is not successfully deserialized, returning a null object, because it seems it is expected to have IXmlDeserializable

[XmlRoot("Quote")]
public class Quote
{
[XmlElement("Insurance")]
public InsuranceDetails InsDetails { get; set; }
[XmlElement("Payment")]
public PaymentDetails PayDetails { get; set; }
}
public class InsuranceDetails : IXmlSerializable
{
[XmlElement(ElementName = "Details1")]
public string Details1 { get; set; }
public void ReadXml(XmlReader reader)
{
reader.ReadStartElement("Insurance");
Details1 = reader.ReadElementString("Details1");
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer)
{
// do write suff
}
}
public class PaymentDetails
{
[XmlElement(ElementName = "Details1")]
public string Details1 { get; set; }
}
Given this example, using XmlSerializer to deserialize my string to QuoteObject, PaymentDetails is not successfully deserialized, returning a null object, because it seems it is expected to have IXmlDeserializable. It only works if PaymentDetails is parsed in first place. Is this some expected behavior from XmlSerializer?
using (TextReader read = new StringReader(xml))
{
var serializer = new XmlSerializer(typeof(Quote));
return (Quote)serializer.Deserialize(read);
}
Well these are the ReadXml and WriteXml I modified:
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
var empty=reader.IsEmptyElement;
reader.ReadStartElement();
if(!empty){
Details1=reader.ReadElementString("Details1");
reader.ReadEndElement();
}
}
public void WriteXml(XmlWriter writer)
{
var str=string.IsNullOrWhiteSpace(Details1)?"":Details1;
writer.WriteElementString("Details1",str);
}
Following are serialize and deserialize functions:
public static string Serialize<T>(T t)
{
var xmlser=new XmlSerializer(typeof(T));
XmlWriterSettings settings = new XmlWriterSettings();
using(StringWriter textWriter = new StringWriter()) {
using(XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings)) {
xmlser.Serialize(xmlWriter, t);
}
return textWriter.ToString();
}
}
public static T Deserialize<T>(string xml)
{
if(string.IsNullOrEmpty(xml)) {
return default(T);
}
XmlSerializer serializer = new XmlSerializer(typeof(T));
XmlReaderSettings settings = new XmlReaderSettings();
using(StringReader textReader = new StringReader(xml)) {
using(XmlReader xmlReader = XmlReader.Create(textReader, settings)) {
return (T) serializer.Deserialize(xmlReader);
}
}
}
Serialization Test:
var q=new Quote();
q.PayDetails = new PaymentDetails{Details1="Payment Details 1"};
q.InsDetails=new InsuranceDetails{Details1="Insurance Details 1"};
str = Serialize<Quote>(q);
Which gives (str):
<?xml version="1.0" encoding="utf-16"?>
<Quote xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Payment>
<Details1>Payment Details 1</Details1>
</Payment>
<Insurance>
<Details1>Insurance Details 1</Details1>
</Insurance>
</Quote>
Deserialization Test:
var dq=Deserialize<Quote>(str);
Console.WriteLine(dq.PaymentDetails.Detail1);//gives "Payment Details 1"
Console.WriteLine(dq.InsuranceDetails.Detail1);//gives "Insurance Details 1"
PS:- The Serialize code was copied from another SO answer verbatim. I learned how to serialize to string using StringWriter.
First of all you don't have to implement IXmlSerializable in any of the classes. Second of all, you don't provide the content of the xml variable. It may contain a mistype/bug, if you created it manually.
I used the following code, to test your classes:
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace XmlDeSerialize
{
[XmlRoot("Quote")]
public class Quote
{
[XmlElement("Insurance")]
public InsuranceDetails InsDetails { get; set; }
[XmlElement("Payment")]
public PaymentDetails PayDetails { get; set; }
}
public class InsuranceDetails
{
[XmlElement(ElementName = "Details1")]
public string Details1 { get; set; }
}
public class PaymentDetails
{
[XmlElement(ElementName = "Details1")]
public string Details1 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var qin = new Quote
{
InsDetails = new InsuranceDetails { Details1 = "insurance details text" },
PayDetails = new PaymentDetails { Details1 = "payment details text" },
};
string xml;
using (var stream = new MemoryStream())
{
var serializer = new XmlSerializer(typeof(Quote));
serializer.Serialize(stream, qin);
stream.Position = 0;
using (var sr = new StreamReader(stream))
{
xml = sr.ReadToEnd();
}
}
Quote qout;
using (TextReader read = new StringReader(xml))
{
var deserializer = new XmlSerializer(typeof(Quote));
var obj = deserializer.Deserialize(read);
qout = (Quote)obj;
}
Console.WriteLine("InsDetails.Details1='{0}'", qout.InsDetails.Details1);
Console.WriteLine("PayDetails.Details1='{0}'", qout.PayDetails.Details1);
}
}
}
The value of xml after serialization:
<?xml version="1.0"?>
<Quote xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Insurance>
<Details1>insurance details text</Details1>
</Insurance>
<Payment>
<Details1>payment details text</Details1>
</Payment>
</Quote>
The console output I received:
InsDetails.Details1='insurance details text'
PayDetails.Details1='payment details text'
Try the code yourself and see if it works for you. Clearly to me you don't provide valid XML content for deserialization, or other part of your code you did not provide in your question is to blame.

Categories