I've got this XML;
<ChartXml>
<Category type="xAxis">
<Value>Mon</Value>
<Value>Tue</Value>
<Value>Wed</Value>
<Value>Thurs</Value>
<Value>Friday</Value>
</Category>
</ChartXml>
And this class structure;
[XmlRoot("ChartXml")]
public class ChartXml
{
[XmlElement("Category")]
public Category Category;
}
public class Category
{
[XmlAttribute("type")]
public string Type;
[XmlArray("Value")]
public List<string> Values;
}
Now when I try to serialise the XML to a ChartXml object, I get object containing the Category with the type attribute set correctly, but i dont get my list of strings.
I'm guessing its something wrong with the way i've set up my classes.
Thanks,
james.
Use [XmlElement("Value")] instead of [XmlArray("Value")], e.g.:
[XmlRoot("ChartXml")]
public class ChartXml
{
[XmlElement("Category")]
public Category Category;
}
public class Category
{
[XmlAttribute("type")]
public string Type;
[XmlElement("Value")]
public List<string> Values;
}
Serialization:
var obj = new ChartXml { Category = new Category { Type = "t", Values = new List<string> { "a", "b", "c" } } };
var ser = new XmlSerializer(obj.GetType());
using (var tw=new StringWriter())
{
ser.Serialize(tw, obj);
Console.WriteLine(tw.ToString());
}
Output:
<?xml version="1.0" encoding="utf-16"?>
<ChartXml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http:
//www.w3.org/2001/XMLSchema">
<Category type="t">
<Value>a</Value>
<Value>b</Value>
<Value>c</Value>
</Category>
</ChartXml>
Use [XmlElement("Value")] instead of [XmlArray("Value")]
Related
During de-serialization, how do I ignore a property? In my case, I don't want the FullName property to get initialized at all. I am not looking for [XMLIgnore] solutions - think it as a scenario where I don't have access to change the class.
Here's my class:
public class Person
{
public int Id { get; set; }
public string FullName { get; set; }
}
Here's how I am initializing:
Person p1 = new Person() { Id = 1, FullName = "P1" };
Person p2 = new Person() { Id = 2, FullName = "P2" };
List<Person> persons = new List<Person> { p, q }; //this object is xml serialized.
And here's my XML: ( I got it though the XML Serialization)
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfPerson xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Person>
<Id>1</Id>
<FullName>P1</FullName>
</Person>
<Person>
<Id>2</Id>
<FullName>P2</FullName>
</Person>
</ArrayOfPerson>
You can use a custom XmlReader in the deserialization process that will simply skip over the FullName elements. Something like this:
public class MyXmlReader : XmlTextReader
{
public MyXmlReader(string filePath) : base(filePath)
{
}
public override bool Read()
{
if (base.Read())
{
if (Name == "FullName")
return base.Read();
return true;
}
return false;
}
}
Then use it like this
var serializer = new XmlSerializer(typeof(List<Person>));
using (var reader = new MyXmlReader("XMLFile.xml"))
{
var person = (List<Person>)serializer.Deserialize(reader);
}
You can implement a different constructor to take a stream or whatever you have. It doesn't have to be a file path.
I am trying to create a WCF-service, which can return me an XML document looking something like this:
<alert>
<identifier>SecretID</identifier>
<info>
<valueName>Name1</valueName>
<value>Info1</value>
</info>
<info>
<valueName>Name2</valueName>
<value>Info2</value>
</info>
</alert>
The DTO of this file would look something like this:
[DataContract(Name = "alert", Namespace = "")]
public class Alert
{
[DataMember(Name = "identifier")]
public string identifier { get; set; }
[DataMember(Name = "info")]
public List<Info> Info { get; set; }
}
[DataContract(Name = "info", Namespace = "")]
public class Info
{
[DataMember(Name = "valueName")]
public string ValueName { get; set; }
[DataMember(Name = "value")]
public string Value { get; set; }
}
However, when I try with the following:
var alert = new Alert()
{
Identifier = "SecretID",
Info = new List<Info>
{
new Info() {ValueName = "Name1", Value = "Info1"},
new Info() {ValueName = "Name2", Value = "Info2"},
}
}
I get:
<alert>
<identifier>SecretID</identifier>
<info>
<info xmlns="">
<valueName>Name1</valueName>
<value>Info1</value>
</info>
<info xmlns="">
<valueName>Name2</valueName>
<value>Info2</value>
</info>
</info>
</alert>
I don't need the extra <info> tag, and the namespace xmlns="" would be nice to have removed too. What should I do to get rid of it?
You can use dictionary or key value pairs as mentioned below:
[DataContract(Name = "alert", Namespace = "")]
public class Alert
{
[DataMember(Name = "identifier")]
public string identifier { get; set; }
[DataMember(Name = "info")]
public KeyValuePair<string, string> Info { get; set; }
}
var alert = new Alert()
{
Identifier = "SecretID",
Info = new KeyValuePair<string, string>()
}
I'm trying to deserialize simple xml file:
<thesaurus xmlns="http://marklogic.com/xdmp/thesaurus">
<metadata>
</metadata>
<entry>
<term>a</term>
<synonym>
<term>as</term>
</synonym>
</entry>
<entry>
<term>b</term>
<synonym>
<term>bs</term>
</synonym>
<synonym>
<term>bss</term>
</synonym>
</entry>
</thesaurus>
I'm using XmlSerializer like this:
var xmlSerializer = new XmlSerializer(typeof(Thesaurus));
var thesaurus = xmlSerializer.Deserialize(stream);
My model looks like this:
[Serializable]
[XmlRoot("thesaurus", Namespace = "http://marklogic.com/xdmp/thesaurus")]
public class Thesaurus
{
[XmlElement("metadata")]
public Metadata Metadata { get; set; }
[XmlElement("entry")]
public List<Entry> Entries { get; set; }
}
public class Metadata
{
}
public class Entry
{
[XmlElement("term")]
public string Term { get; set; }
[XmlElement("synonym")]
public String[] Synonym { get; set; }
}
So when I'm running this code, I get deserialized thesaurus object with parsed metadata and 1 entry with filled term and synonym fields. I can't get all of the entries here.
BUT
when I comment out Synonym field it starts giving me 2 entries in thesaurus object. I can't wrap entries in <entries> tag because it's some internal format of an application I'm feeding with this xml file.
Anyone has any ideas how to parse this xml file correctly? I tried searching for a solution, but this xml looks quite different than ones in examples.
Ok, so if you need to keep inside synonim field array of terms fields you need to change your Entry class to something like this:
public class Entry
{
[XmlElement("term")]
public string Term { get; set; }
[XmlElement("synonim")]
public Term[] Synonym { get; set; }
}
also you'll need to add additional one:
public class Term
{
[XmlElement("term")]
public string Value { get; set; }
}
This way you'll have what you need.
So, additional hierarchy level was added by additional class.
Please find below code for your test:
var xmlSerializer = new XmlSerializer(typeof(Thesaurus));
var r = new Thesaurus();
r.Entries = new List<Entry>();
r.Metadata = new Metadata();
r.Entries.Add(new Entry()
{
Synonym = new Term[] { new Term(){Value = "1"}, new Term() {Value = "2"}, },
Term = "Term1"
});
r.Entries.Add(new Entry()
{
Synonym = new Term[] { new Term() { Value = "3" }, new Term() { Value = "4" }, },
Term = "Term2"
});
using (TextWriter writer = new StreamWriter(#"c:\111.xml"))
{
xmlSerializer.Serialize(writer, r);
writer.Close();
}
using (TextReader reader = new StreamReader(#"c:\111.xml"))
{
Thesaurus tt = xmlSerializer.Deserialize(reader) as Thesaurus;
Console.Write(tt.Entries.Count);
reader.Close();
}
I'm trying to build serialization for my application that must export data in specific format, below is sample what is expected:
<?xml version="1.0" encoding="utf-8"?>
<sync>
<table name="Test" diff="0" mode="db">
<keys>
<key>MY_NUMBER</key>
<key>ID</key>
</keys>
<items task="modify">
<item ID="OK" MY_NUMBER="two"/>
<item ID="NT" MY_NUMBER="two"/>
</items>
</table>
<table name="Second" diff="1" mode="x">
<keys>
<key>ID</key>
</keys>
<items task="add">
<item ID="x" TYPE="c"/>
</items>
</table>
</sync>
I was able to get similar results:
<?xml version="1.0" encoding="utf-8"?>
<sync>
<table name="Test" diff="0" mode="db" task="modify">
<keys>
<key>MY_NUMBER</key>
<key>ID</key>
</keys>
<items>
<item d4p1:type="FirstItem" ID="OK" MY_NUMBER="two" xmlns:d4p1="http://www.w3.org/2001/XMLSchema-instance" />
<item d4p1:type="FirstItem" ID="NT" MY_NUMBER="two" xmlns:d4p1="http://www.w3.org/2001/XMLSchema-instance" />
</items>
</table>
<table name="SecondTest" diff="1" mode="x" task="add">
<keys>
<key>ID</key>
</keys>
<items>
<item d4p1:type="SecondItem" ID="x" TYPE="c" xmlns:d4p1="http://www.w3.org/2001/XMLSchema-instance" />
</items>
</table>
</sync>
But I get unwanted namespaces, I've tried searching SO for solutions (Omitting all xsi and xsd namespaces when serializing an object in .NET?, Remove Namespaces During XML Serialization) but without luck.
I have my classes defined like this:
namespace Sync.Models
{
[XmlRoot("sync")]
[XmlInclude(typeof(FirstItem))]
[XmlInclude(typeof(SecondItem))]
public class Export
{
[XmlElement(ElementName = "table")]
public Table users { get; set; }
[XmlElement(ElementName = "table2")]
public Table items { get; set; }
}
public class Table
{
[XmlAttribute("name")]
public string name { get; set; }
[XmlAttribute("diff")]
public int diff { get; set; }
[XmlAttribute("mode")]
public string mode { get; set; }
[XmlArray("keys")]
[XmlArrayItem("key")]
public List<string> Keys { get; set; }
[XmlArray("items")]
[XmlArrayItem("item")]
public List<BaseItem> Items { get; set; }
[XmlAttribute("task")]
public string Task { get; set; }
}
public class FirstItem:BaseItem
{
[XmlAttribute("MY_NUMBER")]
public string Number { get; set; }
}
public class SecondItem:BaseItem
{
[XmlAttribute("TYPE")]
public string Type { get; set; }
}
}
And finally my serialization functionality:
var testData = new Export
{
users = new Table
{
name = "Test",
diff = 0,
mode = "db",
Keys = new List<string> { "MY_NUMBER", "ID" },
Items = new List<BaseItem>
{
new FirstItem {Id = "OK", Number = "two"},
new FirstItem {Id = "NT", Number = "two"}
},
Task = "modify"
},
items = new Table
{
name = "SecondTest",
diff = 1,
mode = "x",
Keys = new List<string> { "ID" },
Items = new List<BaseItem>
{
new SecondItem{Id = "x",Type = "c"}
},
Task = "add"
}
};
var fileName_tmp = String.Format(#"{0}\xml1.xml", Application.StartupPath);
var fileName = String.Format(#"{0}\xml.xml", Application.StartupPath);
var serializer = new XmlSerializer(typeof(Export));
using (TextWriter writer = new StreamWriter(fileName_tmp))
{
serializer.Serialize(writer, testData, new XmlSerializerNamespaces(new[] {XmlQualifiedName.Empty}));
}
using (FileStream inputStream = File.OpenRead(fileName_tmp))
{
using (StreamReader inputReader = new StreamReader(inputStream))
{
using (StreamWriter outputWriter = File.CreateText(fileName))
{
string tempLineValue;
while (null != (tempLineValue = inputReader.ReadLine()))
{
outputWriter.WriteLine(tempLineValue.Replace("table2", "table"));
}
}
}
}
1. I would like to remove unwanted namespaces and type attribute from nodes, but as I wrote before solution I found isn't working.
2. I need to have multiple nodes with same name (table), for now my only solution is to replace tag after serialization. I know I could use List to store tables, but during serialization this is giving me one extra unwanted level-can this be removed? Or custom serialization is only option?
3. Right now Task property from table is stored as attribute of table node in xml. Can I move it to items, to get desired result? Or do I must create custom serialization?
you can remove namesapces by add this code
using (FileStream stream = new FileStream("FilePath",FileMode.Create))
{
XmlSerializer serializer = new XmlSerializer(typeof(YourClass));
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
serializer.Serialize(stream," Your Object to Serialize",ns);
}
Given the following XML:
<?xml version="1.0"?>
<user_list>
<user>
<id>1</id>
<name>Joe</name>
</user>
<user>
<id>2</id>
<name>John</name>
</user>
</user_list>
And the following class:
public class User {
[XmlElement("id")]
public Int32 Id { get; set; }
[XmlElement("name")]
public String Name { get; set; }
}
Is it possible to use XmlSerializer to deserialize the xml into a List<User> ? If so, what type of additional attributes will I need to use, or what additional parameters do I need to use to construct the XmlSerializer instance?
An array ( User[] ) would be acceptable, if a bit less preferable.
You can encapsulate the list trivially:
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
[XmlRoot("user_list")]
public class UserList
{
public UserList() {Items = new List<User>();}
[XmlElement("user")]
public List<User> Items {get;set;}
}
public class User
{
[XmlElement("id")]
public Int32 Id { get; set; }
[XmlElement("name")]
public String Name { get; set; }
}
static class Program
{
static void Main()
{
XmlSerializer ser= new XmlSerializer(typeof(UserList));
UserList list = new UserList();
list.Items.Add(new User { Id = 1, Name = "abc"});
list.Items.Add(new User { Id = 2, Name = "def"});
list.Items.Add(new User { Id = 3, Name = "ghi"});
ser.Serialize(Console.Out, list);
}
}
If you decorate the User class with the XmlType to match the required capitalization:
[XmlType("user")]
public class User
{
...
}
Then the XmlRootAttribute on the XmlSerializer ctor can provide the desired root and allow direct reading into List<>:
// e.g. my test to create a file
using (var writer = new FileStream("users.xml", FileMode.Create))
{
XmlSerializer ser = new XmlSerializer(typeof(List<User>),
new XmlRootAttribute("user_list"));
List<User> list = new List<User>();
list.Add(new User { Id = 1, Name = "Joe" });
list.Add(new User { Id = 2, Name = "John" });
list.Add(new User { Id = 3, Name = "June" });
ser.Serialize(writer, list);
}
...
// read file
List<User> users;
using (var reader = new StreamReader("users.xml"))
{
XmlSerializer deserializer = new XmlSerializer(typeof(List<User>),
new XmlRootAttribute("user_list"));
users = (List<User>)deserializer.Deserialize(reader);
}
Credit: based on answer from YK1.
Yes, it will serialize and deserialize a List<>. Just make sure you use the [XmlArray] attribute if in doubt.
[Serializable]
public class A
{
[XmlArray]
public List<string> strings;
}
This works with both Serialize() and Deserialize().
I think I have found a better way. You don't have to put attributes into your classes. I've made two methods for serialization and deserialization which take generic list as parameter.
Take a look (it works for me):
private void SerializeParams<T>(XDocument doc, List<T> paramList)
{
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(paramList.GetType());
System.Xml.XmlWriter writer = doc.CreateWriter();
serializer.Serialize(writer, paramList);
writer.Close();
}
private List<T> DeserializeParams<T>(XDocument doc)
{
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(List<T>));
System.Xml.XmlReader reader = doc.CreateReader();
List<T> result = (List<T>)serializer.Deserialize(reader);
reader.Close();
return result;
}
So you can serialize whatever list you want! You don't need to specify the list type every time.
List<AssemblyBO> list = new List<AssemblyBO>();
list.Add(new AssemblyBO());
list.Add(new AssemblyBO() { DisplayName = "Try", Identifier = "243242" });
XDocument doc = new XDocument();
SerializeParams<T>(doc, list);
List<AssemblyBO> newList = DeserializeParams<AssemblyBO>(doc);
Yes, it does deserialize to List<>. No need to keep it in an array and wrap/encapsulate it in a list.
public class UserHolder
{
private List<User> users = null;
public UserHolder()
{
}
[XmlElement("user")]
public List<User> Users
{
get { return users; }
set { users = value; }
}
}
Deserializing code,
XmlSerializer xs = new XmlSerializer(typeof(UserHolder));
UserHolder uh = (UserHolder)xs.Deserialize(new StringReader(str));
Not sure about List<T> but Arrays are certainly do-able. And a little bit of magic makes it really easy to get to a List again.
public class UserHolder {
[XmlElement("list")]
public User[] Users { get; set; }
[XmlIgnore]
public List<User> UserList { get { return new List<User>(Users); } }
}
How about
XmlSerializer xs = new XmlSerializer(typeof(user[]));
using (Stream ins = File.Open(#"c:\some.xml", FileMode.Open))
foreach (user o in (user[])xs.Deserialize(ins))
userList.Add(o);
Not particularly fancy but it should work.
Yes, you can deserialize into List<User> or User[] with the XmlSerializer. I would prefer List<User> over User[]. Note that XmlSerializer does not support deserialization into interfaces, so you cannot deserialize into ICollection<T>, IReadOnlyCollection<T> or IList<T> as this will fail with a NotSupportedException.