Possible to change XML root element name thru XmlSerializer?

C

csharper

If I have this:

public class Book
{
public string Title {get; set;}
public string Author {get; set; }
}

When I XmlSerialize it, the root element of the XML will be Book.

I want it to be Book sometimes and at some other time I want it to be Libros, and still some other time to be TextBook.

Is this achievable thru this single Book class? Is there an XmlSerializer constructor that takes a string parameter as the root element name?

Must I modify the Book class's XmlRootAttribute value thru reflection if at all feasible? Any suggestions? Thanks.
 
A

Arne Vajhøj

If I have this:

public class Book
{
public string Title {get; set;}
public string Author {get; set; }
}

When I XmlSerialize it, the root element of the XML will be Book.

I want it to be Book sometimes and at some other time I want it to be Libros, and still some other time to be TextBook.

Is this achievable thru this single Book class? Is there an XmlSerializer constructor that takes a string parameter as the root element name?

Must I modify the Book class's XmlRootAttribute value thru reflection if at all feasible? Any suggestions? Thanks.

I am a bit skeptical about the concept, but XmLSerializer supports it.

Example:

using System;
using System.IO;
using System.Xml.Serialization;

namespace E
{
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}
public class Program
{
public static void Standard()
{
Book o = new Book();
o.Title = "XML Serialization is fun";
o.Author = "Arne";
XmlSerializer xmlser = new XmlSerializer(typeof(Book));
using(StreamWriter sw = new StreamWriter(@"C:\work\book.xml"))
{
xmlser.Serialize(sw, o);
}
Book o2;
using(StreamReader sr = new StreamReader(@"C:\work\book.xml"))
{
o2 = (Book)xmlser.Deserialize(sr);
}
Console.WriteLine(o2.Title + "/" + o2.Author);
}
public static void Modifying()
{
Book o = new Book();
o.Title = "XML Serialization is fun";
o.Author = "Arne";
XmlAttributes attrs = new XmlAttributes();
attrs.XmlRoot = new XmlRootAttribute("GoodBook");
XmlAttributeOverrides over = new XmlAttributeOverrides();
over.Add(typeof(Book), attrs);
XmlSerializer xmlser = new XmlSerializer(typeof(Book), over);
using(StreamWriter sw = new StreamWriter(@"C:\work\book2.xml"))
{
xmlser.Serialize(sw, o);
}
Book o2;
using(StreamReader sr = new StreamReader(@"C:\work\book2.xml"))
{
o2 = (Book)xmlser.Deserialize(sr);
}
Console.WriteLine(o2.Title + "/" + o2.Author);
}
public static void Main(string[] args)
{
Standard();
Modifying();
Console.ReadKey();
}
}
}

Arne
 
C

csharper

Thanks. I did find out at MSDN that XmlSerializer does have a constructor that takes an XmlRootAttribute, through which we can name our root element. Arne's code also shows this. Thanks.

Things will be more complicated if a class contains a Book object like so:

public class Wraper
{
public string Name { get; set; }
public Book book { get; set; }
}

Now the said XmlSerializer constructor won't help. I guess Peter's idea of inheriting from Book will help. Thank him.

It would be nice if we could do something like this:

Book b = new Book {Title = "My Book Title", Author = "John Doe"};
b.XmlRootAttribute = new XmlRootAttribute("Libros"); // This doesn't exist.
Wraper w = new Wraper{ Name = "My Wraper Name", Book = b};

Then we simply create an XmlSerializer for the Wraper type and serialized it to XML and we get a Libros element.

When I need it to be TextBook, I simply say:

b.XmlRootAttribute = new XmlRootAttribute("TextBook"); // This doesn't exist.

Maybe an extension method using reflection? I haven't tested it out, but it seems it is possible to modify the attribute through reflection. See here: http://stackoverflow.com/questions/2160476/how-to-set-attributes-values-using-reflection
 
A

Arne Vajhøj

Thanks. I did find out at MSDN that XmlSerializer does have a
constructor that takes an XmlRootAttribute, through which we can name
our root element. Arne's code also shows this. Thanks.

Things will be more complicated if a class contains a Book object like so:

public class Wraper
{
public string Name { get; set; }
public Book book { get; set; }
}

Now the said XmlSerializer constructor won't help. I guess Peter's
idea of inheriting from Book will help. Thank him.

It would be nice if we could do something like this:

Book b = new Book {Title = "My Book Title", Author = "John Doe"};
b.XmlRootAttribute = new XmlRootAttribute("Libros"); // This doesn't exist.
Wraper w = new Wraper{ Name = "My Wraper Name", Book = b};

Then we simply create an XmlSerializer for the Wraper type and serialized it to XML and we get a Libros element.

When I need it to be TextBook, I simply say:

b.XmlRootAttribute = new XmlRootAttribute("TextBook"); // This doesn't exist.

Maybe an extension method using reflection? I haven't tested it out,
but it seems it is possible to modify the attribute through
reflection. See here:
http://stackoverflow.com/questions/2160476/how-to-set-attributes-values-using-reflection

Multi level is supported as well.

using System;
using System.IO;
using System.Xml.Serialization;

