XML Deserialization - c#

I have the following xml file.
<a>
<b>
<c>val1</c>
<d>val2</d>
</b>
<b>
<c>val3</c>
<d>val4</d>
</b>
<a>
I want to deserialize this into a class and I want to access them with the objects of the class created. I am using C#. I am able to deserialize and get the value into the object of class ‘a’ (the <a> tag). but how to access the value of <b> from this object?
I did the following coding:
[Serializable()]
[XmlRoot("a")]
public class a
{
[XmlArray("a")]
[XmlArrayItem("b", typeof(b))]
public b[] bb{ get; set; }
}
[Serializable()]
public class b
{
[XmlElement("c")]
public string c{ get; set; }
[XmlElement("d")]
public string d{ get; set; }
}
class Program
{
static void Main(string[] args)
{
a i = null;
string path = "test.xml";
XmlSerializer serializer = new XmlSerializer(typeof(a));
StreamReader reader = new StreamReader(path);
i = (a)serializer.Deserialize(reader);
reader.Close();
//i want to print all b tags here
Console.Read();
}
}

For this to work you can make the following change
public class a
{
[XmlElement("b")]
public b[] bb{ get; set; }
}
By using the XmlElement attribute on the array, you are essentially telling the serializer that the array elements should be serialize/deserialized as direct child elements of the current element.
Here is a working example, I put the XML in a string just to make the example self contained.
using System;
using System.IO;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string xml =
#"<a>
<b>
<c>val1</c>
<d>val2</d>
</b>
<b>
<c>val3</c>
<d>val4</d>
</b>
</a>";
XmlSerializer xs = new XmlSerializer(typeof(a));
a i = (a)xs.Deserialize(new StringReader(xml));
if (i != null && i.bb != null && i.bb.Length > 0)
{
Console.WriteLine(i.bb[0].c);
}
else
{
Console.WriteLine("Something went wrong!");
}
Console.ReadKey();
}
}
[XmlRoot("a")]
public class a
{
[XmlElement("b")]
public b[] bb { get; set; }
}
public class b
{
[XmlElement("c")]
public string c { get; set; }
[XmlElement("d")]
public string d { get; set; }
}
}

When in doubt with creating your xml serialization classes, i find the easiest way to solve the problem is to:
dump all your dummy data into an XML file
run xsd.exe to create a .xsd schema file
run xsd.exe on your schema file to create a class file
i wrote a quick tutorial on it in a blog post a while ago:
http://www.diaryofaninja.com/blog/2010/05/07/make-your-xml-stronglytyped-because-you-can-and-its-easy
it takes less than a minute and you can then easily tweak things from there. XSD.exe is your friend

I find the easiest way to solve the problem is to change your definition of classes a and b to the following
public class b {
public string c { get; set; }
public string d { get; set; }
}
[XmlRoot(Namespace="", ElementName="a")]
public class a : List<b> { }
then your program will work. Optionally you can add to the class b the attribute [XmlRoot (Namespace = "", ElementName = "b")]

class Program
{
static void Main(string[] args)
{
string employeedata = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><tag><name>test</bar></nmae>";//demo xml data
using (TextReader sr = new StringReader(employeedata))
{
XmlSerializer serializer = new XmlSerializer(typeof(Employee));//pass type name in XmlSerializer constructor here
Employee response = (Employee)serializer.Deserialize(sr);
Console.WriteLine(response.name);
}
}
}
[System.Xml.Serialization.XmlRoot("tag")]
public class Employee
{
public string name { get; set; }
}

