I am facing a problem in deserializing xml into a class object. The class has a slightly different structure than the xml, so am unable to deserialize it. Following is the main code
public class Customer
{
[XmlElement(ElementName = "CustomerName")]
public string Name { get; set; }
}
public class XmlCheck
{
[XmlElement(ElementName = "Customer")]
public Customer Customer { get; set; }
public List<Customer> CustomersList { get; set; }
}
class Program
{
static string xml = #"<?xml version=""1.0"" ?>
<XmlCheck>
<Customer>
<CustomerName>Omer</CustomerName>
</Customer>
<Customer>
<CustomerName>Ali</CustomerName>
</Customer>
</XmlCheck>";
static void Main(string[] args)
{
var serializer = new XmlSerializer(typeof(XmlCheck), new XmlRootAttribute("XmlCheck"));
using (var stringReader = new StringReader(xml))
using (var reader = XmlReader.Create(stringReader))
{
var xmlResult = (XmlCheck)serializer.Deserialize(reader);
xmlResult.CustomersList.Add(xmlResult.Customer);
Console.WriteLine(xmlResult.Customer.Name);
}
}
Is there any way, to deserialize the xml into the customers list without having to insert that node inside the xml? Currently this only deserializes the first customer node that has name as 'Omer' and it adds that to the list.
I know how to accomplish the above by writing a custom xml reader, but need to use xml deserialization for this. However, if this isn't possible using xml deserialization, any way to achieve this using any customer (s/de)erializer?
Please try this:
public class Customer
{
[XmlElement(ElementName = "CustomerName")]
public string Name { get; set; }
}
[XmlRoot("XmlCheck")]
public class XmlCheck
{
[XmlElement(ElementName = "Customer")]
public List<Customer> CustomersList { get; set; }
}
class Program
{
static string xml = #"<?xml version=""1.0"" ?>
<XmlCheck>
<Customer>
<CustomerName>Omer</CustomerName>
</Customer>
<Customer>
<CustomerName>Ali</CustomerName>
</Customer>
</XmlCheck>";
static void Main(string[] args)
{
var serializer = new XmlSerializer(typeof(XmlCheck), new XmlRootAttribute("XmlCheck"));
using (var stringReader = new StringReader(xml))
using (var reader = XmlReader.Create(stringReader))
{
var xmlResult = (XmlCheck)serializer.Deserialize(reader);
//xmlResult.CustomersList.Add(xmlResult.Customer);
foreach(var c in xmlResult.CustomersList)
{
Console.WriteLine(c.Name);
}
}
}
}
I got it from: Is it possible to deserialize XML into List<T>?
Related
I have below XML stored in a string RequestPassengerXML. How can I deserialize it so that I can get the tags information which I can assign it to my class members.
<?xml version="1.0" encoding="utf-16"?>
<Passenger xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>John Vaz</Name>
<Email>john#gmail.com</Email>
<cardNumber>CB2390VT</cardNumber>
</Passenger>
I have created a class with below details
public class PassengerDetails
{
public PassengerDetails();
public string Name { get; set; }
public string Email { get; set; }
public string CardNumber{ get; set; }
}
public static XElement ToXElement<T>(this object obj)
{
using (var memoryStream = new MemoryStream())
{
using (TextWriter streamWriter = new StreamWriter(memoryStream))
{
var xmlSerializer = new XmlSerializer(typeof(T));
xmlSerializer.Serialize(streamWriter, obj);
return XElement.Parse(Encoding.ASCII.GetString(memoryStream.ToArray()));
}
}
}
public static T FromXElement<T>(this XElement xElement)
{
var xmlSerializer = new XmlSerializer(typeof(T));
return (T)xmlSerializer.Deserialize(xElement.CreateReader());
}
Usage:
XElement element = PassengerDetails.ToXElement<PassengerDetails>();
var newMyClass = element.FromXElement<PassengerDetails>();
I'm currently working on an ASP.NET MVC 4.6 application using SQL Server 2014 as a data storage.
I need to parse a XML document from an URL, transform the XML into an object and store it into the database using Entity Framework 6.
I need to parse the XML from an URL like this:
http: //api.myserver.com/notes.xml
My XML looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<notes>
<note>
<to>Tove</to>
<from>Jane</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
<note>
<to>Doe</to>
<from>John</from>
<heading>Meeting</heading>
<body>Hello Monday!</body>
</note>
<notes>
My model class note looks like this:
public class Note
{
public string To { get; set; }
public string From { get; set; }
public string Heading { get; set; }
public string Body { get; set; }
}
My current test implementation looks like this, BUT I'm not so happy about the parsing of the XML:
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
String url = "https://www.w3schools.com/xml/note.xml";
XmlDocument doc = new XmlDocument();
doc.Load(url);
XmlElement root = doc.DocumentElement;
StringBuilder sb = new StringBuilder();
sb.Append("<note>");
foreach (XmlNode item in root)
{
sb.Append(item.OuterXml);
}
sb.Append("</note>");
var result = Deserialize<Note>(sb.ToString());
// further processing of result using EF 6
Console.ReadKey();
}
public static T Deserialize<T>(string xmlText)
{
try
{
var stringReader = new StringReader(xmlText);
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
catch (Exception ex)
{
throw;
}
}
}
[XmlRoot(ElementName = "note", Namespace = "")]
public class Note
{
[XmlElement("to")]
public string To { get; set; }
[XmlElement("from")]
public string From { get; set; }
[XmlElement("heading")]
public string Heading { get; set; }
[XmlElement("body")]
public string Body { get; set; }
}
}
As far as I noticed, .NET provides several approaches on how to parse XML:
XmlReader Class
XPath
XDocument
DataSet
LINQ
I was wondering, what would you use and how would u solve the parsing of the XML document from an URL using .NET and converting into an object to store it in a SQL Server DB?
Thanks for your help and consideration!!
This might help you, a serialize method and a Deserialize method.
public static string Serialize<T>(T dataToSerialize)
{
try
{
var stringwriter = new ISOEncodingStringWriter();
var serializer = new XmlSerializer(typeof(T));
var xns = new XmlSerializerNamespaces();
xns.Add(string.Empty, string.Empty);
serializer.Serialize(stringwriter, dataToSerialize, xns);
return stringwriter.ToString();
}
catch
{
throw;
}
}
public static T Deserialize<T>(string xmlText)
{
try
{
var stringReader = new System.IO.StringReader(xmlText);
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
catch
{
throw;
}
}
You use the method like this:
Note result = Deserialize<Note>(xmlstring);
Note note = new Note(){...};
string convertedToXML = Serialize<Note>(note);
Just remember that you need to add some data to your note class so it can actually serialize the data:
[XmlRoot(ElementName = "root", Namespace = "")]
public class Note
{
[XmlElementAttribute("To")]
public string To { get; set; }
[XmlElementAttribute("From")]
public string From { get; set; }
[XmlElementAttribute("Heading")]
public string Heading { get; set; }
[XmlElementAttribute("Body")]
public string Body { get; set; }
}
I hope it helps :)
You can use LINQ to XML to traverse the nodes of your documents and either EF or ADO.NET to store them in the DB.
You can start with this helpful tutorial: http://www.c-sharpcorner.com/UploadFile/de41d6/learning-linq-made-easy-tutorial-1/
I'm trying to convert the XML data to an object list, but it throws an error.
XML
<?xml version="1.0" encoding="utf-8" ?>
<Servers>
<Server>
<ServerName>STAGING</ServerName>
<ServerIP>XXX.XXX.XX.X</ServerIP>
</Server>
</Servers>
C#
public class ServerDetails
{
public string ServerName { get; set; }
public string ServerIP { get; set; }
}
private void GetXMLData()
{
XmlSerializer serializer = new XmlSerializer(typeof(List<ServerDetails>));
using (FileStream stream = File.OpenRead("D:\\Resource.xml"))
{
List<ServerDetails> list = (List<ServerDetails>)serializer.Deserialize(stream);
//Exception here
}
}
ERROR
Inner Exception : <Servers xmlns=''> was not expected.
There is an error in XML document (2,2)
I tried adding the [Serializabe] and [XMLElement] attributes to the class,
and also xmlns="http://www.example.com/xsd/ServerDetails" in the XML
but that did not help.
You have ServerDetails as your class name and in the xml the tag name is different, Try something like this.
public class ServerDetails
{
public string ServerName { get; set; }
public string ServerIP { get; set; }
}
public class ServerList
{
[XmlArray("Servers")]
[XmlArrayItem("Server", Type = typeof(ServerDetails))]
public ServerDetails[] Servers { get;set;}
}
private void GetXMLData()
{
XmlSerializer serializer = new XmlSerializer(typeof(ServerList));
using (FileStream stream = File.OpenRead("D:\\Resource.xml"))
{
var list = (ServerList)serializer.Deserialize(stream);
//Exception here
}
}
I used to use XmlSerializer a lot, but I totally stopped using it because you are forced to create your object structure fitting the xml structure. That makes it hard to maintain. Also XmlSerializer has some serious memory leaks.
If you don't mind, I would suggest to switch to XElement
public IEnumerable<ServerDetails> GetServers(string file)
{
using (var stream = File.Open(file, FileMode.Open, FileAccess.Read))
return GetServers(stream);
}
public IEnumerable<ServerDetails> GetServers(Stream stream)
{
var root = XElement.Load(stream);
return GetServers(root);
}
public IEnumerable<ServerDetails> GetServers(XElement root)
{
foreach (var server in root.Elements("Server"))
{
yield return new ServerDetails
{
ServerName = (string)server.Element("ServerName"),
ServerIP = (string)server.Element("ServerIP"),
};
}
}
Please note that you have to reference System.Xml.Linq
For your convenience here is a test case.
[TestMethod]
public void CanReadServers()
{
var xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" + #"
<Servers>
<Server>
<ServerName>STAGING</ServerName>
<ServerIP>XXX.XXX.XX.X</ServerIP>
</Server>
</Servers>";
IEnumerable<ServerDetails> servers;
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
servers = GetServers(stream).ToList();
Assert.AreEqual(1, servers.Count());
Assert.AreEqual("STAGING", servers.ElementAt(0).ServerName);
Assert.AreEqual("XXX.XXX.XX.X", servers.ElementAt(0).ServerIP);
}
Use XmlElement Notation to specify the element name.
public class Servers
{
[XmlElement("Server")]
public ServerDetails[] ServersDetails { get; set; }
}
public class ServerDetails
{
public string ServerName { get; set; }
public string ServerIP { get; set; }
}
private void GetXMLData()
{
XmlSerializer serializer = new XmlSerializer(typeof(Servers));
using (FileStream stream = File.OpenRead("D:\\Resource.xml"))
{
Servers list = (Servers)serializer.Deserialize(stream);
//Exception here
}
}
So if I have 3 different files that contain extremely long and complex xml (which i've abbreviated here) but just the root element name is different (not really random but I'd rather not duplicate classes for the same xml):
File 1
<?xml version="1.0"?>
<somerootname1>
<car>
<make>honda</make>
<model>civic</model>
</car>
</somerootname1>
File 2
<?xml version="1.0"?>
<somerootname2>
<car>
<make>honda</make>
<model>civic</model>
</car>
</somerootname2>
File 3
<?xml version="1.0"?>
<somerootname3>
<car>
<make>honda</make>
<model>civic</model>
</car>
</somerootname3>
Because XmlRootAttribute has AllowMultiple=false I can't create a single class to represent the previous xml.
Duplicate 'XmlRootAttribute' attribute
[XmlRoot(ElementName="somerootname1")]
[XmlRoot(ElementName="somerootname2")]
public class Root
{
public Automobile Car { get; set; }
}
public class Automobile
{
public string Make { get; set; }
public string Model { get; set; }
}
I thought I might be able to be sneaky and grab just the element I need and deserialize it:
var xDoc = XDocument.Parse("myfile.xml");
var xCar = xDoc.Root.Descendants()
.FirstOrDefault(d => d.Name.LocalName.Equals("car"))
var serializer = new XmlSerializer(typeof(Automobile))
using (var reader = xml.CreateReader())
{
result = (Automobile)serializer.Deserialize(reader);
}
However that results in an InvalidOperationException:
There is an error in XML document (0, 0).
With an InnerException:
<car> was not expected.
It seems really hacky to either; rename the root element and then deserialize OR take the element and prefix the <?xml> tag. Any other suggestions?
You have extracted the <car> node, but you're still deserializing from the document's root node. That's why the exception suggests that it's expecting the <car> node. The following works for me:
[XmlRoot("car")]
public class Automobile
{
[XmlElement("make")]
public string Make { get; set; }
[XmlElement("model")]
public string Model { get; set; }
}
Deserialize from <car>:
var xCar = xDoc.Root.Descendants()
.FirstOrDefault(d => d.Name.LocalName.Equals("car"));
var serializer = new XmlSerializer(typeof(Automobile));
using (var reader = xCar.CreateReader())
{
var result = (Automobile)serializer.Deserialize(reader);
Console.WriteLine(result.Make);
Console.WriteLine(result.Model);
}
I did not come up with this but just tweaked it some, the original is from: Deserialize XML To Object using Dynamic
Not sure if it is exactly what you need, but it would allow you to grab the values from the xml and put them into your class. It can be adjusted for arrays or collections as well, if there were multiple cars in one xml doc.
You will need to add:
using System.Dynamic;
using System.Xml.Linq;
protected Car ParseXml(Car NewCar) {
string xml = "<?xml version=\"1.0\"?><somerootname1> <car> <make>honda</make> <model>civic</model> </car></somerootname1>";
dynamic car = DynamicXml.Parse(xml);
NewCar.Make = car.car.make;
NewCar.Model = car.car.model;
return NewCar;
}
[Serializable]
public partial class Car
{
public Car() { }
public string Make { get; set; }
public string Model { get; set; }
}
public class DynamicXml : DynamicObject
{
XElement _root;
private DynamicXml(XElement root)
{
_root = root;
}
public static DynamicXml Parse(string xmlString)
{
return new DynamicXml(XDocument.Parse(xmlString).Root);
}
public static DynamicXml Load(string filename)
{
return new DynamicXml(XDocument.Load(filename).Root);
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;
var att = _root.Attribute(binder.Name);
if (att != null)
{
result = att.Value;
return true;
}
var nodes = _root.Elements(binder.Name);
if (nodes.Count() > 1)
{
result = nodes.Select(n => new DynamicXml(n)).ToList();
return true;
}
var node = _root.Element(binder.Name);
if (node != null)
{
if (node.HasElements)
{
result = new DynamicXml(node);
}
else
{
result = node.Value;
}
return true;
}
return true;
}
}
I have a problem about deserialize an array. Becouse array elements can be of various types. You can see the example:
<?xml version="1.0" encoding="UTF-8"?><export xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://zakupki.gov.ru/oos/export/1" xmlns:oos="http://zakupki.gov.ru/oos/types/1">
<notificationZK>
... item 1 data
</notificationZK>
<notificationZK>
... item 2 data
</notificationZK>
<notificationFF>
... item 3 data
</notificationFF>
</export>
All elements extends notificationType
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationSZType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationPOType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationZKType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationEFType))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(notificationOKType))]
public partial class notificationType
{
...
So the question is how can I get the collection of notificationType elements from my XML file? I think I cant do something like
[Serializable()]
[System.Xml.Serialization.XmlRoot("export")]
public class NotificationCollection
{
[XmlArray("")] // ???? what I need write here?
[XmlArrayItem("", typeof(notificationType))] // ??? and here?
public notificationType[] notification { get; set; }
}
Regards!
ADDED-------------
So. I make this:
[Serializable()]
[System.Xml.Serialization.XmlRoot("export")]
public class NotificationCollection
{
[XmlElement("notificationSZType", Type = typeof(notificationSZType))]
[XmlElement("notificationPOType", Type = typeof(notificationPOType))]
[XmlElement("notificationZKType", Type = typeof(notificationZKType))]
[XmlElement("notificationEFType", Type = typeof(notificationEFType))]
[XmlElement("notificationOKType", Type = typeof(notificationOKType))]
public notificationType[] notification { get; set; }
}
class Program
{
static void Main(string[] args)
{
NotificationCollection collection = null;
string path = #"E:\notification.xml";
XmlSerializer serializer = new XmlSerializer(typeof(notificationType));
StreamReader reader = new StreamReader(path);
collection = (NotificationCollection) serializer.Deserialize(reader);
reader.Close();
}
}
but have System.InvalidOperationException was unhandled while serializer.Deserialize(reader);
Message=<export xmlns='http://zakupki.gov.ru/oos/export/1'> not expected.
What im doing wrong?
How about moving the type declarations into the collection?
[XmlRoot("export")]
public class NotificationCollection
{
[XmlElement("notificationZK", typeof(NotificationTypeZK))]
[XmlElement("notificationFF", typeof(NotificationTypeFF))]
public List<NotificationType> Notifications { get; set; }
}
public class NotificationType
{
}
public class NotificationTypeZK : NotificationType { }
public class NotificationTypeFF : NotificationType { }
static void Main(string[] args)
{
var data = #"<export><notificationZK /><notificationZK /><notificationFF /></export>";
var serializer = new XmlSerializer(typeof(NotificationCollection));
using (var reader = new StringReader(data))
{
var notifications = serializer.Deserialize(reader);
}
}
This should do the job
[Serializable()]
[System.Xml.Serialization.XmlRoot("export")]
public class NotificationCollection
{
[XmlElement("notificationSZType", Type = typeof(notificationSZType))]
[XmlElement("notificationPOType", Type = typeof(notificationPOType))]
[XmlElement("notificationZKType", Type = typeof(notificationZKType))]
[XmlElement("notificationEFType", Type = typeof(notificationEFType))]
[XmlElement("notificationOKType", Type = typeof(notificationOKType))]
public notificationType[] notification { get; set; }
}
This question is interesting for me as well. I wrote the simplified app to achieve what you ask for:
[Serializable]
[XmlInclude(typeof(ItemA))]
[XmlInclude(typeof(ItemB))]
public class BaseItem
{
public bool Value { get; set; }
}
[Serializable]
public class ItemA : BaseItem
{
public string Text { get; set; }
}
[Serializable]
public class ItemB : BaseItem
{
public int Number { get; set; }
}
[Serializable]
public class ItemsArray
{
public BaseItem[] Items { get; set; }
}
class Program
{
static void Main(string[] args)
{
var array = new ItemsArray
{
Items = new BaseItem[]
{
new ItemA { Value = true, Text = "Test" },
new ItemB { Value = false, Number = 7 }
}
};
ItemsArray output;
using (var stream = new MemoryStream())
{
var serializer = new XmlSerializer(typeof(ItemsArray));
serializer.Serialize(stream, array);
stream.Position = 0;
output = (ItemsArray)serializer.Deserialize(stream);
}
}
}
After deserialization we get exactly what we serialized. The XML inside stream looks like:
<?xml version="1.0"?>
<ItemsArray xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Items>
<BaseItem xsi:type="ItemA">
<Value>true</Value>
<Text>Test</Text>
</BaseItem>
<BaseItem xsi:type="ItemB">
<Value>false</Value>
<Number>7</Number>
</BaseItem>
</Items>
</ItemsArray>
As was mentioned in other answer, you can't use different tags inside XML array. However, it's still possible to store different types. Serializer does this by using xsi:type attribute.
In order to solve your problem you probably need to use a bit another scheme of XML.