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
});
Related
I'm trying to integrate a WebDAV client into some bigger tool suite to be able to create events/notifications from my software in the users existing calendar. My project is a WPF application written in c#.
I have set up a calendar with a WebDAV interface/api available and now I try to read the ctag property of the calendar. When sending the PROPFIND http request
<?xml version="1.0" encoding="utf-8"?>
<d:propfind xmlns:d=\"DAV:\" xmlns:cs=\"http://calendarserver.org/ns/\">
<d:prop>
<d:displayname/>
<cs:getctag/>
</d:prop>
</d:propfind>
I receive a http response with the following content:
<?xml version="1.0" encoding="utf-8"?>
<d:multistatus xmlns:d="DAV:" xmlns:nmm="http://all-inkl.com/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/">
<d:response>
<d:href>/calendars/cal0015dc8/1/</d:href>
<d:propstat>
<d:prop>
<d:displayname>My Calendar Name</d:displayname>
<cs:getctag>0</cs:getctag>
</d:prop>
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>
</d:response>
</d:multistatus>
I know that the namespaces might look a little suspicious, some with and some without a trailing slash /, namespace d even with a trailing colon :, but this is exactly what I get from the server. If I for example change the namespace xmlns:d="DAV:" in my request to xmlns:d="DAV", I get a response status 500: InternalServerError, so I took the namespace declarations exactly as they are in the response.
Now, I want to get the value from the cs:getctag node. Problem is, everything I tried always returns null when navigating through the xml structure.
For clarification: response.Content.ReadAsStringAsync().Result returns the afore mentioned response xml string.
First try: Load response in a XmlDocument and access the subnodes by namespace/name combination:
using System.Xml;
XmlDocument doc = new XmlDocument();
XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(doc.NameTable);
xmlNamespaceManager.AddNamespace("d", "DAV:");
xmlNamespaceManager.AddNamespace("nmm", "http://all-inkl.com/ns");
xmlNamespaceManager.AddNamespace("cal", "urn:ietf:params:xml:ns:caldav");
xmlNamespaceManager.AddNamespace("cs", "http://calendarserver.org/ns/");
doc.LoadXml(response.Content.ReadAsStringAsync().Result);
XmlNode root = doc.DocumentElement;
XmlNode ctagNode = root["response", "d"]["propstat", "d"]["prop", "d"]["getctag", "cs"];
ctag = Convert.ToInt64(ctagNode.InnerText);
The node root is correctly set to element <d:multistatus>, but in the next line, where ctagNode should get selected, the code throws an exception:
System.NullReferenceException: Object reference not set to an instance of an object.
Second Try: Get the node with a XPath selection
using System.IO;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
XmlReader xmlReader = XmlReader.Create(new StringReader(response.Content.ReadAsStringAsync().Result));
XmlNamespaceManager nsManager = new XmlNamespaceManager(xmlReader.NameTable);
nsManager.AddNamespace("d", "DAV:");
nsManager.AddNamespace("nmm", "http://all-inkl.com/ns");
nsManager.AddNamespace("cal", "urn:ietf:params:xml:ns:caldav");
nsManager.AddNamespace("cs", "http://calendarserver.org/ns/");
XDocument myXDocument = XDocument.Load(xmlReader);
XPathNavigator myNavigator = myXDocument.CreateNavigator();
string query = "//d:multistatus/d:response/d:propstat/d:prop/cs:getctag";
XPathNavigator ctagElement = myNavigator.SelectSingleNode(query, nsManager);
ctag = ctagElement.ValueAsLong;
After the execution of XPathNavigator ctagElement = myNavigator.SelectSingleNode(query, nsManager);, the object ctagElement is still null.
Can someone point out what I'm doing wrong in either case (1-Bare xml, 2-XPath) and how to do it right?
I would appreciate answers that help me solve this problem and that generally help me understand how to correctly navigate in xml data. You're welcome to also link to a comprehensive documentation or tutorial.
As #GSerg pointed out in his comment to my question, I was indeed not using the XmlNamespaceManager I have created in my First Try solution.
As it turns out, in my code example was just one small mistake:
using System.Xml;
XmlDocument doc = new XmlDocument();
XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(doc.NameTable);
xmlNamespaceManager.AddNamespace("d", "DAV:");
xmlNamespaceManager.AddNamespace("nmm", "http://all-inkl.com/ns");
xmlNamespaceManager.AddNamespace("cal", "urn:ietf:params:xml:ns:caldav");
xmlNamespaceManager.AddNamespace("cs", "http://calendarserver.org/ns/");
doc.LoadXml(response.Content.ReadAsStringAsync().Result);
XmlNode root = doc.DocumentElement;
// THIS LINE WAS WRONG
XmlNode ctagNode = root["response", "d"]
["propstat", "d"]
["prop", "d"]
["getctag", "cs"];
// IT SHOULD LOOK LIKE THIS:
XmlNode ctagNode = root["response", xmlNamespaceManager.LookupNamespace("d")]
["propstat", xmlNamespaceManager.LookupNamespace("d")]
["prop", xmlNamespaceManager.LookupNamespace("d")]
["getctag", xmlNamespaceManager.LookupNamespace("cs")];
ctag = Convert.ToInt64(ctagNode.InnerText);
Looks like the syntax
XmlNode childNode = parentNode["nameOfChildNode", "namespaceOfChildNode"]
requires the full namespace, not the namespace prefix.
As for my Second Try, I already used the namespace manager and the code worked after a VisualStudio restart and solution rebuild. No code change required.
Thank you #GSerg :-)
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication186
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
string xml = File.ReadAllText(FILENAME);
StringReader sReader = new StringReader(xml);
XmlReader xReader = XmlReader.Create(sReader);
XmlSerializer serializaer = new XmlSerializer(typeof(MultiStatus));
MultiStatus multiStatus = (MultiStatus)serializaer.Deserialize(xReader);
}
}
[XmlRoot(ElementName = "multistatus", Namespace = "DAV:")]
public class MultiStatus
{
[XmlElement(Namespace = "DAV:")]
public Response response { get; set; }
}
public class Response
{
[XmlElement(Namespace = "DAV:")]
public string href { get; set; }
[XmlElement(ElementName = "propstat", Namespace = "DAV:")]
public Propstat propstat { get; set; }
}
public class Propstat
{
[XmlElement(ElementName = "prop", Namespace = "DAV:")]
public Prop prop { get; set; }
[XmlElement(ElementName = "status", Namespace = "DAV:")]
public string status { get; set; }
}
public class Prop
{
[XmlElement(Namespace = "DAV:")]
public string displayname { get; set; }
[XmlElement(Namespace = "http://calendarserver.org/ns/")]
public string getctag { get; set; }
}
}
I want to save a list of dates in an xml, I want to use dataset to achieve the task, I do the same to a database using Entity Framework. This allows me to access the dates using event.eventDates.start
but in the dataset I cannot achieve it.
public class Event
{
[Key]
public string id { get; set; }
public virtual ICollection<Date> eventDates { get; set; }
}
Date class
public class Date
{
public DateTime start { get; set; }
public DateTime end { get; set; }
}
When using entity framework I can access the eventDates Object using event.eventDates.start
I mapped the data from the sql database in the dataset builder the relations look like this
I want the xml file to be in this format
<?xml version="1.0" standalone="yes"?>
<db xmlns="http://tempuri.org/LocalDB.xsd">
<Event>
<id>ID</id>
<eventdates>
<date>
<startdate></startdate>
<enddate></enddate>
<date>
<date>
<startdate></startdate>
<enddate></enddate>
<date>
</eventdates>
</Event>
</db>
Is there any way to achieve that using datasets?
I'm new to C# any help would be appreciated
This code works, based on generate xml files based on my c# classes. I would rename your classes to something else than "Event" and "Date". These names are too generic and are usually protected by the system. ICollection is not serializable. Read XML serialization of interface property. You can use DTO or you can change the collection type (i.e. with List<>) and with XML serialization attributes avoid circular references and/or disable lazy load (i.e. use eagerly load using Include method) or the risk is that you serialize the whole database.
Program.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
namespace EFToXml
{
public class MyEvent
{
public string Id { get; set; }
public virtual List<MyDate> EventDates { get; set; }
}
public class MyDate
{
public DateTime Start { get; set; }
public DateTime End { get; set; }
}
public class Program
{
public static void Main(string[] args)
{
var myDate1 = new MyDate
{
Start = DateTime.Now,
End = DateTime.Now.AddDays(1)
};
var eventDates = new List<MyDate> { myDate1 };
var myEvent = new MyEvent
{
Id = "1",
EventDates = eventDates
};
XmlSerializer serializer = new XmlSerializer(typeof(MyEvent));
serializer.Serialize(File.Create(#"C:\Users\<UserName>\Source\Repos\myEvents.xml"), myEvent);
}
}
}
myEvents.xml:
<?xml version="1.0"?>
<MyEvent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Id>1</Id>
<EventDates>
<MyDate>
<Start>2019-11-24T21:52:04.5032671+01:00</Start>
<End>2019-11-25T21:52:04.5194026+01:00</End>
</MyDate>
</EventDates>
</MyEvent>
If i understand correctly, you just want to reproduce EntityFramework's behavior using DataSets.
You can achieve exactly same thing simply creating DataSet (using integrated VisualStudio designer) with proper tables, fields and relations:
later on you can access your data using code like this:
var ds = new DataSet1();
var ue = ds.UserEvents.FirstOrDefault();
var ued = ue.GetChildRows("FK_UserEvents_EventDates")
.Cast<DataSet1.EventDatesRow>();
var date = ued.FirstOrDefault().Date;
Next thing to do is serialization - it's quite easy:
Serialization example
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.
I am dealing with an API that occasionally changes the namespaces on the XML that I receive. The XML structure remains the same. I need to deserialize the XML into a strongly typed model.
How do I perform the deserialization regardless of what namespace is on the XML?
I was using a model like this:
[Serializable, XmlRoot(ElementName = "TestModel", Namespace = "http://schemas.datacontract.org/UnknownNamespace1")]
public class TestModel
{
public TestModel()
{
TestElements = new List<TestModelChildren>();
}
[XmlElement("TestModelChildren")]
public List<TestModelChildren> TestElements { get; set; }
}
I try to deserialize some XML into that model with code like this:
public TestModel DeserializeIt(XDocument xDoc)
{
TestModel result;
var serializer = new XmlSerializer(typeof(TestModel));
using(var sr = new StringReader(xDoc.ToString()))
{
result = (TestModel)serializer.Deserialize(sr);
}
return result;
}
My problem is that every so often, the namespace on the XML I am receiving changes. I might start getting XML like this:
<TestModel xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/UnknownNamespace2">
<TestModelChildren>
...
</TestModelChildren>
</TestModel>
I don't want to have to recompile my code every time this namespace change happens. How do I deal with it?
I was able to solve the problem by passing the namespace in to the XmlSerializer as a default namespace. I can pluck the namespace off of the XDocument to do this.
My new model would look like this without a namespace specified:
[Serializable, XmlRoot(ElementName = "TestModel")]
public class TestModel
{
public TestModel()
{
TestElements = new List<TestModelChildren>();
}
[XmlElement("TestModelChildren")]
public List<TestModelChildren> TestElements { get; set; }
}
My code to deserialize the XML would look like this:
public TestModel DeserializeIt(XDocument xDoc)
{
TestModel result;
var serializer = new XmlSerializer(typeof(TestModel), xDoc.Root.Name.Namespace.ToString());
using(var sr = new StringReader(xDoc.ToString()))
{
result = (TestModel)serializer.Deserialize(sr);
}
return result;
}
This has been working for me.
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.