XmlSerializer add attribute - c#

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

Related

Serialize XML without tag name

I have the following XML format:-
<?xml version="1.0"?>
<Price xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<amount>
<currency>USD</currency>
100
</amount>
<amount>
<currency>EUR</currency>
50
</amount>
</Price>
the XML value contains the amount in the xml root. May I know how can I serialize the value of 100 & 50 ?
[Serializable]
[XmlRoot("amount")]
public sealed class amount
{
[XmlElement("currency")]
public string currency{ get; set; }
}
class Program
{
static void Main(string[] args)
{
var list = new List<amount> {new amount() {Description = "USD"}, new amount() {Description = "EUR"}};
var serializer = new XmlSerializer(typeof(List<amount>), new XmlRootAttribute("Price"));
var ms = new MemoryStream();
serializer.Serialize(ms, list);
ms.Position = 0;
var result = new StreamReader(ms).ReadToEnd();
}
}
You can use XmlText:
[XmlRoot("amount")]
public sealed class amount
{
[XmlElement("currency")]
public string Description { get; set; }
// http://stackoverflow.com/a/1528429/613130
[XmlIgnore]
public int Value { get; set; }
[XmlText]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public string ValueXml
{
get
{
return XmlConvert.ToString(Value);
}
set
{
Value = XmlConvert.ToInt32(value);
}
}
}

Serializing with namespace declaration on childnode with XmlSerializer

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; }
}

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>

XmlSerializer / StreamWriter multiple types/classes into same xml file

I want to create an XML file depending on a schema (XSD). I've found this question on StackOverflow: Generating XML file using XSD file
It works with a simple sample like this:
var data = new ProfileType();
data.Name = "Test";
data.Address = "Street";
var serializer = new XmlSerializer(typeof(ProfileType));
using (var stream = new StreamWriter("D:\\test.xml")) serializer.Serialize(stream, data);
But how can I add more classes/types into the same xml file? If I add these lines of codes after them below, they overwrite the text.xml file:
var data2 = new MemberType();
data2.Age = "25";
data2.Code = "Z14x";
data2.Color = "Red":
var serializer2 = new XmlSerializer(typeof(MemberType));
using (var stream = new StreamWriter("D:\\test.xml")) serializer2.Serialize(stream, data2);
You can serialize a list of objects:
example:
public class Type1
{
public string Name { get; set; }
public Type1() { }
}
public class Type2
{
public string Name { get; set; }
public Type2() { }
}
//....
List<object> list = new List<object>();
list.Add(new Type1() { Name = "Name1" });
list.Add(new Type2() { Name = "Name2" });
XmlSerializer serializer = new XmlSerializer(typeof(List<object>), new Type[] { typeof(Type1), typeof(Type2) });
using (TextWriter writer = new StreamWriter("result.xml"))
{
serializer.Serialize(writer, list);
}
result:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfAnyType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<anyType xsi:type="Type1">
<Name>Name1</Name>
</anyType>
<anyType xsi:type="Type2">
<Name>Name2</Name>
</anyType>
</ArrayOfAnyType>

Xml node is getting replaced by another

