C# DataContract DataMember EmitDefaultValue=false still outputting nil - c#

I have some very simple code.
//File Company.cs
using System;
using Sharpen;
using System.Reflection;
using System.Runtime.Serialization;
namespace XPathTest
{
[DataContract(IsReference=true)]
public class Company
{
[DataMember]
public AList<Person> employees {get; set;}
public Company ()
{
employees = new AList<Person>();
}
}
}
//File Employee.cs
using System;
using System.Reflection;
using System.Runtime.Serialization;
namespace XPathTest
{
[DataContract(IsReference=true)]
public class Employee : Person
{
public Employee ()
{
}
}
}
//File Manager.cs
using System;
using Sharpen;
using System.Reflection;
using System.Runtime.Serialization;
namespace XPathTest
{
[DataContract(IsReference=true)]
public class Manager : Person
{
[DataMember(EmitDefaultValue=false)]
public AList<Person> employees { get; set; }
public Manager ()
{
employees = new AList<Person>();
}
public void AddEmployee(Person employee)
{
employees.Add (employee);
}
}
}
//File Person.cs
using System;
using System.Reflection;
using System.Runtime.Serialization;
namespace XPathTest
{
[DataContract(IsReference=true)]
public class Person
{
[DataMember]
public int SSNum { get; set; }
[DataMember]
public string name { get; set; }
[DataMember(EmitDefaultValue=false)]
public Person manager {get; set;}
public Person ()
{
}
public void SetManager(Person manager)
{
this.manager = manager;
}
}
}
//File Main.cs
using System;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using XPathTest;
public class Test
{
public static void Main()
{
// Read and write purchase orders.
Test t = new Test();
t.MarshalCompany ("po.xml");
}
public Company BuildCompany ()
{
Company company = new Company();
Manager employee1 = new Manager();
employee1.SSNum = 1337;
employee1.name ="Jane Doe";
company.employees.Add(employee1);
Employee employee2 = new Employee();
employee2.SSNum = 8008132;
employee2.name = "John Smith";
employee2.SetManager(employee1);
company.employees.Add(employee2);
Employee employee3 = new Employee();
employee3.SSNum = 1138;
employee3.name = "Anne Jones";
employee3.SetManager(employee1);
company.employees.Add(employee3);
employee1.AddEmployee(employee2);
employee1.AddEmployee(employee3);
Manager manager1 = new Manager();
manager1.SSNum = 314;
manager1.name = "Boss Hog";
//manager1.setManager(manager1);
manager1.AddEmployee(employee1);
company.employees.Add(manager1);
return company;
}
public void MarshalCompany(string filename)
{
Company po = BuildCompany ();
System.Runtime.Serialization.DataContractSerializer serializer = new DataContractSerializer (po.GetType ());
using (FileStream stream = File.Create (filename))
{
serializer.WriteObject(stream, po);
}
}
And I happen to be getting this output...
<Company xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/XPathTest" z:Id="i1">
<employees>
<Person i:type="Manager" z:Id="i2">
<SSNum>1337</SSNum>
<manager i:nil="true"/>
<name>Jane Doe</name>
<employees>
<Person i:type="Employee" z:Id="i3">
<SSNum>8008132</SSNum>
<manager i:type="Manager" z:Ref="i2"/>
<name>John Smith</name>
</Person>
<Person i:type="Employee" z:Id="i4">
<SSNum>1138</SSNum>
<manager i:type="Manager" z:Ref="i2"/>
<name>Anne Jones</name>
</Person>
</employees>
</Person>
<Person i:type="Employee" z:Ref="i3"/>
<Person i:type="Employee" z:Ref="i4"/>
<Person i:type="Manager" z:Id="i5">
<SSNum>314</SSNum>
<manager i:nil="true"/>
<name>Boss Hog</name>
<employees>
<Person i:type="Manager" z:Ref="i2"/>
</employees>
</Person>
</employees>
</Company>
Why are the [DataMember(EmitDefaultValue=false)] attributes not working?
The output shows things like
<manager i:nil="true"/>
which I do not want for reasons of size, and compatibility with what jaxb outputs in the even that something is null (no element).
All the stuff I've read on MSDN and even other stackoverflow pages says this should work, but it is not. I would appreciate anyone who could help me figure out why the nil elements are still present. The code posted there is working code, for me, if you have the Sharpen namespace from the "ngit" project (for Sharpen.AList, which is just a wrapper for Collections.List). I've been doing a lot of java/c# lately.
The reason I'm interested in this is because I have a large large huge amount of data which is basically some java classes serialized with Jaxb 2.2.7, which I send over TCP to a C# client which has analogous classes of the in java. The goal is to unmarshal that data right into the c# classes. Unfortunately, I could not find a method in jaxb of naturally creating a universal reference tracker per object serialized, and had to manually write an #XmlAdapter for each class I wanted tracked and referred to. It seems that DataContract does this naturally and quite easily. However, the JAXB doesn't put references to null elements in the output xml, and I'd like to make sure that the DataContract stuff mimics the same behavior.
Thank you.

Apparently I am running into this bug, as I am using Monodevelop 3.0.3.2.
https://bugzilla.xamarin.com/show_bug.cgi?id=7999
Who would've guessed. I tested it in VC# 2012 and it works fine. Fails in Monodevelop.

Related

Unit testing XML deserialisation

I am new to unit testing and I am wondering what would be the best practices for unit testing xml deserialisation.
Consider the following xml:
<people>
<person id="1">
<name>Joe</name>
<age>28</age>
</person>
<person id="2">
<name>Jack</name>
<age>38</age>
</person>
</people>
And the following model class for the people:
[XmlRoot(ElementName ="people")]
public class People
{
public People() { PeopleList = new List<Person>(); }
[XmlElement("person")]
public List<Person> PeopleList { get; set; }
}
public class Person
{
[XmlAttribute("id")]
public int id { get; set; }
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("age")]
public int Age { get; set; }
}
I deserialize the xml using:
public List<Person> GetListOfPeople()
{
People plist = new People();
string content;
using (StreamReader sr = new StreamReader(manager.Open("People.xml")))
{
var serializer = new XmlSerializer(typeof(People));
plist = (People)serializer.Deserialize(sr);
}
return plist.PeopleList;
}
What would be the best methods to unit test the GetListOfPeople method above?
If you can change your method to take an xml file as an input parameter, you can have a sample xml file created and added to your test project. Since you know the values of your xml file, you can start comparing the values directly.
Considering you'll use the sample file you provided in your test, you can verify like this:
var persons = x.GetListOfPeople("sample.xml");
Assert.AreEqual("Joe", persons[0].Name);
Assert.AreEqual(38, persons[1].Age);
If the xml file is coming to your code from some source and you think it couldn't be following your xml schema all the time, then probably you can create some sample xml files again which violate your schema and prepare tests to call your method which should throw some exception if schema is not correct.
Hope this helps.