namespace E
{
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}
public class Wrapper
{
public string Name { get; set; }
public Book Book { get; set; }
}
public class Program
{
public static void Standard()
{
Book o = new Book();
o.Title = "XML Serialization is fun";
o.Author = "Arne";
Wrapper w = new Wrapper();
w.Name = "Demo";
w.Book = o;
XmlSerializer xmlser = new XmlSerializer(typeof(Wrapper));
using(StreamWriter sw = new
StreamWriter(@"C:\work\wrapper.xml"))
{
xmlser.Serialize(sw, w);
}
Wrapper w2;
using(StreamReader sr = new
StreamReader(@"C:\work\wrapper.xml"))
{
w2 = (Wrapper)xmlser.Deserialize(sr);
}
Console.WriteLine(w2.Name + ": " + w2.Book.Title + "/" +
w2.Book.Author);
}
public static void Modifying()
{
Book o = new Book();
o.Title = "XML Serialization is fun";
o.Author = "Arne";
Wrapper w = new Wrapper();
w.Name = "Demo";
w.Book = o;
XmlAttributes attrs = new XmlAttributes();
attrs.XmlRoot = new XmlRootAttribute("SomeWrapper");
XmlAttributes attrs2 = new XmlAttributes();
attrs2.XmlElements.Add(new XmlElementAttribute("GoodBook"));
XmlAttributeOverrides over = new XmlAttributeOverrides();
over.Add(typeof(Wrapper), attrs);
over.Add(typeof(Wrapper), "Book", attrs2);
XmlSerializer xmlser = new XmlSerializer(typeof(Wrapper),
over);
using(StreamWriter sw = new
StreamWriter(@"C:\work\wrapper2.xml"))
{
xmlser.Serialize(sw, w);
}
Wrapper w2;
using(StreamReader sr = new
StreamReader(@"C:\work\wrapper2.xml"))
{
w2 = (Wrapper)xmlser.Deserialize(sr);
}
Console.WriteLine(w2.Name + ": " + w2.Book.Title + "/" +
w2.Book.Author);
}
public static void Main(string[] args)
{
Standard();
Modifying();
Console.ReadKey();
}
}
}

Arne
 
C

csharper

That said, it seems that it is a little easier to simply inherit from Book. I am yet to research the XmlAttributeOverrides class. It seems from your code that we have to know that Book should be an immediate child of Wraper.
 
A

Arne Vajhøj

That said, it seems that it is a little easier to simply inherit from Book.

I would hav ethougth that would mean more cod, but maybe not.
It seems from your code that we have to know that Book should be an immediate child of Wraper.

Yes.

The approach is somewhat typesafe/specific - you change the element
name for specific properties in specific classes.

If you want a pure textual approach, then you need to something
like this:

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace E
{
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}
public class Wrapper
{
public string Name { get; set; }
public Book Book { get; set; }
}
public class Program
{
public static void Standard()
{
Book o = new Book();
o.Title = "XML Serialization is fun";
o.Author = "Arne";
Wrapper w = new Wrapper();
w.Name = "Demo";
w.Book = o;
XmlSerializer xmlser = new XmlSerializer(typeof(Wrapper));
using(StreamWriter sw = new
StreamWriter(@"C:\work\wrapper.xml"))
{
xmlser.Serialize(sw, w);
}
Wrapper w2;
using(StreamReader sr = new
StreamReader(@"C:\work\wrapper.xml"))
{
w2 = (Wrapper)xmlser.Deserialize(sr);
}
Console.WriteLine(w2.Name + ": " + w2.Book.Title + "/" +
w2.Book.Author);
}
private class MyXmlTextWriter : XmlTextWriter
{
private Dictionary<string, string> repl;
public MyXmlTextWriter(StreamWriter sw, Dictionary<string,
string> repl) : base(sw)
{
base.Formatting = Formatting.Indented;
this.repl = repl;
}
public override void WriteStartElement(string prefix,
string localName, string ns)
{
if(repl.ContainsKey(localName))
{
base.WriteStartElement(prefix, repl[localName], ns);
}
else
{
base.WriteStartElement(prefix, localName, ns);
}
}
}
private class MyXmlTextReader : XmlTextReader
{
private Dictionary<string, string> repl;
public MyXmlTextReader(StreamReader sr, Dictionary<string,
string> repl) : base(sr)
{
this.repl = repl;
}
public override string LocalName
{
get
{
if(repl.ContainsKey(base.LocalName))
{
return repl[base.LocalName];
}
else
{
return base.LocalName;
}
}
}
}
public static void Modifying()
{
Dictionary<string, string> wrepl = new Dictionary<string,
string>();
wrepl.Add("Wrapper", "SomeWrapper");
wrepl.Add("Book", "GoodBook");
Dictionary<string, string> rrepl = new Dictionary<string,
string>();
rrepl.Add("SomeWrapper", "Wrapper");
rrepl.Add("GoodBook", "Book");
Book o = new Book();
o.Title = "XML Serialization is fun";
o.Author = "Arne";
Wrapper w = new Wrapper();
w.Name = "Demo";
w.Book = o;
XmlSerializer xmlser = new XmlSerializer(typeof(Wrapper));
using(XmlWriter xw = new MyXmlTextWriter(new
StreamWriter(@"C:\work\wrapper2.xml"), wrepl))
{
xmlser.Serialize(xw, w);
}
Wrapper w2;
using(XmlReader xr = new MyXmlTextReader(new
StreamReader(@"C:\work\wrapper2.xml"), rrepl))
{
w2 = (Wrapper)xmlser.Deserialize(xr);
}
Console.WriteLine(w2.Name + ": " + w2.Book.Title + "/" +
w2.Book.Author);
}
public static void Main(string[] args)
{
Standard();
Modifying();
Console.ReadKey();
}
}
}

Arne
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top