I want to write university courses in an xml file and be sorted by departments. So if I add a course from different departments I will get something like:
<Departments>
<FST_Department>
<Course_Details>
<Course_Name >Networking</Course_Name>
<Course_Code >xx</Course_Code>
</Course_Details>
</FST_Department >
<Medicine_Department>
<Course_Details>
<Course_Name >General_Medicine</Course_Name>
<Course_Code >xxxxxxx</Course_Code>
</Course_Details>
</Medicine_Department>
</Departments
But instead all my courses go inside the last department I added a course to and the other department 'tags' disapear.Like below.
<Departments>
<Medicine_Department>
<Course_Details>
<Course_Name >Networking</Course_Name>
<Course_Code >xx</Course_Code>
</Course_Details>
<Course_Details>
<Course_Name >General Medicine</Course_Name>
<Course_Code >xxxxxxx</Course_Code>
</Course_Details>
</Medicine_Department>
</Departments
The code to create the xml file .
string path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
if (!Directory.Exists(path = "E:\\Sorting\\Directory"))
Directory.CreateDirectory(path = "E:\\Sorting\\Directory");
if (!File.Exists(path = "E:\\Sorting\\Directory\\Sort.xml"))
{
XmlTextWriter xW = new XmlTextWriter(path ="E:\\Sorting\\Directory\\Sort.xml", Encoding.UTF8);
xW.WriteStartElement("Departments");//my root
xW.WriteEndElement(); // Departments
xW.Close();
This is the code I use to add data to the xml file :
string path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
XmlDocument xDoc = new XmlDocument();
xDoc.Load("E:\\Sorting\\Directory\\Sort.xml");
XmlNode xNode = xDoc.SelectSingleNode("Departments");
xNode.RemoveAll();
if (combboBox.Text == "FST")
{
XmlNode xDepartmentNode = xDoc.CreateElement("FST_Department");
foreach (Course c in courses)
{
XmlNode xCourse_Details = xDoc.CreateElement("Course_Details");
XmlNode xCode = xDoc.CreateElement("Course_Code");
xCode.InnerText = c.courseCode;
xCourse_Details.AppendChild(xCode);
XmlNode xName = xDoc.CreateElement("Course_Name");
xName.InnerText = c.courseName;
xCourse_Details.AppendChild(xName);
xDepartmentNode.AppendChild(xCourse_Details);
xDoc.DocumentElement.AppendChild(xDepartmentNode);
xDoc.Save("E:\\Sorting\\Directory\\Sort.xml");
}
}
else if(combbobox.Text == "Medicine")
{
XmlNode xDepartmentNode = xDoc.CreateElement("Medicine_Department");
foreach (Course c in courses)
{
XmlNode xCourse_Details = xDoc.CreateElement("Course_Details");
XmlNode xCode = xDoc.CreateElement("Course_Code");
xCode.InnerText = c.courseCode;
xCourse_Details.AppendChild(xCode);
XmlNode xName = xDoc.CreateElement("Course_Name");
xName.InnerText = c.courseName;
xCourse_Details.AppendChild(xName);
xDepartmentNode.AppendChild(xCourse_Details);
xDoc.DocumentElement.AppendChild(xDepartmentNode);
xDoc.Save("E:\\Sorting\\Directory\\Sort.xml");
}
}
You clear all departments with this line. So anything that was there before is wiped from the file:
xNode.RemoveAll();
Second, with your foreach on courses, you will add all the courses to that department. Since you neither sort or filter the courses by department, you are getting them all into whatever department combobox.Text is selecting.
Scrap it and use XML Serialization lol
Departments Class
using System.Xml.Serialization;
namespace Serializer
{
public class Departments
{
public FST_Department FST_Department { get; set; }
public Medicine_Department Medicine_Department { get; set; }
}
}
FST_Department Class
using System.Collections.Generic;
namespace Serializer
{
public class FST_Department
{
public List<Course> Course_Details { get; set; }
}
}
Medicine_Department Class
using System.Collections.Generic;
namespace Serializer
{
public class Medicine_Department
{
public List<Course> Course_Details { get; set; }
}
}
Course Class
namespace Serializer
{
public class Course
{
public string Course_Name { get; set; }
public string Course_Code { get; set; }
}
}
Read and write serialized xml
using System.Collections.Generic;
using System.Xml.Serialization;
using System.IO;
namespace Serializer
{
class XMLManager
{
private const string configFileName = "Departments.xml";
public static Departments Read()
{
XmlSerializer reader = new XmlSerializer(typeof(Departments));
using (FileStream file = File.OpenRead(Path.Combine(Global.AppRoot, configFileName)))
{
return reader.Deserialize(file) as Departments;
}
}
public static void Write(Departments settings)
{
XmlSerializer writer = new XmlSerializer(typeof(Departments));
using (FileStream file = File.Create(Path.Combine(Global.AppRoot, configFileName)))
{
writer.Serialize(file, settings);
}
}
}
}
Usage
Departments depart = new Departments();
FST_Department fst = new FST_Department();
fst.Course_Details = new List<Course>();
Course course = new Course();
course.Course_Code = "1";
course.Course_Name = "Test 1";
fst.Course_Details.Add(course);
course = new Course();
course.Course_Code = "2";
course.Course_Name = "Test2";
fst.Course_Details.Add(course);
depart.FST_Department = fst;
Medicine_Department med = new Medicine_Department();
med.Course_Details = new List<Course>();
course = new Course();
course.Course_Code = "3";
course.Course_Name = "Test 3";
med.Course_Details.Add(course);
depart.Medicine_Department = med;
//put it all in some kind of order in case things were added out of order
depart.FST_Department.Course_Details.OrderBy(c => c.Course_Code);
depart.Medicine_Department.Course_Details.OrderBy(c => c.Course_Code);
XMLManager.Write(depart);
XML File Output
<?xml version="1.0"?>
<Departments xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<FST_Department>
<Course_Details>
<Course>
<Course_Name>Test 1</Course_Name>
<Course_Code>1</Course_Code>
</Course>
<Course>
<Course_Name>Test2</Course_Name>
<Course_Code>2</Course_Code>
</Course>
</Course_Details>
</FST_Department>
<Medicine_Department>
<Course_Details>
<Course>
<Course_Name>Test 3</Course_Name>
<Course_Code>3</Course_Code>
</Course>
</Course_Details>
</Medicine_Department>
</Departments>

Categories