I tried to Deserialize an XML to an object using C#, but I am getting some errors.
My XML file is:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:transmission xmlns:ns0="blabla.xsd">
<ns0:DiagnosisErrorResponse>
<ns0:ID>7</ns0:ID>
<ns0:ErrorCode>9</ns0:ErrorCode>
<ns0:ErrorDescription>sometext</ns0:ErrorDescription>
<ns0:ErrorDate>11-12-2018</ns0:ErrorDate>
</ns0:DiagnosisErrorResponse>
</ns0:transmission>
I have an entity like this:
[XmlRoot(Namespace = "blabla.xsd", ElementName = "ns0:transmission", DataType = "string", IsNullable = true)]
public class DiagnosisErrorResponse
{
[XmlElement("ID")]
public long ID { get; set; }
[XmlElement("ErrorCode")]
public int ErrorCode { get; set; }
[XmlElement("ErrorDescription")]
public string ErrorDescription { get; set; }
[XmlElement("ErrorDate")]
public string ErrorDate { get; set; }
}
and my error is:
<transmission xmlns='blabla.xsd'> was not expected.
My function is this:
private void ReadXmlFileByPath(string filePath)
{
string xmlText = string.Empty;
XmlDocument d = new XmlDocument();
using (XmlTextReader tr = new XmlTextReader(filePath))
{
tr.Namespaces = true;
d.Load(tr);
xmlText = d.InnerXml;
}
XmlSerializer xs = new XmlSerializer(typeof(DiagnosisErrorResponse), "");
using (TextReader reader = new StringReader(xmlText))
{
DiagnosisErrorResponse result = (DiagnosisErrorResponse)xs.Deserialize(reader);
}
}
Update: I removed the 'ns0:' prefix from the XmlElement attribute at entity but I still get the same error.
Your problem is that your c# data model does not match your XML. Specifically:
You have indicated that the element name for DiagnosisErrorResponse should be "ns0:transmission". The namespace prefix ns0: should not be included, it is just a lookup in the XML file to find the actual namespace, which is defined by the xmlns attribute xmlns:ns0="blabla.xsd".
The XML has a level of nesting not accounted for in your data model. The XML in your question is not indented; if I indent it, it looks like:
<ns0:transmission xmlns:ns0="blabla.xsd">
<ns0:DiagnosisErrorResponse>
<ns0:ID>7</ns0:ID>
<ns0:ErrorCode>9</ns0:ErrorCode>
<ns0:ErrorDescription>sometext</ns0:ErrorDescription>
<ns0:ErrorDate>11-12-2018</ns0:ErrorDate>
</ns0:DiagnosisErrorResponse>
</ns0:transmission>
As can now be seen, ID and so on are not children of transmission, they are children of DiagnosisErrorResponse which is a child of transmission. This needs to be accounted for in your c# classes.
Combining these two issues, your data model should look like:
[XmlRoot(Namespace = "blabla.xsd", ElementName = "transmission", IsNullable = true)]
public class Transmission
{
[XmlElement("DiagnosisErrorResponse")]
public DiagnosisErrorResponse DiagnosisErrorResponse { get; set; }
}
[XmlRoot(Namespace = "blabla.xsd", IsNullable = true)]
public class DiagnosisErrorResponse
{
[XmlElement("ID")]
public long ID { get; set; }
[XmlElement("ErrorCode")]
public int ErrorCode { get; set; }
[XmlElement("ErrorDescription")]
public string ErrorDescription { get; set; }
[XmlElement("ErrorDate")]
public string ErrorDate { get; set; }
}
And your deserialization code should look like:
private DiagnosisErrorResponse ReadXmlFileByPath(string filePath)
{
using (var xmlReader = XmlReader.Create(filePath))
{
XmlSerializer xs = new XmlSerializer(typeof(Transmission));
var result = (Transmission)xs.Deserialize(xmlReader);
return result.DiagnosisErrorResponse;
}
}
Demo fiddle #1 here.
Alternatively, if you were trying to deserialize just the <DiagnosisErrorResponse> element inside the XML file without creating a data model for the root element, you could use XmlReader to scan forward in the XML until the appropriate element is found:
private DiagnosisErrorResponse ReadXmlFileByPath(string filePath)
{
using (var xmlReader = XmlReader.Create(filePath))
{
if (!xmlReader.ReadToDescendant("DiagnosisErrorResponse", "blabla.xsd"))
return null;
XmlSerializer xs = new XmlSerializer(typeof(DiagnosisErrorResponse));
var result = (DiagnosisErrorResponse)xs.Deserialize(xmlReader);
return result;
}
}
Demo fiddle #2 here.
Notes:
There is no need to load the XML file into an XmlDocument, re-serialize it as a string, then deserialize the string. The XML can be deserialized directly from a file in one step using an XmlReader or StreamReader.
XmlTextReader has been deprecated by Microsoft in its documentation. XmlReader.Create() should be used instead.
You can avoid modeling errors such as the ones above by using one of the code generation tools mentioned in Generate C# class from XML.
The easiest way to diagnose an error in XML deserialization is to serialize an instance of your root type, then compare the resulting XML with the XML to be deserialized. Inconsistencies are typically easily spotted and indicate where you have problems.
Related
How do I Deserialize this XML document:
<?xml version="1.0" encoding="utf-8"?>
<Cars>
<Car>
<StockNumber>1020</StockNumber>
<Make>Nissan</Make>
<Model>Sentra</Model>
</Car>
<Car>
<StockNumber>1010</StockNumber>
<Make>Toyota</Make>
<Model>Corolla</Model>
</Car>
<Car>
<StockNumber>1111</StockNumber>
<Make>Honda</Make>
<Model>Accord</Model>
</Car>
</Cars>
I have this:
[Serializable()]
public class Car
{
[System.Xml.Serialization.XmlElementAttribute("StockNumber")]
public string StockNumber{ get; set; }
[System.Xml.Serialization.XmlElementAttribute("Make")]
public string Make{ get; set; }
[System.Xml.Serialization.XmlElementAttribute("Model")]
public string Model{ get; set; }
}
.
[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
[XmlArrayItem(typeof(Car))]
public Car[] Car { get; set; }
}
.
public class CarSerializer
{
public Cars Deserialize()
{
Cars[] cars = null;
string path = HttpContext.Current.ApplicationInstance.Server.MapPath("~/App_Data/") + "cars.xml";
XmlSerializer serializer = new XmlSerializer(typeof(Cars[]));
StreamReader reader = new StreamReader(path);
reader.ReadToEnd();
cars = (Cars[])serializer.Deserialize(reader);
reader.Close();
return cars;
}
}
that don't seem to work :-(
How about you just save the xml to a file, and use xsd to generate C# classes?
Write the file to disk (I named it foo.xml)
Generate the xsd: xsd foo.xml
Generate the C#: xsd foo.xsd /classes
Et voila - and C# code file that should be able to read the data via XmlSerializer:
XmlSerializer ser = new XmlSerializer(typeof(Cars));
Cars cars;
using (XmlReader reader = XmlReader.Create(path))
{
cars = (Cars) ser.Deserialize(reader);
}
(include the generated foo.cs in the project)
Here's a working version. I changed the XmlElementAttribute labels to XmlElement because in the xml the StockNumber, Make and Model values are elements, not attributes. Also I removed the reader.ReadToEnd(); (that function reads the whole stream and returns a string, so the Deserialize() function couldn't use the reader anymore...the position was at the end of the stream). I also took a few liberties with the naming :).
Here are the classes:
[Serializable()]
public class Car
{
[System.Xml.Serialization.XmlElement("StockNumber")]
public string StockNumber { get; set; }
[System.Xml.Serialization.XmlElement("Make")]
public string Make { get; set; }
[System.Xml.Serialization.XmlElement("Model")]
public string Model { get; set; }
}
[Serializable()]
[System.Xml.Serialization.XmlRoot("CarCollection")]
public class CarCollection
{
[XmlArray("Cars")]
[XmlArrayItem("Car", typeof(Car))]
public Car[] Car { get; set; }
}
The Deserialize function:
CarCollection cars = null;
string path = "cars.xml";
XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
StreamReader reader = new StreamReader(path);
cars = (CarCollection)serializer.Deserialize(reader);
reader.Close();
And the slightly tweaked xml (I needed to add a new element to wrap <Cars>...Net is picky about deserializing arrays):
<?xml version="1.0" encoding="utf-8"?>
<CarCollection>
<Cars>
<Car>
<StockNumber>1020</StockNumber>
<Make>Nissan</Make>
<Model>Sentra</Model>
</Car>
<Car>
<StockNumber>1010</StockNumber>
<Make>Toyota</Make>
<Model>Corolla</Model>
</Car>
<Car>
<StockNumber>1111</StockNumber>
<Make>Honda</Make>
<Model>Accord</Model>
</Car>
</Cars>
</CarCollection>
You have two possibilities.
Method 1. XSD tool
Suppose that you have your XML file in this location C:\path\to\xml\file.xml
Open Developer Command Prompt
You can find it in Start Menu > Programs > Microsoft Visual Studio 2012 > Visual Studio Tools
Or if you have Windows 8 can just start typing Developer Command Prompt in Start screen
Change location to your XML file directory by typing cd /D "C:\path\to\xml"
Create XSD file from your xml file by typing xsd file.xml
Create C# classes by typing xsd /c file.xsd
And that's it! You have generated C# classes from xml file in C:\path\to\xml\file.cs
Method 2 - Paste special
Required Visual Studio 2012+
Copy content of your XML file to clipboard
Add to your solution new, empty class file (Shift+Alt+C)
Open that file and in menu click Edit > Paste special > Paste XML As Classes
And that's it!
Usage
Usage is very simple with this helper class:
using System;
using System.IO;
using System.Web.Script.Serialization; // Add reference: System.Web.Extensions
using System.Xml;
using System.Xml.Serialization;
namespace Helpers
{
internal static class ParseHelpers
{
private static JavaScriptSerializer json;
private static JavaScriptSerializer JSON { get { return json ?? (json = new JavaScriptSerializer()); } }
public static Stream ToStream(this string #this)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(#this);
writer.Flush();
stream.Position = 0;
return stream;
}
public static T ParseXML<T>(this string #this) where T : class
{
var reader = XmlReader.Create(#this.Trim().ToStream(), new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Document });
return new XmlSerializer(typeof(T)).Deserialize(reader) as T;
}
public static T ParseJSON<T>(this string #this) where T : class
{
return JSON.Deserialize<T>(#this.Trim());
}
}
}
All you have to do now, is:
public class JSONRoot
{
public catalog catalog { get; set; }
}
// ...
string xml = File.ReadAllText(#"D:\file.xml");
var catalog1 = xml.ParseXML<catalog>();
string json = File.ReadAllText(#"D:\file.json");
var catalog2 = json.ParseJSON<JSONRoot>();
The following snippet should do the trick (and you can ignore most of the serialization attributes):
public class Car
{
public string StockNumber { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
[XmlRootAttribute("Cars")]
public class CarCollection
{
[XmlElement("Car")]
public Car[] Cars { get; set; }
}
...
using (TextReader reader = new StreamReader(path))
{
XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
return (CarCollection) serializer.Deserialize(reader);
}
See if this helps:
[Serializable()]
[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
[XmlArrayItem(typeof(Car))]
public Car[] Car { get; set; }
}
.
[Serializable()]
public class Car
{
[System.Xml.Serialization.XmlElement()]
public string StockNumber{ get; set; }
[System.Xml.Serialization.XmlElement()]
public string Make{ get; set; }
[System.Xml.Serialization.XmlElement()]
public string Model{ get; set; }
}
And failing that use the xsd.exe program that comes with visual studio to create a schema document based on that xml file, and then use it again to create a class based on the schema document.
I don't think .net is 'picky about deserializing arrays'. The first xml document is not well formed.
There is no root element, although it looks like there is. The canonical xml document has a root and at least 1 element (if at all). In your example:
<Root> <-- well, the root
<Cars> <-- an element (not a root), it being an array
<Car> <-- an element, it being an array item
...
</Car>
</Cars>
</Root>
try this block of code if your .xml file has been generated somewhere in disk and if you have used List<T>:
//deserialization
XmlSerializer xmlser = new XmlSerializer(typeof(List<Item>));
StreamReader srdr = new StreamReader(#"C:\serialize.xml");
List<Item> p = (List<Item>)xmlser.Deserialize(srdr);
srdr.Close();`
Note: C:\serialize.xml is my .xml file's path. You can change it for your needs.
For Beginners
I found the answers here to be very helpful, that said I still struggled (just a bit) to get this working. So, in case it helps someone I'll spell out the working solution:
XML from Original Question. The xml is in a file Class1.xml, a path to this file is used in the code to locate this xml file.
I used the answer by #erymski to get this working, so created a file called Car.cs and added the following:
using System.Xml.Serialization; // Added
public class Car
{
public string StockNumber { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
[XmlRootAttribute("Cars")]
public class CarCollection
{
[XmlElement("Car")]
public Car[] Cars { get; set; }
}
The other bit of code provided by #erymski ...
using (TextReader reader = new StreamReader(path))
{
XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
return (CarCollection) serializer.Deserialize(reader);
}
... goes into your main program (Program.cs), in static CarCollection XCar() like this:
using System;
using System.IO;
using System.Xml.Serialization;
namespace ConsoleApp2
{
class Program
{
public static void Main()
{
var c = new CarCollection();
c = XCar();
foreach (var k in c.Cars)
{
Console.WriteLine(k.Make + " " + k.Model + " " + k.StockNumber);
}
c = null;
Console.ReadLine();
}
static CarCollection XCar()
{
using (TextReader reader = new StreamReader(#"C:\Users\SlowLearner\source\repos\ConsoleApp2\ConsoleApp2\Class1.xml"))
{
XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
return (CarCollection)serializer.Deserialize(reader);
}
}
}
}
Hope it helps :-)
Kevin's anser is good, aside from the fact, that in the real world, you are often not able to alter the original XML to suit your needs.
There's a simple solution for the original XML, too:
[XmlRoot("Cars")]
public class XmlData
{
[XmlElement("Car")]
public List<Car> Cars{ get; set; }
}
public class Car
{
public string StockNumber { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
And then you can simply call:
var ser = new XmlSerializer(typeof(XmlData));
var data = (XmlData)ser.Deserialize(XmlReader.Create(PathToCarsXml));
One liner:
var object = (Cars)new XmlSerializer(typeof(Cars)).Deserialize(new StringReader(xmlString));
Try this Generic Class For Xml Serialization & Deserialization.
public class SerializeConfig<T> where T : class
{
public static void Serialize(string path, T type)
{
var serializer = new XmlSerializer(type.GetType());
using (var writer = new FileStream(path, FileMode.Create))
{
serializer.Serialize(writer, type);
}
}
public static T DeSerialize(string path)
{
T type;
var serializer = new XmlSerializer(typeof(T));
using (var reader = XmlReader.Create(path))
{
type = serializer.Deserialize(reader) as T;
}
return type;
}
}
How about a generic class to deserialize an XML document
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Generic class to load any xml into a class
// used like this ...
// YourClassTypeHere InfoList = LoadXMLFileIntoClass<YourClassTypeHere>(xmlFile);
using System.IO;
using System.Xml.Serialization;
public static T LoadXMLFileIntoClass<T>(string xmlFile)
{
T returnThis;
XmlSerializer serializer = new XmlSerializer(typeof(T));
if (!FileAndIO.FileExists(xmlFile))
{
Console.WriteLine("FileDoesNotExistError {0}", xmlFile);
}
returnThis = (T)serializer.Deserialize(new StreamReader(xmlFile));
return (T)returnThis;
}
This part may, or may not be necessary. Open the XML document in Visual Studio, right click on the XML, choose properties. Then choose your schema file.
The idea is to have all level being handled for deserialization
Please see a sample solution that solved my similar issue
<?xml version="1.0" ?>
<TRANSACTION_RESPONSE>
<TRANSACTION>
<TRANSACTION_ID>25429</TRANSACTION_ID>
<MERCHANT_ACC_NO>02700701354375000964</MERCHANT_ACC_NO>
<TXN_STATUS>F</TXN_STATUS>
<TXN_SIGNATURE>a16af68d4c3e2280e44bd7c2c23f2af6cb1f0e5a28c266ea741608e72b1a5e4224da5b975909cc43c53b6c0f7f1bbf0820269caa3e350dd1812484edc499b279</TXN_SIGNATURE>
<TXN_SIGNATURE2>B1684258EA112C8B5BA51F73CDA9864D1BB98E04F5A78B67A3E539BEF96CCF4D16CFF6B9E04818B50E855E0783BB075309D112CA596BDC49F9738C4BF3AA1FB4</TXN_SIGNATURE2>
<TRAN_DATE>29-09-2015 07:36:59</TRAN_DATE>
<MERCHANT_TRANID>150929093703RUDZMX4</MERCHANT_TRANID>
<RESPONSE_CODE>9967</RESPONSE_CODE>
<RESPONSE_DESC>Bank rejected transaction!</RESPONSE_DESC>
<CUSTOMER_ID>RUDZMX</CUSTOMER_ID>
<AUTH_ID />
<AUTH_DATE />
<CAPTURE_DATE />
<SALES_DATE />
<VOID_REV_DATE />
<REFUND_DATE />
<REFUND_AMOUNT>0.00</REFUND_AMOUNT>
</TRANSACTION>
</TRANSACTION_RESPONSE>
The above XML is handled in two level
[XmlType("TRANSACTION_RESPONSE")]
public class TransactionResponse
{
[XmlElement("TRANSACTION")]
public BankQueryResponse Response { get; set; }
}
The Inner level
public class BankQueryResponse
{
[XmlElement("TRANSACTION_ID")]
public string TransactionId { get; set; }
[XmlElement("MERCHANT_ACC_NO")]
public string MerchantAccNo { get; set; }
[XmlElement("TXN_SIGNATURE")]
public string TxnSignature { get; set; }
[XmlElement("TRAN_DATE")]
public DateTime TranDate { get; set; }
[XmlElement("TXN_STATUS")]
public string TxnStatus { get; set; }
[XmlElement("REFUND_DATE")]
public DateTime RefundDate { get; set; }
[XmlElement("RESPONSE_CODE")]
public string ResponseCode { get; set; }
[XmlElement("RESPONSE_DESC")]
public string ResponseDesc { get; set; }
[XmlAttribute("MERCHANT_TRANID")]
public string MerchantTranId { get; set; }
}
Same Way you need multiple level with car as array
Check this example for multilevel deserialization
If you're getting errors using xsd.exe to create your xsd file, then use the XmlSchemaInference class as mentioned on msdn. Here's a unit test to demonstrate:
using System.Xml;
using System.Xml.Schema;
[TestMethod]
public void GenerateXsdFromXmlTest()
{
string folder = #"C:\mydir\mydata\xmlToCSharp";
XmlReader reader = XmlReader.Create(folder + "\some_xml.xml");
XmlSchemaSet schemaSet = new XmlSchemaSet();
XmlSchemaInference schema = new XmlSchemaInference();
schemaSet = schema.InferSchema(reader);
foreach (XmlSchema s in schemaSet.Schemas())
{
XmlWriter xsdFile = new XmlTextWriter(folder + "\some_xsd.xsd", System.Text.Encoding.UTF8);
s.Write(xsdFile);
xsdFile.Close();
}
}
// now from the visual studio command line type: xsd some_xsd.xsd /classes
You can just change one attribute for you Cars car property from XmlArrayItem to XmlElment. That is, from
[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
[XmlArrayItem(typeof(Car))]
public Car[] Car { get; set; }
}
to
[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
[XmlElement("Car")]
public Car[] Car { get; set; }
}
My solution:
Use Edit > Past Special > Paste XML As Classes to get the class in your code
Try something like this: create a list of that class (List<class1>), then use the XmlSerializer to serialize that list to a xml file.
Now you just replace the body of that file with your data and try to deserialize it.
Code:
StreamReader sr = new StreamReader(#"C:\Users\duongngh\Desktop\Newfolder\abc.txt");
XmlSerializer xml = new XmlSerializer(typeof(Class1[]));
var a = xml.Deserialize(sr);
sr.Close();
NOTE: you must pay attention to the root name, don't change it. Mine is "ArrayOfClass1"
I wanna get xml file from http and convert it to object.
So right now I have 2 methods: one to get http response body like that:
var httpClient = new HttpClient();
var op = httpClient.GetStringAsync(uri);
var httpResponseBody = "";
try {
var httpResponse = await httpClient.GetAsync(uri);
httpResponse.EnsureSuccessStatusCode();
httpResponseBody = await httpResponse.Content.ReadAsStringAsync();
return httpResponseBody;
}
...
which returns string httpResponseBody.
Second one tries to convert this xml in string to object:
res = await task;
var reader = new XmlSerializer(typeof(Schedule));
using (var tr = new MemoryStream(Encoding.UTF8.GetBytes(res)))
{
var schedule = (Schedule)reader.Deserialize(tr);
return schedule;
}
The problem is that the content I receive is in different encoding and I don't know how to convert it to make deserialization possible.
I am getting something like this:
<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ramowka><dzien name=\"PoniedziaÅ\u0082ek\" count=\"2\"/></ramowka>\n
How to get rid of '\n' and Å\u0082 (should be ł) ?
Right now I am getting Exception from reader.Deserialize: {"<ramowka xmlns=''> was not expected."}
Schedule class:
[XmlType(AnonymousType = true)]
[XmlRootAttribute(Namespace = "", IsNullable = false)]
public class Schedule
{
[XmlElementAttribute("ramowka")]
public ScheduleDay[] AuditionDays { get; set; }
}
I've changed Schedule class to:
[XmlType(AnonymousType = true)]
[XmlRootAttribute("ramowka")]
public class Schedule
{
[XmlElementAttribute("dzien")]
public ScheduleDay[] AuditionDays { get; set; }
}
Now it looks like working. Thanks Petter for hint with Root attribute.
Setting the root object on the XmlSerializer fixes the problem:
var reader = new XmlSerializer(typeof(Schedule), new XmlRootAttribute("ramowka"));
...though I used slightly different attributes:
[DataContract]
public class ScheduleDay
{
[DataMember, XmlAttribute]
public string name { get; set; }
[DataMember, XmlAttribute]
public string count { get; set; }
}
[DataContract]
public class Schedule
{
[DataMember]
public ScheduleDay dzien { get; set; }
}
I haven't tried yours yet, but these work.
For a collection of ScheduleDays, this combo works:
[XmlType("dzien")]
public class ScheduleDay
{
[XmlAttribute]
public string name { get; set; }
[XmlAttribute]
public string count { get; set; }
}
Usage:
XmlSerializer reader = new XmlSerializer(typeof(List<ScheduleDay>), new XmlRootAttribute("ramowka"));
using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(Xml)))
{
List<ScheduleDay> schedule = (List<ScheduleDay>)reader.Deserialize(stream);
}
The Schedule class just disappeared from the equation.
Escapes in the HTML
The \ns are part of the XML structure, so no need to worry about those. The deserializer will translate \u0082 into its equivalent character, which is
BREAK PERMITTED HERE. Which you probably don't want. The Å looks out of place too -- it's the last letter of the Norwegian alphabet and not used in Polish, AFAIK.
I am attempting to deserialize an XML string to an object.
The object is:
[Serializable]
public class THIRD_PARTY_CONFIRMATION
{
public string thirdPartyId { get; set; }
}
and the code I am trying to run is:
var response = "<?xml version='1.0' encoding='UTF-8' ?><THIRD_PARTY_CONFIRMATION thirdPartyId = \"3984000\" />";
using (var stream = new StringReader(response))
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(THIRD_PARTY_CONFIRMATION));
var temp = (THIRD_PARTY_CONFIRMATION)xmlSerializer.Deserialize(stream);
}
If I inspect temp in Visual Studio, thirdPartyId is null. What am I doing wrong?
You need to add the property XmlAttribute to the thirdPartyId
[Serializable]
public class THIRD_PARTY_CONFIRMATION
{
[XmlAttribute]
public string thirdPartyId { get; set; }
}
otherwise it'll start looking for a value of the element and not an attribute.
I'm trying to read in element values from a simple XML doc and bind these to an object however I'm running into problems with my XML document. I have validated it and can confirm there are no issues with the document itself however expanding the results on the line:
var nodes = from xDoc in xml.Descendants("RewriteRule")
select xmlSerializer.Deserialize(xml.CreateReader()) as Url;
Show "There is an error in XML document (0, 0)"
The inner exceptions reads <RewriteRules xmlns=''> was not expected.
I'm not sure what I'm doing wrong here?
My XML is below:
<?xml version="1.0" encoding="utf-8" ?>
<RewriteRules>
<RewriteRule>
<From>fromurl</From>
<To>tourl</To>
<Type>301</Type>
</RewriteRule>
</RewriteRules>
The code that loads the XML file and attempts to de-serialize it: -
public static UrlCollection GetRewriteXML(string fileName)
{
XDocument xml = XDocument.Load(HttpContext.Current.Server.MapPath(fileName));
var xmlSerializer = new XmlSerializer(typeof(Url));
var nodes = from xDoc in xml.Descendants("RewriteRule")
select xmlSerializer.Deserialize(xml.CreateReader()) as Url;
return nodes as UrlCollection;
}
My Url object class: -
[Serializable]
[XmlRoot("RewriteRule")]
public class Url
{
[XmlElement("From")]
public string From { get; set; }
[XmlElement("To")]
public string To { get; set; }
[XmlElement("Type")]
public string StatusCode { get; set; }
public Url()
{
}
public Url(Url url)
{
url.From = this.From;
url.To = this.To;
url.StatusCode = this.StatusCode;
}
}
Can anyone see what I'm doing wrong here?
Thanks
I'm not too familiar with the from select statement, but it seems you just pass in xml which is the whole XDocument, instead of the XElement that is your RewriteRule. That is why you get the error message that RewriteRules is unknown - the XmlSerializer expects a single RewriteRule.
I managed to rewrite your code using LINQ instead (but if you know how to get the single element from the from select statement, that should work equally well).
This should give you the correct result - rr is the XElement that is returned from Descendants:
public static IEnumerable<Url> GetRewriteXML()
{
XDocument xml = XDocument.Load(HttpContext.Current.Server.MapPath(fileName));
var xmlSerializer = new XmlSerializer(typeof(Url));
var nodes = xml.Descendants("RewriteRule")
.Select(rr => xmlSerializer.Deserialize(rr.CreateReader()) as Url);
return nodes;
}
EDIT:
The name of your Url class does not match. You need to rename it to "RewriteRule"or define it this way:
[Serializable]
[System.Xml.Serialization.XmlRoot("RewriteRule")]
public class Url
{
[XmlElement("From")]
public string From { get; set; }
[XmlElement("To")]
public string To { get; set; }
[XmlElement("Type")]
public string StatusCode { get; set; }
public Url()
{
}
public Url(Url url)
{
url.From = this.From;
url.To = this.To;
url.StatusCode = this.StatusCode;
}
}
my approach is more used, when you deserialize it directly to an instance of a class.
If you want to use XDocument you may just write it like this. I do not use XDocument in my code. Since I need to deserialize the full xml packages I do it directly with deserialize of the root node. Therefore, I have proposed to the the root node to the right place in the previous post.
With XDocument you can access subparts directly. Here is working code for your purpose, but there may be others who can help you setting this code up more elegant:
public static UrlCollection GetRewriteXML(string fileName)
{
XDocument xml = XDocument.Load(HttpContext.Current.Server.MapPath(fileName));
var urls = from s in xml.Descendants("RewriteRule")
select new
{
From = (string)s.Element("From").Value,
To = (string)s.Element("To").Value,
StatusCode = (string)s.Element("Type").Value
};
UrlCollection nodes = new UrlCollection();
foreach (var url in urls)
{
nodes.Add(new Url(url.From, url.To, url.StatusCode));
}
return nodes;
}
[Serializable]
public class Url
{
[XmlElement("From")]
public string From { get; set; }
[XmlElement("To")]
public string To { get; set; }
[XmlElement("Type")]
public string StatusCode { get; set; }
public Url()
{
}
public Url(string From, string To, string StatusCode)
{
this.From = From;
this.To = To;
this.StatusCode = StatusCode;
}
public Url(Url url)
{
url.From = this.From;
url.To = this.To;
url.StatusCode = this.StatusCode;
}
}
I'm pulling in the XML from Twitter via OAuth.
I'm doing a request to http://twitter.com/account/verify_credentials.xml, which returns the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<user>
<id>16434938</id>
<name>Lloyd Sparkes</name>
<screen_name>lloydsparkes</screen_name>
<location>Hockley, Essex, UK</location>
<description>Student</description>
<profile_image_url>http://a3.twimg.com/profile_images/351849613/twitterProfilePhoto_normal.jpg</profile_image_url>
<url>http://www.lloydsparkes.co.uk</url>
<protected>false</protected>
<followers_count>115</followers_count>
<profile_background_color>9fdaf4</profile_background_color>
<profile_text_color>000000</profile_text_color>
<profile_link_color>220f7b</profile_link_color>
<profile_sidebar_fill_color>FFF7CC</profile_sidebar_fill_color>
<profile_sidebar_border_color>F2E195</profile_sidebar_border_color>
<friends_count>87</friends_count>
<created_at>Wed Sep 24 14:26:09 +0000 2008</created_at>
<favourites_count>0</favourites_count>
<utc_offset>0</utc_offset>
<time_zone>London</time_zone>
<profile_background_image_url>http://s.twimg.com/a/1255366924/images/themes/theme12/bg.gif</profile_background_image_url>
<profile_background_tile>false</profile_background_tile>
<statuses_count>1965</statuses_count>
<notifications>false</notifications>
<geo_enabled>false</geo_enabled>
<verified>false</verified>
<following>false</following>
<status>
<created_at>Mon Oct 12 19:23:47 +0000 2009</created_at>
<id>4815268670</id>
<text>» #alexmuller your kidding? it should all be "black tie" dress code</text>
<source><a href="http://code.google.com/p/wittytwitter/" rel="nofollow">Witty</a></source>
<truncated>false</truncated>
<in_reply_to_status_id>4815131457</in_reply_to_status_id>
<in_reply_to_user_id>8645442</in_reply_to_user_id>
<favorited>false</favorited>
<in_reply_to_screen_name>alexmuller</in_reply_to_screen_name>
<geo/>
</status>
</user>
I am using the following code to deserialize:
public User VerifyCredentials()
{
string url = "http://twitter.com/account/verify_credentials.xml";
string xml = _oauth.oAuthWebRequestAsString(oAuthTwitter.Method.GET, url, null);
XmlSerializer xs = new XmlSerializer(typeof(User),"");
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(xml));
return (User)xs.Deserialize(ms);
}
And I have the following for my User class:
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class User
{
[XmlElement(ElementName = "id")]
public long Id { get; set; }
[XmlElement(ElementName = "name")]
public string Name { get; set; }
[XmlElement(ElementName = "screen_name")]
public string ScreenName { get; set; }
[XmlElement(ElementName = "location")]
public string Location { get; set; }
[XmlElement(ElementName = "description")]
public string Description { get; set; }
[XmlElement(ElementName = "profile_image_url")]
public string ProfileImageUrl { get; set; }
[XmlElement(ElementName = "url")]
public string Url { get; set; }
[XmlElement(ElementName = "protected")]
public bool Protected { get; set; }
[XmlElement(ElementName = "followers_count")]
public int FollowerCount { get; set; }
[XmlElement(ElementName = "profile_background_color")]
public string ProfileBackgroundColor { get; set; }
[XmlElement(ElementName = "profile_text_color")]
public string ProfileTextColor { get; set; }
[XmlElement(ElementName = "profile_link_color")]
public string ProfileLinkColor { get; set; }
[XmlElement(ElementName = "profile_sidebar_fill_color")]
public string ProfileSidebarFillColor { get; set; }
[XmlElement(ElementName = "profile_sidebar_border_color")]
public string ProfileSidebarBorderColor { get; set; }
[XmlElement(ElementName = "friends_count")]
public int FriendsCount { get; set; }
[XmlElement(ElementName = "created_at")]
public string CreatedAt { get; set; }
[XmlElement(ElementName = "favourties_count")]
public int FavouritesCount { get; set; }
[XmlElement(ElementName = "utc_offset")]
public int UtcOffset { get; set; }
[XmlElement(ElementName = "time_zone")]
public string Timezone { get; set; }
[XmlElement(ElementName = "profile_background_image_url")]
public string ProfileBackgroundImageUrl { get; set; }
[XmlElement(ElementName = "profile_background_tile")]
public bool ProfileBackgroundTile { get; set; }
[XmlElement(ElementName = "statuese_count")]
public int StatusesCount { get; set; }
[XmlElement(ElementName = "notifications")]
public string Notifications { get; set; }
[XmlElement(ElementName = "geo_enabled")]
public bool GeoEnabled { get; set; }
[XmlElement(ElementName = "Verified")]
public bool Verified { get; set; }
[XmlElement(ElementName = "following")]
public string Following { get; set; }
[XmlElement(ElementName = "status", IsNullable=true)]
public Status CurrentStatus { get; set; }
}
But when it's deserializing the above XML the application throws the following:
$exception {"There is an error in XML document (2, 2)."} System.Exception {System.InvalidOperationException}
InnerException {"<user xmlns=''> was not expected."} System.Exception {System.InvalidOperationException}
Now I have searched around and the best solution I can find is to add blank namespaces to the serializer when you serialize the content, but i'm not serializing it so I can't.
I also have some code for receiving Statuses, which works fine.
So can someone explain to me why the error is happening? As well as a possible solution?
Either decorate your root entity with the XmlRoot attribute which will be used at compile time.
[XmlRoot(Namespace = "www.contoso.com", ElementName = "MyGroupName", DataType = "string", IsNullable=true)]
Or specify the root attribute when de serializing at runtime.
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "user";
// xRoot.Namespace = "http://www.cpandl.com";
xRoot.IsNullable = true;
XmlSerializer xs = new XmlSerializer(typeof(User),xRoot);
Even easier is just to add the following annotations to the top of your class:
[Serializable, XmlRoot("user")]
public partial class User
{
}
XmlSerializer xs = new XmlSerializer(typeof(User), new XmlRootAttribute("yourRootName"));
The error message is so vague, for me I had this code:
var streamReader = new StreamReader(response.GetResponseStream());
var xmlSerializer = new XmlSerializer(typeof(aResponse));
theResponse = (bResponse) xmlSerializer.Deserialize(streamReader);
Notice xmlSerializer is instantiated with aResponse but on deserializing I accidentally casted it to bResonse.
The simplest and best solution is just to use XMLRoot attribute in your class, in which you wish to deserialize.
Like:
[XmlRoot(ElementName = "YourPreferableNameHere")]
public class MyClass{
...
}
Also, use the following Assembly :
using System.Xml.Serialization;
In my case, my xml had multiple namespaces and attributes.
So I used this site to generate the objects - https://xmltocsharp.azurewebsites.net/
And used the below code to deserialize
XmlDocument doc = new XmlDocument();
doc.Load("PathTo.xml");
User obj;
using (TextReader textReader = new StringReader(doc.OuterXml))
{
using (XmlTextReader reader = new XmlTextReader(textReader))
{
XmlSerializer serializer = new XmlSerializer(typeof(User));
obj = (User)serializer.Deserialize(reader);
}
}
As John Saunders says, check if the class/property names matches the capital casing of your XML. If this isn't the case, the problem will also occur.
The only thing that worked in my case was by using david valentine code. Using Root Attr. in the Person class did not help.
I have this simple Xml:
<?xml version="1.0"?>
<personList>
<Person>
<FirstName>AAAA</FirstName>
<LastName>BBB</LastName>
</Person>
<Person>
<FirstName>CCC</FirstName>
<LastName>DDD</LastName>
</Person>
</personList>
C# class:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
De-Serialization C# code from a Main method:
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "personList";
xRoot.IsNullable = true;
using (StreamReader reader = new StreamReader(xmlFilePath))
{
List<Person> result = (List<Person>)(new XmlSerializer(typeof(List<Person>), xRoot)).Deserialize(reader);
int numOfPersons = result.Count;
}
My issue was that the root element actually has a xmlns="abc123"
So had to make XmlRoot("elementname",NameSpace="abc123")
Nothing worked for me for these errors EXCEPT
... was not expected,
... there is an error in XML document (1,2)
... System.FormatException Input String was not in correct format ...
Except this way
1- You need to inspect the xml response as string (the response that you are trying to de-serialize to an object)
2- Use online tools for string unescape and xml prettify/formatter
3- MAKE SURE that the C# Class (main class) you are trying to map/deserialize the xml string to HAS AN XmlRootAttribute that matches the root element of the response.
Exmaple:
My XML Response was staring like :
<ShipmentCreationResponse xmlns="http://ws.aramex.net/ShippingAPI/v1/">
<Transaction i:nil="true" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"/>
....
And the C# class definition + attributes was like :
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.MessageContractAttribute(WrapperName="ShipmentCreationResponse", WrapperNamespace="http://ws.aramex.net/ShippingAPI/v1/", IsWrapped=true)]
public partial class ShipmentCreationResponse {
.........
}
Note that the class definition does not have "XmlRootAttribute"
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://ws.aramex.net/ShippingAPI/v1/", IsNullable = false)]
And when i try to de serialize using a generic method :
public static T Deserialize<T>(string input) where T : class
{
System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(T));
using (System.IO.StringReader sr = new System.IO.StringReader(input))
{
return (T)ser.Deserialize(sr);
}
}
var _Response = GeneralHelper.XMLSerializer.Deserialize<ASRv2.ShipmentCreationResponse>(xml);
I was getting the errors above
... was not expected, ... there is an error in XML document (1,2) ...
Now by just adding the "XmlRootAttribute" that fixed the issue for ever and for every other requests/responses i had a similar issue with:
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://ws.aramex.net/ShippingAPI/v1/", IsNullable = false)]
..
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://ws.aramex.net/ShippingAPI/v1/")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://ws.aramex.net/ShippingAPI/v1/", IsNullable = false)]
public partial class ShipmentCreationResponse
{
........
}
My problem was one of my elements had the xmlns attribute:
<?xml version="1.0" encoding="utf-8"?>
<RETS ReplyCode="0">
<RETS-RESPONSE xmlns="blahblah">
...
</RETS-RESPONSE>
</RETS>
No matter what I tried the xmlns attribute seemed to be breaking the serializer, so I removed any trace of xmlns="..." from the xml file:
<?xml version="1.0" encoding="utf-8"?>
<RETS ReplyCode="0">
<RETS-RESPONSE>
...
</RETS-RESPONSE>
</RETS>
and voila! Everything worked.
I now parse the xml file to remove this attribute before deserializing.
Not sure why this works, maybe my case is different since the element containing the xmlns attribute is not the root element.
All above not worked for me, but this was:
Check that the name of Root element of class is exactly like the one from XML case sensitive.