How to deserialize XML into an object? - c#

I'm trying to convert an XML doc (invoiceList) into an object. Ideally I'd like to convert it into a collection of objects ("Invoices").
Below is a fragment of the XML. I've also included a fragment off my INVOICE object. And finally my attempt at deserialization. The error message I get is so useless that I don't know where to start.
<?xml version="1.0" encoding="utf-8"?>
<invoiceListResponse>
<invoiceList>
<invoiceListItem>
<invoiceUid>39165890</invoiceUid>
<lastUpdatedUid>AAAAADrKwis=</lastUpdatedUid>
<ccy>JPY</ccy>
<autoPopulateFXRate>false</autoPopulateFXRate>
<fcToBcFxRate>1.0000000000</fcToBcFxRate>
<transactionType>S</transactionType>
<invoiceDate>2013-12-26</invoiceDate>
<utcFirstCreated>2013-12-26T08:12:22</utcFirstCreated>
<utcLastModified>2013-12-26T08:12:22</utcLastModified>
<summary />
<invoiceNumber>W101010101</invoiceNumber>
fragment of Invoice object code
[XmlRoot(ElementName = "invoice")]
public class InvoiceDto : TransactionDto
{
public InvoiceDto()
{
TradingTerms = new TradingTermsDto();
QuickPayment = new QuickPaymentDto();
}
public InvoiceDto(string transactionType, string layout)
{
Layout = layout;
TransactionType = transactionType;
TradingTerms = new TradingTermsDto();
QuickPayment = new QuickPaymentDto();
}
[XmlElement(ElementName = "transactionType")]
public string TransactionType;
[XmlElement(ElementName = "invoiceType")]
public string InvoiceType;
[XmlElement(ElementName = "contactUid")]
public int ContactUid;
[XmlElement(ElementName = "shipToContactUid")]
public int ShipToContactUid;
[XmlElement(ElementName = "externalNotes")]
public string ExternalNotes;
My code:
Dim list As XmlDocument = proxy.Find(queries)
'Deserialize text file to a new object.
Using reader As XmlReader = XmlReader.Create(New StringReader(list.InnerXml))
reader.MoveToContent()
reader.Read()
reader.Read()
theinv = DirectCast(New XmlSerializer(GetType(Dto.InvoiceDto)).Deserialize(reader), Dto.InvoiceDto)
Debug.Write(theinv.InvoiceNumber)
Error is:
An unhandled exception of type 'System.InvalidOperationException'
occurred in System.Xml.dll
Additional information: There is an error in XML document (1, 74).

The easiest thing to do is to create a class that matches the entire XML document from its root level down. It doesn't need to contain a property for every node, but it needs to at least contain a property for each element in the path to get from the root element down to the ones that you care about. For instance, the following class will work to load the document in your example:
[XmlRoot("invoiceListResponse")]
public class InvoiceListResponse
{
[XmlArray("invoiceList")]
[XmlArrayItem("invoiceListItem")]
public InvoiceDto[] InvoiceList;
}
Then, you can deserialize into it like this:
XmlSerializer s = new XmlSerializer(typeof(InvoiceListResponse));
using (FileStream f = new FileStream("Test.xml", System.IO.FileMode.Open))
{
InvoiceListResponse response = (InvoiceListResponse)s.Deserialize(f);
}
Edit
Based on your comments, below, it appears that what you need to do is to deserialize into that exact DTO class, without making any modifications to it. If that is the case, and you don't want to create a wrapper class as I demonstrated in my first example, you could always do something like this:
Dim invoices As New List(Of InvoiceDto)()
Dim serializer As New XmlSerializer(GetType(InvoiceDto))
For Each i As XmlNode In doc.SelectNodes("/invoiceListResponse/invoiceList/invoiceListItem")
Using reader As New StringReader("<invoice>" & i.InnerXml & "</invoice>")
invoices.Add(DirectCast(serializer.Deserialize(reader), InvoiceDto))
End Using
Next

Related

Use only parts of an xml-file

I'm relatively new to Unity and C#. Actually, I mainly look at application code and try to learn a little bit. And that's fun.
Now I've stumbled upon a problem.
I'm trying to read an XML file and continue using the data from it. That even works. But now I don't want to use all records of the XML file, but only those that have a certain ID.
Currently I do it like this:
public class Data
{
public frage[] fragen = new frage[0];
public Data () { }
public static void Write(Data data)
{
XmlSerializer serializer = new XmlSerializer(typeof(Data));
using (Stream stream = new FileStream (GameUtility.XmlFilePath, FileMode.Create))
{
serializer.Serialize(stream, data);
}
}
public static Data Fetch ()
{
return Fetch(out bool result);
}
public static Data Fetch(out bool result)
{
if (!File.Exists(GameUtility.XmlFilePath)) { result = false; return new Data(); }
XmlSerializer deserializer = new XmlSerializer(typeof(Data));
using (Stream stream = new FileStream(GameUtility.XmlFilePath, FileMode.Open))
{
var data = (Data)deserializer.Deserialize(stream);
result = true;
return data;
}
}
}
This causes, I think, that all data is stored in the corresponding variable (data). But now I want only those data sets to be transferred that have the ID 5. Is this possible with simple adjustments or do I have to think about everything?
My data set, which is created via XML, looks like this:
<?xml version="1.0"?>
<Data xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<fragen>
<frage>
<info>IRGENDEINTEXT1</info>
<antworten>
<antwort>
<info>1</info>
<korrekt>true</korrekt>
</antwort>
<antwort>
<info>2</info>
<korrekt>false</korrekt>
</antwort>
<antwort>
<info>3</info>
<korrekt>false</korrekt>
</antwort>
<antwort>
<info>4</info>
<korrekt>false</korrekt>
</antwort>
</antworten>
<id>5</id>
</frage>
<frage>
<info>IRGENDEINTEXT2</info>
<antworten>
<antwort>
<info>1</info>
<korrekt>false</korrekt>
</antwort>
<antwort>
<info>2</info>
<korrekt>false</korrekt>
</antwort>
<antwort>
<info>3</info>
<korrekt>false</korrekt>
</antwort>
<antwort>
<info>4</info>
<korrekt>true</korrekt>
</antwort>
</antworten>
<id>7</id>
</frage>
</fragen>
</Data>
Thank you - I hope my question is meaningful and not too unclear. Forgive my incorrect English.
You could have, but I would advise against reading only partial files all the time because that is a constant IO hit. Every time you would need to fetch a new question you get another read IO on the disk. Unless your set of questions is humongous (e.g. several hundred MB), you could read all data into your Data object at game start and then just add a helper function into your Data class that supplies you with the relevant information. E.g.
class Data {
private Frage[] fragen;
// Read a Frage by ID
public Frage QuestionById(string id) {
return this.fragen.First(it => it.id == id);
}
}
Same for the answers:
class Frage {
private Antwort[] antworten;
public Antwort GetCorrectAnswer() {
return antworten.First(it => it.korrekt);
}
}
Then in your game logic you can just call:
var aFrage = data.QuestionById("4711");
var anAntwort = aFrage.GetCorrectAnswer();
You could theoretically also use XPath to just select the XML nodes you need, but for this to work you would still need to load the whole XML document into memory in order to run your XPath over it (I'm unaware of any XPath implementation for .net that would work on streams). So you may as well just use your own data structure as I have laid out.
If you really need a huge data set and cannot load everything into memory, you should maybe look at some database solution, e.g. SQLite to store your data. This would allow you to do SQL DB queries and doesn't require you to load all the data into memory.
I agree with Jan Thomä. What you do is called deserialization (when you create an xml or json from an object you do a serialization). So you just create an object from your xml file (of course tecnically you need to read the file to deserialize it but, of course, you use a library to handle it). Once you got the data you should be able to change your object or create a modified copy with your correct data.
Usually I prefer json so I'm not sure how to do it with XmlSerializer library (link with an example in the edit part) but you should create a bean object that is a representation of your xml file
(example antwort is one of the basic object and it should have a int info variable and a korrekt boolean variable, while antworten is an object that only contains a List of antwort and so on until you reach your main object fragen that contains all your basic rappresentation of your xml bean to deserialize (to create a bean just declare your variables and Getter/Setters for those variables))
After that you just deserialize your xml as a Fragen instead of as a Data type and you can than change your object the easy way.
EDIT. To undestand it better just look the example here:
https://learn.microsoft.com/it-it/dotnet/api/system.xml.serialization.xmlserializer.deserialize?view=netframework-4.8
you got the xml and the class that representate the object OrderedItem (note: the namespace should be optional but you need to call the variable with the same name of the variable in your xml and you should need getter and setter in OrderedItem class). Once you got OrderedItem item it's easy to read/write new value to the object, serialize a new xml with new data or just create a modified copy of your object.
You should just do the same thing.
Example from the link:
using System;
using System.IO;
using System.Xml.Serialization;
// This is the class that will be deserialized.
public class OrderedItem
{
[XmlElement(Namespace = "http://www.cpandl.com")]
public string ItemName;
[XmlElement(Namespace = "http://www.cpandl.com")]
public string Description;
[XmlElement(Namespace="http://www.cohowinery.com")]
public decimal UnitPrice;
[XmlElement(Namespace = "http://www.cpandl.com")]
public int Quantity;
[XmlElement(Namespace="http://www.cohowinery.com")]
public decimal LineTotal;
// A custom method used to calculate price per item.
public void Calculate()
{
LineTotal = UnitPrice * Quantity;
}
}
public class Test
{
public static void Main()
{
Test t = new Test();
// Read a purchase order.
t.DeserializeObject("simple.xml");
}
private void DeserializeObject(string filename)
{
Console.WriteLine("Reading with Stream");
// Create an instance of the XmlSerializer.
XmlSerializer serializer =
new XmlSerializer(typeof(OrderedItem));
// Declare an object variable of the type to be deserialized.
OrderedItem i;
using (Stream reader = new FileStream(filename, FileMode.Open))
{
// Call the Deserialize method to restore the object's state.
i = (OrderedItem)serializer.Deserialize(reader);
}
// Write out the properties of the object.
Console.Write(
i.ItemName + "\t" +
i.Description + "\t" +
i.UnitPrice + "\t" +
i.Quantity + "\t" +
i.LineTotal);
}
}
xml
<?xml version="1.0"?>
<OrderedItem xmlns:inventory="http://www.cpandl.com" xmlns:money="http://www.cohowinery.com">
<inventory:ItemName>Widget</inventory:ItemName>
<inventory:Description>Regular Widget</inventory:Description>
<money:UnitPrice>2.3</money:UnitPrice>
<inventory:Quantity>10</inventory:Quantity>
<money:LineTotal>23</money:LineTotal>
</OrderedItem>

DataContractSerializer - Name cannot begin with the '.' character, hexadecimal value 0x2E

This question has been asked a lot of times on SO, but neither solution helped me.
My datastruct is serialized into an XML file using dataContractSerializer. The (de)serialization code is the following:
public static void serialize<T>(T xObject, string xFilePath, string xIndent = "")
{
XmlWriterSettings xSettings = ( xIndent == "" ? new XmlWriterSettings {Indent = false } : new XmlWriterSettings { Indent = true, IndentChars = xIndent } );
using (XmlWriter xStream = XmlWriter.Create(xFilePath, xSettings))
new DataContractSerializer(typeof(T)).WriteObject(xStream, xObject);
}
public static T deserialize<T>(string xFilePath)
{
using (FileStream xStream = new FileStream(xFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
DataContractSerializer xSerializer = new DataContractSerializer(typeof(T));
return (T)xSerializer.ReadObject(xStream);
}
}
A snippet of the written XML is
<?xml version="1.0" encoding="utf-8"?>
<PxePriceListEod xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="libTypes.salesApp">
<DateOfValidity xmlns:d2p1="libTypes">
<d2p1:_x002E_>2016-09-09T00:00:00</d2p1:_x002E_>
</DateOfValidity>
<PriceRecords>
<AbstractPxePriceRecordEod i:type="PxePriceRecordEodBal">
<Product xmlns:d4p1="libTypes">
<d4p1:Ccy>
<d4p1:_x002E_>EUR</d4p1:_x002E_>
</d4p1:Ccy>
<d4p1:Commodity>
<d4p1:_x002E_>Electricity</d4p1:_x002E_>
</d4p1:Commodity>
<d4p1:Duration>
<d4p1:_x002E_>Month</d4p1:_x002E_>
</d4p1:Duration>
<d4p1:Exchange>
<d4p1:_x002E_>Pxe</d4p1:_x002E_>
</d4p1:Exchange>
<d4p1:Period>
<d4p1:_x002E_>9</d4p1:_x002E_>
</d4p1:Period>
<d4p1:Type>
<d4p1:_x002E_>Base</d4p1:_x002E_>
</d4p1:Type>
<d4p1:Year>
<d4p1:_x002E_>2016</d4p1:_x002E_>
</d4p1:Year>
</Product>
<IsDeduced xmlns:d4p1="libTypes">
<d4p1:_x002E_>false</d4p1:_x002E_>
</IsDeduced>
<IsInterpolated xmlns:d4p1="libTypes">
<d4p1:_x002E_>false</d4p1:_x002E_>
</IsInterpolated>
<IsSynthetic xmlns:d4p1="libTypes">
<d4p1:_x002E_>false</d4p1:_x002E_>
</IsSynthetic>
<Price xmlns:d4p1="libTypes">
<d4p1:_x002E_>30.45</d4p1:_x002E_>
</Price>
<DateOfValidity xmlns:d4p1="libTypes">
<d4p1:_x002E_>2016-09-09T00:00:00</d4p1:_x002E_>
</DateOfValidity>
</AbstractPxePriceRecordEod>
... more AbstractPxePriceRecordEod elements ...
</PriceRecords>
</PxePriceListEod>
Features of the problem:
The error points to Line=0, Position=1 (which does not make sense)
There is no element with name containing "."
All classes that make it into the file are properly decorated with DataContract
The XML file is checked to really be in UTF-8 encoding (when read by Notepad++) and none of the other versions of (de)serializing code listed on SO (that implicitly specify UTF-8 encoding) helped
I know the file is ugly (autogenerated element names like "d4p1:x002E" - Im the only dev in this company and unfortunately I dont have time to nicely decorate 100+ classes)
Everything was working fine for 2.5 years, the problems started today.
Any hint is much appreciated,
Daniel
UPDATE
I have added a minimal amount of code that reproduces the problem here. The application tries to read the given class from an xml file, the classes that have the problematic dataContractNames are located in library\+support\+qprimitive.
_x002E_ is how XmlConvert.EncodeLocalName() encodes the string ".". See https://dotnetfiddle.net/phUYO3 for a demonstration. So you either:
Have a data member with "." as its name.
Have implemented IXmlSerializable and are writing elements with this name.
That being said, making a data contract type with [DataMember(Name = ".")] on one of the data members does not cause problems for me. I.e. I can serialize and deserialize the following successfully:
[DataContract(Namespace = "libTypes.salesApp")]
public class PxePriceListEod
{
[DataMember]
public DateOfValidity DateOfValidity { get; set; }
}
// DateOfValidity
[DataContract(Namespace = "libTypes")]
public class DateOfValidity
{
[DataMember(Name = ".")]
public DateTime DateTime { get; set; }
}

How to deserialize a numbered array of XML elements

I need to deserialize the following XML:
<TIMEWINDOWS>
<NUMBER>10</NUMBER>
<NO0>
<FROM>22-11-2013 08:00:00</FROM>
<TO>22-11-2013 11:59:00</TO>
</NO0>
<NO1>
<FROM>22-11-2013 12:00:00</FROM>
<TO>22-11-2013 15:59:00</TO>
</NO1>
<NO2>
<FROM>23-11-2013 08:00:00</FROM>
<TO>23-11-2013 11:59:00</TO>
</NO2>
<NO3>
<FROM>23-11-2013 12:00:00</FROM>
<TO>23-11-2013 15:59:00</TO>
</NO3>
...
</TIMEWINDOWS>
The output that I require is a collection (list, array, whatever) of TimeWindow objects, for example:
public class TimeWindow
{
public string From { get; set; }
public string To { get; set; }
}
Is there a standard way to handle the NO0, NO1, NO2, ... elements?
I can always build my own parser, but I would much prefer to use a standard approach, such as System.Xml.Serialization.XmlSerializer.
You could use LINQ to XML. Something like...
XDocument doc = XDocument.Load("XMLFile1.xml");
var result = new List<TimeWindow>();
foreach (XElement s in doc.Elements().Descendants())
{
if (s.Name.ToString().StartsWith("NO"))
{
var tw = new TimeWindow {From = (string)s.Element("FROM"),
To = (string)s.Element("TO")};
result.Add(tw);
}
}
You'd probably want to add some checking for nulls around the FROM and TO elements to ensure they are present in the data.
The format of this is quite crazy. Unfortunately this means you will need to parse the xml manually with XDocument or XmlDocument. Lets use the former as it is easier:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Xmlarrayload
{
class Program
{
static void Main(string[] args)
{
var document = XDocument.Parse(#"<TIMEWINDOWS>
<NUMBER>4</NUMBER>
<NO0>
<FROM>22-11-2013 08:00:00</FROM>
<TO>22-11-2013 11:59:00</TO>
</NO0>
<NO1>
<FROM>22-11-2013 12:00:00</FROM>
<TO>22-11-2013 15:59:00</TO>
</NO1>
<NO2>
<FROM>23-11-2013 08:00:00</FROM>
<TO>23-11-2013 11:59:00</TO>
</NO2>
<NO3>
<FROM>23-11-2013 12:00:00</FROM>
<TO>23-11-2013 15:59:00</TO>
</NO3>
</TIMEWINDOWS>");
int number = int.Parse(document.Root.Element("NUMBER").Value);
TimeWindow[] windows = (TimeWindow[])Array.CreateInstance(typeof(TimeWindow), number);
for (int i = 0; i < number; i++)
{
var element = document.Root.Element(string.Format("NO{0}", i));
TimeWindow window = new TimeWindow
{
//it is extremely important to use the correct culture (invariant) to parse the dates.
To = DateTime.ParseExact(element.Element("TO").Value, "dd-MM-yyyy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat),
From = DateTime.ParseExact(element.Element("FROM").Value, "dd-MM-yyyy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat)
};
windows[i] = window;
}
}
}
public class TimeWindow
{
public DateTime From { get; set; }
public DateTime To { get; set; }
}
}
A possibility is to do the following:
public class TimeWindow
{
public int number{get;set;}
public Times NO0 = new Times();
public Times NO1 = new Times();
public Times NO2 = new Times();
public Times NO3 = new Times();
}
public class Times()
{
public string FROM{get;set;}
public string TO{get;set;}
}
And if you have this class and help class, you can simply do the following (in another class of course):
XmlSerializer serializer = new XmlSerializer(typeof(TimeWindow));
TimeWindow timeWindow = (TimeWindow)serializer.Deserialize(new StreamReader(pathToFile));
After this, you can access the (at present time "string"-formatted) data via
timeWindow.NO0.FROM;
For me, this worked some days ago.
But I just wrote it from my mind.
//EDIT
Sorry, I didn't realize that there are different numbers of "NOx"-Tags.
This example here works only, id you know the exact amount of those tags.
There is no standard way to handle elements with different names. Because your xml is not standard xml. All children of same type should have same names otherwise they considered as different elements. Additional information (like index of window) should be provided via attributes or elements of child, not via element name:
<TimeWindows number="10"> <!-- actually you don't need number attribute here -->
<TimeWindow index="1">
<From>22-11-2013 08:00:00</From>
<To>22-11-2013 11:59:00</To>
</TimeWindow>
<TimeWindow index="2">
<From>22-11-2013 12:00:00</From>
<To>22-11-2013 15:59:00</To>
</TimeWindow>
<TimeWindow index="3">
<From>23-11-2013 08:00:00</From>
<To>23-11-2013 11:59:00</To>
</TimeWindow>
</TimeWindows>
So, you should handle this manually, e.g. by filtering out <NUMBER> element and just enumerating all other elements
var xdoc = XDocument.Load(path_to_xml);
var windows = xdoc.Root.Elements().Where(e => e.Name.LocalName != "NUMBER")
.Select(n => new TimeWindow {
From = (string)n.Element("FROM"),
To = (string)n.Element("TO")
}).ToList();
Also consider to use DateTime properties in your TimeWindow class, because they hold dates.
The way that I used for solving this problem of mine was saving them to .xml files and retrieved from the .xml file. Check the below code:
private void SaveTimeWindow(TimeWindow[] time, string filePath)
{
//Open a file stream
System.IO.FileStream fs = new System.IO.FileStream(filePath, System.IO.FileMode.Create);
// Create a xml Serializer object
System.Xml.Serialization.XmlSerializer xmlSer = new System.Xml.Serialization.XmlSerializer(typeof(TimeWindow[]));
xmlSer.Serialize(fs, time);
// Close the file stream
fs.Close();
}
For loading you may use the below:
private static TimeWindow[] LoadTime(string filePath)
{
//Open the XML file
if (System.IO.File.Exists(filePath))
{
System.IO.FileStream fs = new System.IO.FileStream(filePath, System.IO.FileMode.Open);
// First create a xml Serializer object
System.Xml.Serialization.XmlSerializer xmlSer = new System.Xml.Serialization.XmlSerializer(typeof(TimeWindow[]));
// Deserialize the Matrix object
TimeWindow[] time= (TimeWindow[])xmlSer.Deserialize(fs);
// Close the file stream
fs.Close();
return time;
}
else
{
return null;
}
}
Then you may save your XML based on:
SaveTimeWindow(TimeWindow, yourPath);
and Load it based on:
TimeWindow[] t = LoadTime(yourPath);

Deserializing child nodes outside of parent's namespace using XmlSerializer.Deserialize() in C#

I have an application that uses namespaces to help deserialize objects stored in XML. The XML namespace is also the C# namespace where the object resides. For example, given the following XML snip:
<xml-config xmlns:app="MyAppNS">
<app:Person>
<Name>Bill</Name>
<Car>
<Make>Honda</Make>
<Model>Accord</Model>
</Car>
</app:Person>
<app:Person>
<Name>Jane</Name>
<Car>
<Make>VW</Make>
<Model>Jetta</Model>
</Car>
</app:Person>
<app:Car>
<Make>Audi</Make>
<Model>A6</Model>
</app:Car>
</xml-config>
The configuration is really just a random bag of objects. As you can see, there is a mix of Person and Car objects at the top level. I use the namespace to determine the object type at load time for proper deserialization. Here is the code for loading the document:
public static void LoadFile(String file) {
XmlDocument doc = new XmlDocument();
doc.Load(file);
XmlNode root = doc.DocumentElement;
foreach (XmlNode child in root.ChildNodes) {
if (child.NodeType != XmlNodeType.Element) {
continue;
}
Object obj = LoadObject(child);
// TODO handle object here...
}
}
private static Object LoadObject(XmlNode node) {
Object obj = null;
StringBuilder str = new StringBuilder();
if ((node.Prefix != null) && (node.Prefix != "")) {
str.Append(node.NamespaceURI).Append('.');
}
str.Append(node.LocalName);
Assembly assy = Assembly.GetExecutingAssembly();
Type type = assy.GetType(str.ToString());
XmlSerializer serdes = new XmlSerializer(type);
using (XmlNodeReader reader = new XmlNodeReader(node)) {
obj = serdes.Deserialize(reader);
}
return obj;
}
You many see the issue right away, but when this code is used, the resulting Person objects are empty, however there are no errors. The only way to get the deserializer to populate the child elements is to use the same namespace for the child elements:
<app:Person>
<app:Name>Bill</app:Name>
<app:Car>
<app:Make>Honda</app:Make>
<app:Model>Accord</app:Model>
</app:Car>
</app:Person>
The only other option I have tried is to use fully-qualified type names as the element name (no namespaces), however I haven't had any luck with that. Is there a way to cause the deserializer to accept the non-prefixed children of the XML node?
I finally found a method to accomplish this, modifying the answer found here.
I created a custom deserializer that replicates the namespace for the starting node:
public class CustomXmlNodeReader : XmlNodeReader {
private String _namespace;
public CustomXmlNodeReader(XmlNode node) : base(node) {
_namespace = node.NamespaceURI;
}
public override String NamespaceURI {
get { return _namespace; }
}
}
Using this reader, I am able to load stored objects with the following:
using (XmlNodeReader reader = new CustomXmlNodeReader(node)) {
obj = serdes.Deserialize(reader);
}
I know this is a bit bad form for XML (forcing the application of a specific namespace), however it suits my needs quite nicely. Answered here in case it helps anyone else.

HR-XML Error, trying to deserialize XML examples

The HR-XML 3.0 spec provides WSDL's to generate their entities. I'm trying to deserialize their example xml in their documentation, but it's not working.
Candidate.CandidateType candidate = null;
string path = "c:\\candidate.xml";
XmlSerializer serializer = new XmlSerializer(typeof(Candidate.CandidateType), "http://www.hr-xml.org/3");
StreamReader reader = null;
reader = new StreamReader(path);
candidate = (Candidate.CandidateType)serializer.Deserialize(reader);
The error I'm getting:
"<Candidate xmlns='http://www.hr-xml.org/3'> was not expected."
Any suggestions?
Update: I tried XmlSerializing a CandidatePerson element and it looks like it uses CandidatePersonType instead of CandidatePerson. I think I'm doing something wrong here though...
first lines of Candidate.CandidateType (all auto-generated):
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.3082")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.hr-xml.org/3")]
public partial class CandidateType {
private IdentifierType2 documentIDField;
private IdentifierType2[] alternateDocumentIDField;
private string documentSequenceField;
The following is more of a comment, but it's too long, so I'll put it here.
The CandidateType class is properly decorated with the XmlType attribute. That is an attribute that applies to types, and determines how the type will be emitted in any generated XML Schema. It has nothing to do with the namespace on an element that happens to have the same type.
Consider the following C# code:
public class CandidateType {}
public class Foo
{
CandidateType _candidate1;
CandidateType _candidate2;
}
Note that you can have multiple variables of the same type. In the same way, you could have:
<xs:element name="Candidate1" type="hrx:CandidateType"/>
<xs:element name="Candidate2" type="hrx:CandidateType"/>
These are two elements which will validate against the same type definition, but which are otherwise unrelated. If they are in the same XML Schema, then they will be in the same namespace. But what if they're not? Then you could have an instance document like:
<ns1:Candidate1 xmlns:ns1="namespace1" xmlns="http://www.hr-xml.org/3"> ... </ns1:Candidate1>
<ns2:Candidate2 xmlns:ns2="namespace2" xmlns="http://www.hr-xml.org/3"> ... </ns1:Candidate2>
What you need to do is specify the namespace of the Candidate element to the XML Serializer. The fact that the CandidateType type is in a particular namespace does not determine the namespace of the Candidate element.
Muahaha I figured it out finally!
John Saunders was right, I needed to specify the default namespace in the XmlSerializer, but in addition to that I have to specify the XmlRootAttribute because the Class that I'm trying to de-serialize to does not have the same name as the root element.
Here is my code for de-serializing the HR-XML ProcessCandidate example:
protected static ImportTest.CandidateService.ProcessCandidateType DeserializeProcessCandidate(string path)
{
CandidateService.ProcessCandidateType processCandidate = null;
XmlRootAttribute root = new XmlRootAttribute("ProcessCandidate");
XmlSerializer serializer = new XmlSerializer(typeof(CandidateService.ProcessCandidateType), new XmlAttributeOverrides(), new Type[0], root, "http://www.hr-xml.org/3");
StreamReader reader = null;
try
{
reader = new StreamReader(path);
processCandidate = (CandidateService.ProcessCandidateType)serializer.Deserialize(reader);
reader.Close();
}
catch (Exception ex)
{
reader.Close();
throw (new Exception(ex.InnerException.Message));
}
return processCandidate;
}
Thanks for the help John!

Categories

Resources