Change if block (from Chris Tyler's answer) like below.
if (i != null && i.bb != null && i.bb.Length > 0)
{
foreach (b t in i.bb)
{
Console.WriteLine(t.c);
Console.WriteLine(t.d);
}
}
then it will give you below result:
val1
val2
val3
val4

Related

How to deserialize a given xml document [duplicate]

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"

XmlSerializer deserializing list with different element names

I am trying to parse XML that has an element that looks like the following using an XmlSerializer. There are quite a few currency types under the amount element and I'd like to deserialize them into a collection of objects that have a string property holding the currency type and an integer property holding the amount.
Is there any clean way of doing this without having to custom parse the amount. I'd like to just apply the XmlSerializer attributes to my classes and get something that works.
I have no control over the output XML.
<root>
<property1>a</property1>
<property1>b</property1>
<property1>c</property1>
<amount>
<EUR type="integer">1000</EUR>
<USD type="integer">1100</USD>
</amount>
<root>
The best way to attack XML deserialization is to start with serialization. To that end, here are some classes with attributes to control XML serialization:
public sealed class root
{
[XmlElement("property1")]
public List<string> property1;
[XmlArrayItem(Type = typeof(EUR))]
[XmlArrayItem(Type = typeof(USD))]
public List<amount> amount;
}
public abstract class amount
{
[XmlAttribute]
public string type { get; set; }
[XmlText]
public string Value { get; set; }
}
public sealed class EUR : amount { }
public sealed class USD : amount { }
Test code is:
var root = new root { property1 = new List<string>(), amount = new List<amount>() };
root.property1.AddRange(new[]{ "a", "b", "c"});
var eur = new EUR { type = "integer", Value = "1000" };
var usd = new USD { type = "integer", Value = "1100" };
root.amount.AddRange(new amount[]{ eur, usd});
which generates the following XML:
<?xml version="1.0" encoding="utf-16"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<property1>a</property1>
<property1>b</property1>
<property1>c</property1>
<amount>
<EUR type="integer">1000</EUR>
<USD type="integer">1100</USD>
</amount>
</root>
I think your best bet will be to do partial XML parsing, and keep the content of the <amount> element as a collection of XmlElements. You'll still have to parse it manually, but you'll only have to parse that part manually. Example:
[XmlRoot("root")]
public class RecordInfo
{
[XmlElement("property1")]
public List<string> Property1;
[XmlElement("amount")]
public AmountRawData AmountData;
}
public class AmountRawData
{
[XmlAnyElement]
public List<XmlElement> Content;
public IEnumerable<AmountInfo> Parse()
{
foreach (var element in this.Content)
{
yield return
new AmountInfo()
{
Currency = element.LocalName,
Type = element.GetAttribute("type"),
Amount = element.InnerText,
};
}
}
}
public class AmountInfo
{
public string Currency;
public string Type;
public string Amount;
}
Example usage:
var serializer = new XmlSerializer(typeof(RecordInfo));
var result = (RecordInfo)serializer.Deserialize(dataStream);
foreach (var amount in result.AmountData.Parse())
Console.WriteLine($"{amount.Currency} {amount.Type} {amount.Amount}");
Answered a similar question last week using xml linq:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Elements().Select(x => new {
property1 = x.Elements("property1").Select(y => (string)y).ToList(),
dictCurrency = x.Elements("amount").Elements().GroupBy(y => y.Name.LocalName, z => (int)z)
.ToDictionary(y => y.Key, z => z.FirstOrDefault())
}).FirstOrDefault();
}
}
}
Here is one other approach using Cinchoo ETL - an open source library, to de-serialize such XML as below
Define POCO object
[ChoXmlRecordObject]
public class Root
{
[ChoXmlNodeRecordField(XPath = "//property1")]
public string[] Properties { get; set; }
[ChoXmlNodeRecordField(XPath = "//amount/*")]
public double[] Amounts { get; set; }
}
Deserialize it using xml reader object
var rec = ChoXmlReader.Deserialize<Root>("*** Xml File Path ***").FirstOrDefault();
Console.WriteLine(rec.Dump());
Disclaimer: I'm the author of this library.

Have both DataType and additional Attribute during Xml Serialization