Reading large XML files with C#

I would like to know how can I read a XML file from my desktop and put it into a string?
Here is my XML:
<smallusers>
<user id="1">
<name>John</name>
<motto>I am john, who are you?</motto>
</user>
<user id="2">
<name>Peter</name>
<motto>Hello everyone!</motto>
</user>
</smallusers>
<bigusers>
<user id="3">
<name>Barry</name>
<motto>Earth is awesome</motto>
</user>
</bigusers>
I want to store each user, but still detect if their small or big, is there a way to do this?
Before you downrate this, you might want to check google because I did research, but found nothing.
"Before you downrate this, you might want to check google because I
did research, but found nothing"
You found nothing because you don't know what you are searching for, also your XML is invalid, you need to enclose it in a rootElement. Then the first thing you need to do is read this file from the desktop if it exists.
You can check the size if you wish at that time and determine if this is "too large" even though it doesn't really matter. I highly doubt your XML file will be 5+ GB in size. If it is then you need an alternative, no single object in a .Net program may be over 2GB, the best you could do is 1,073,741,823 on a 64bit machine.
For very large XML files, anything above 1.0 GB, combine XmlReader and LINQ as stated by Jon Skeet here:
If your document is particularly huge, you can combine XmlReader and
LINQ to XML by creating an XElement from an XmlReader for each of your
"outer" elements in a streaming manner: this lets you do most of the
conversion work in LINQ to XML, but still only need a small portion of
the document in memory at any one time.
For small XML files, anything 1.0 GB or lower stick to the DOM as shown below.
With that said, what you need is to learn what Serialization and Deserialization mean.
Serialize convert an object instance to an XML document.
Deserialize convert an XML document into an object instance.
Instead of XML you can also use JSON, binary, etc.
In your case this is what can be done to Deserialize this XML document back into an Object in order for you to use in your code.
First fix up the XML and give it a Root.
<?xml version="1.0" encoding="UTF-8"?>
<DataRoot>
<smallusers>
<user id="1">
<name>John</name>
<motto>I am john, who are you?</motto>
</user>
<user id="2">
<name>Peter</name>
<motto>Hello everyone!</motto>
</user>
</smallusers>
<bigusers>
<user id="3">
<name>Barry</name>
<motto>Earth is awesome</motto>
</user>
</bigusers>
</DataRoot>
Then create the root class in C#, you may generate this directly in Visual Studio 2012+ by copying your XML and going to Edit - Paste Special, but I like to use: XML to C# Class Generator
Here is what your code would look like after you generate the C# Root Class for your XML, hope it helps you understand it better.
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
public class Program
{
[XmlRoot(ElementName = "user")]
public class User
{
[XmlElement(ElementName = "name")]
public string Name { get; set; }
[XmlElement(ElementName = "motto")]
public string Motto { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
}
[XmlRoot(ElementName = "smallusers")]
public class Smallusers
{
[XmlElement(ElementName = "user")]
public List<User> User { get; set; }
}
[XmlRoot(ElementName = "bigusers")]
public class Bigusers
{
[XmlElement(ElementName = "user")]
public User User { get; set; }
}
[XmlRoot(ElementName = "DataRoot")]
public class DataRoot
{
[XmlElement(ElementName = "smallusers")]
public Smallusers Smallusers { get; set; }
[XmlElement(ElementName = "bigusers")]
public Bigusers Bigusers { get; set; }
}
static void Main(string[] args)
{
string testXMLData = #"<DataRoot><smallusers><user id=""1""><name>John</name><motto>I am john, who are you?</motto></user><user id=""2""><name>Peter</name><motto>Hello everyone!</motto></user></smallusers><bigusers><user id=""3""><name>Barry</name><motto>Earth is awesome</motto></user></bigusers></DataRoot>";
var fileXmlData = File.ReadAllText(#"C:\XMLFile.xml");
var deserializedObject = DeserializeFromXML(fileXmlData);
var serializedToXML = SerializeToXml(deserializedObject);
//I want to store each user, but still detect if their small or big, is there a way to do this?
foreach (var smallUser in deserializedObject.Smallusers.User)
{
//Iterating your collection of Small users?
//Do what you need here with `smalluser`.
var name = smallUser.Name; //Example...
}
Console.WriteLine(serializedToXML);
Console.ReadKey();
}
public static string SerializeToXml(DataRoot DataObject)
{
var xsSubmit = new XmlSerializer(typeof(DataRoot));
using (var sw = new StringWriter())
{
using (var writer = XmlWriter.Create(sw))
{
xsSubmit.Serialize(writer, DataObject);
var data = sw.ToString();
writer.Flush();
writer.Close();
sw.Flush();
sw.Close();
return data;
}
}
}
public static DataRoot DeserializeFromXML(string xml)
{
var xsExpirations = new XmlSerializer(typeof(DataRoot));
DataRoot rootDataObj = null;
using (TextReader reader = new StringReader(xml))
{
rootDataObj = (DataRoot)xsExpirations.Deserialize(reader);
reader.Close();
}
return rootDataObj;
}
}
}

.net / wcf: deserialization of an xml with xsi:type

I have a little wcf server wich receive POST request with a XML (note that I HAVE NO CONTROL OVER THE XML). I can deserialize it with no problem unless it has a xsi:type = "something" attribute.
When I try to serialize my class, everthing works (even the xsi:type attribute).
The XML:
<?xml version="1.0" encoding="utf-8"?>
<Node1 Att1="" Att2=""
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="SOMETHING"
xmlns="http://www.CIP4.org/JDFSchema_1_1"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Node2/>
</Node1>
The server throw a bad request (400) when I send this XML, but if I remove the "xsi:type="SOMETHING"", everthing works.
Here is what the server send when i ask for the serialised class:
<?xml version="1.0" encoding="utf-8"?>
<Node1 Att1="" Att2="" xsi:type="SOMETHING"
xmlns="http://www.CIP4.org/JDFSchema_1_1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Node2/>
</Node1>
If the serialization works well, why the deserialization don't?
Here is my class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Xml.Serialization;
using System.Xml.Schema;
namespace ConsoleApplicationTest.dom
{
[Serializable()]
[XmlRoot(ElementName = "Node1", Namespace = "http://www.CIP4.org/JDFSchema_1_1")]
public class Test
{
//attributes
[XmlAttribute("Att1")]
public string Att1 = "";
[XmlAttribute("Att2")]
public string Att2 = "";
[XmlAttribute("type", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string Type="typetypetype";
[XmlElement("Node2")]
public string node2 = "";
}
}
Please help me :(
Try this class:
[Serializable()]
[XmlRoot(ElementName = "Node1", Namespace = "http://www.CIP4.org/JDFSchema_1_1")]
public class SOMETHING
{
//attributes
[XmlAttribute("Att1")]
public string Att1 = "";
[XmlAttribute("Att2")]
public string Att2 = "";
[XmlElement("Node2")]
public string node2 = "";
}
My code works fine
XmlSerializer ser = new XmlSerializer(typeof(SOMETHING));
SOMETHING t = (SOMETHING)ser.Deserialize(new System.IO.StringReader(textBox1.Text));

ASP.NET MVC 2: Deserialize SPROC Response

I need to ask a general question. I don't have the code in front of me because I'm writing this on my iPhone.
I have a Class that represents a certain XML schema. I have a SPROC that returns this XML. What I need to do is deserialize the XML to this Class.
XML:
<xml>
<person>
<firstName>Bob</firstName>
<lastName>Robby</lastName>
</person>
</xml>
I need to deserialize this XML into the custom Person Class so I can loop through this Model and spit it out in the View. I'm sure there's some kind of casting involved, I just don't know how to do it.
My Solution:
public class Program {
public static void Main(string[] args) {
string xml = #"<xml><person><firstName>Bob</firstName><lastName>Robby</lastName></person></xml>";
var doc = XElement.Parse(xml);
var person = (from x in doc.Elements("person") select x).FirstOrDefault();
XmlSerializer serializer = new XmlSerializer(typeof(Person));
var sr = new StringReader(person.ToString());
// Use the Deserialize method to restore the object's state.
var myPerson = (Person)serializer.Deserialize(sr);
}
}
And Class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace ConsoleApplication3 {
[XmlRoot("person")]
public class Person {
[XmlElement("firstName")]
public string FirstName { get; set; }
[XmlElement("lastName")]
public string LastName { get; set; }
}
}
in linq it would be something like this
XDocument xmlFile = XDocument.Parse(yourXml)
var people = (from x in xmlFile.Descendants("person")
select new Person(){
firstname = (string)x.Element("firstname").Value,
lastname = (string)x.Element("lastname").Value
});

How does XML Serialization know where to put the attribute?

I am trying to figure out how to do XML serialization.
This is how I want my XML document too look like in the end
<course>
<name></name>
<backgroundColor></backgroundColor>
<fontColor></fontColor>
<sharingKey></sharingKey>
<task id="">
<taskName></taskName>
<description></description>
</task>
<task id="">
<taskName></taskName>
<description></description>
</task>
</course>
So far mine looks like
<?xml version="1.0" encoding="utf-8"?>
<Course xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<name>name</name>
<backgroundColor>test</backgroundColor>
<fontColor>test2</fontColor>
<sharingKey>9324bfab-6cc7-49e5-84f7-56130b8dc099</sharingKey>
<task id="first Task" />
<task id="Second task" />
</Course>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
[XmlRoot("Course")]
public class MyWrapper
{
public MyWrapper()
{
TaskList = new List<Tasks>();
}
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("backgroundColor")]
public string BackgroundColor { get; set; }
[XmlElement("fontColor")]
public string FontColor { get; set; }
[XmlElement("sharingKey")]
public Guid SharingKey { get; set; }
[XmlElement("task")]
public List<Tasks> TaskList { get; set; }
}
public class Tasks
{
[XmlAttribute("id")]
public string Id { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Tasks task = new Tasks();
task.Id = "first Task";
Tasks task2 = new Tasks();
task2.Id = "Second task";
MyWrapper wrap = new MyWrapper();
wrap.BackgroundColor = "test";
wrap.FontColor = "test2";
wrap.Name = "name";
wrap.SharingKey = Guid.NewGuid();
wrap.TaskList.Add(task);
wrap.TaskList.Add(task2);
SerializeToXML(wrap);
}
static public void SerializeToXML(MyWrapper list)
{
XmlSerializer serializer = new XmlSerializer(typeof(MyWrapper));
TextWriter textWriter = new StreamWriter(#"C:\New folder\test.xml");
serializer.Serialize(textWriter, list);
textWriter.Close();
}
}
}
So my question is with the "id" for the task.
All I have is another class with this in it
[XmlAttribute("id")]
public string Id { get; set; }
How did it know to put this attribute in the task tag?
What happens if I wanted to have another property
[XmlElement()]
public string TaskName {get; set;}
Say I wanted to have an attribute with this element how would I make sure that the attribute would be with TaskName not with Task?
[XmlElement("task")]
public List<Tasks> TaskList { get; set; }
This part of your code told it to serialize every Tasks object in an element called "task". The serializer looks at the properties on the Tasks class and finds the Id property which you marked with [XmlAttribute("id")] so it gets serialized as an attribute to the "task" element for the object.
I'm not sure I understand your second question. You cannot add an attribute to the element because its type is a string. Instead you would have to create a new class to wrap the concept of a task name which would have a name property and whatever other properties you wanted to add to it.
Basically: XML elements can have two kinds of properties, attributes and elements. You defined an XML element task and an attribute on task called id, thus the serializer adds the attribute to task.
Now, suppose you want to add elements to reside within task -- this is also okay, as I said, XML elements can contain other elements, or have attributes. Simply define any elements you want to contain within task...within task.
If you want to have an attribute attached to a different element, you need to create a new XML element (here that corresponds to a class) and literally set it as an attribute type using the [XmlAttribute("id")] syntax.
Perhaps there's a disconnect for you here -- when you define the simplest form of element, we can call that a simpleType, and it can have values that are Strings or Integers or any kind of relatively primitive type (dates are valid, too). But if you want that same element to also have attributes, it suddenly needs to become a complexType, since it has complexContent -- it may contains both simple content, and properties, like, say, an attribute.
Take a look at how to write XML Schemas - w3schools have some excellent tutorials - I think that you'll gain a much better understanding of this whole mix of simple and complex content. Effectively, by defining an XML serialization for your classes you are also defining an XML schema; and you can in fact compile your code into such a schema. Understanding to construct the schemas will let you understand how to construct your code to generate the appropriate schemas, and additionally, understand your serialization output.

Categories