I am attempting to create an XML document through the application of attributes on fields/properties ([XmlAttribute], [XmlElement], etc.) My problem is that I have a requirement that I attach an additional attribute to a primitive datatype in the style of:
<document xmlns:dt="urn:schemas-microsoft-com:datatypes" >
<binary addAttribute="X" dt:dt="bin.base64">
[... binary ...]
</binary>
</document>
I'm making use of code like the following:
[Serializable]
public class Document {
[XmlElement]
public BinaryObject Binary { get; set; }
}
[Serializable]
public class BinaryObject {
[XmlText(DataType = "base64Binary")]
public byte[] Binary { get; set; }
[XmlAttribute]
public int AddAttribute { get; set; }
}
public class XmlExample {
public static void Main(string[] args)
{
Document document = new Document();
document.Binary = new BinaryObject();
document.Binary.Binary = File.ReadAllBytes(#"FileName");
document.Binary.AddAttribute = 0;
XmlSerializer serializer = new XmlSerializer(typeof(Document));
serializer.Serialize(Console.Out, document);
Console.ReadLine();
}
}
This, however, provides the following output:
<document>
<binary addAttribute="X">
[... binary ...]
</binary>
</document>
If I attempt to move the byte[] Binary to the Document class instead I can get the xmlns:dt="..." as expected but I cannot attach the arbitrary addAttribute when I do so (unless I missed something obvious.) This was incorrect; I misread something in the XML that I was getting out of the XML. The xmlns:dt element was not added in this case.
The question is: Can I do this (have both the DataType and the addAttribute) exclusively through C# attributes?
The answer to this question came partially from here: XmlSerializer attribute namespace for element type dt:dt namespace. The DataType = "base64Binary" does not apply the xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="bin.base64" to the element that it is attached to. An attribute has to be added to the BinaryObject that provides the dt:dt = "bin.base64" with the correct name space.
Final Code
[Serializable]
public class Document {
public BinaryObject Binary { get; set; }
}
[Serializable]
public class BinaryObject {
[XmlText]
public byte[] Binary { get; set; }
[XmlAttribute]
public int AddAttribute { get; set; }
// Adds the dt:dt object to the correct name space.
[XmlAttribute("dt", Namespace = "urn:schemas-microsoft-com:datatypes")]
public string DataType { get; set; }
public BinaryObject() { DataType = "bin.base64"; }
}
public class XmlExample {
public static void Main(string[] args)
{
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
// Adds the needed namespace to the document.
namespaces.Add("dt", "urn:schemas-microsoft-com:datatypes");
Document document = new Document();
document.Binary = new BinaryObject();
document.Binary.Binary = new byte[]{0,1,2,3,4,5,6,7,8,9};
document.Binary.AddAttribute = 0;
XmlSerializer serializer = new XmlSerializer(typeof(Document));
serializer.Serialize(Console.Out, document, namespaces);
Console.ReadLine();
}
}
Final Output
<Document xmlns:dt="urn:schemas-microsoft-com:datatypes">
<Binary AddAttribute="0" dt:dt="bin.base64">AAECAwQFBgcICQ==</Binary>
</Document>
Try this
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
}
}
[XmlRoot("Document")]
public class Document
{
[XmlText(DataType = "base64Binary")]
public byte[] Binary { get; set; }
[XmlAttribute]
public int AddAttribute { get; set; }
}
}
​

C# XML Deserialize object is empty

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.

Deserialize child elements using xmlserializer

I'm trying to deserialize a xml file of this strucuture, but when I call this method
XmlSerializer(responseType).Deserialize(new MemoryStream(responseData))
none of the PricingQuote Child elements come through
<Pricing>
<Code>Success</Code>
<PricingQuotes>
<PricingQuote>
<ProductName>Conforming 30 Year Fixed</ProductName>
</PricingQuote>
<PricingQuote>
<ProductName>Conforming 20 Year Fixed</ProductName>
</PricingQuote>
</PricingQuotes>
</Pricing>
You need to make sure that your class definitions match the incoming XML. The ones below do that, and the deserialization works as expected.
public class StackOverflow_12608671
{
const string XML = #"<Pricing>
<Code>Success</Code>
<PricingQuotes>
<PricingQuote>
<ProductName>Conforming 30 Year Fixed</ProductName>
</PricingQuote>
<PricingQuote>
<ProductName>Conforming 20 Year Fixed</ProductName>
</PricingQuote>
</PricingQuotes>
</Pricing> ";
public class Pricing
{
public string Code { get; set; }
public List<PricingQuote> PricingQuotes { get; set; }
}
public class PricingQuote
{
public string ProductName { get; set; }
}
public static void Test()
{
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(XML));
XmlSerializer xs = new XmlSerializer(typeof(Pricing));
Pricing p = (Pricing)xs.Deserialize(ms);
foreach (var q in p.PricingQuotes)
{
Console.WriteLine(q.ProductName);
}
}
}

Categories