I am having trouble with XML deserialization.
In a nutshell -
I have 2 classes:
SMSMessage
SMSSendingResponse
I call an API that takes a bunch of parameters (represented by SMSMessage class)
It returns an XML response.
The response looks like this:
<?xml version="1.0" encoding="utf-8"?>
<data>
<status>1</status>
<message>OK</message>
<results>
<result>
<account>12345</account>
<to>012345678</to>
<from>054321</from>
<message>Testing</message>
<flash></flash>
<replace></replace>
<report></report>
<concat></concat>
<id>f8d3eea1cbf6771a4bb02af3fb15253e</id>
</result>
</results>
</data>
Here is the SMSMessage class (with the xml serialization attributes so far)
using System.Xml.Serialization;
namespace XMLSerializationHelp
{
[XmlRoot("results")]
public class SMSMessage
{
public string To
{
get
{
return Result.To;
}
}
public string From
{
get
{
return Result.From;
}
}
public string Message
{
get
{
return Result.Message;
}
}
[XmlElement("result")]
public Result Result { get; set; }
}
}
Here is SMSMessageSendingResponse:
using System.Xml.Serialization;
namespace XMLSerializationHelp
{
[XmlRoot("data")]
public class SMSSendingResponse
{
//should come from the results/result/account element. in our example "12345"
public string AccountNumber
{
get
{
return SMSMessage.Result.AccountNumber;
}
}
//should come from the "status" xml element
[XmlElement("status")]
public string Status { get; set; }
//should come from the "message" xml element (in our example - "OK")
[XmlElement("message")]
public string Message { get; set; }
//should come from the "id" xml element (in our example - "f8d3eea1cbf6771a4bb02af3fb15253e")
public string ResponseID
{
get
{
return SMSMessage.Result.ResponseID;
}
}
//should be created from the results/result element - ignore flash, replace, report and concat elements for now.
[XmlElement("results")]
public SMSMessage SMSMessage { get; set; }
}
}
Here is the other class (Result) - I want to get rid of this, so only the 2 previously mentioned classes remain
using System.Xml.Serialization;
namespace XMLSerializationHelp
{
[XmlRoot("result")]
public class Result
{
[XmlElement("account")]
public string AccountNumber{ get; set; }
[XmlElement("to")]
public string To { get; set; }
[XmlElement("from")]
public string From { get; set; }
[XmlElement("message")]
public string Message { get; set; }
[XmlElement("id")]
public string ResponseID { get; set; }
}
}
I don't want SMSMessage to be aware of the SMSSendingResponse - as this will be handled by a different part of my application
I hope this helps. The XML structure implies the <result> element can occur more than once, so see if this helps you achieve what you need:
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Xml;
namespace XMLSerializationHelp
{
class Program
{
static void Main(string[] args)
{
string strXML = #"<?xml version=""1.0"" encoding=""utf-8""?>
<data>
<status>1</status>
<message>OK</message>
<results>
<result>
<account>12345</account>
<to>012345678</to>
<from>054321</from>
<message>Testing</message>
<flash></flash>
<replace></replace>
<report></report>
<concat></concat>
<id>f8d3eea1cbf6771a4bb02af3fb15253e</id>
</result>
</results>
</data>";
XmlSerializer serializer = new XmlSerializer(typeof(SMSSendingResponse));
SMSSendingResponse obj = (SMSSendingResponse)serializer.Deserialize(new XmlTextReader(strXML, XmlNodeType.Document, null));
Console.WriteLine("Status: {0}", obj.Status);
Console.WriteLine("Message: {0}", obj.Message);
Console.WriteLine("Account Number: {0}", obj.AccountNumber);
Console.WriteLine("ResponseID: {0}", obj.ResponseID);
Console.WriteLine("To: {0}", obj.To);
Console.WriteLine("From: {0}", obj.From);
Console.WriteLine("ResultMessage: {0}", obj.ResultMessage);
Console.ReadLine();
}
}
[Serializable]
[XmlRoot("data")]
public class SMSSendingResponse
{
public SMSSendingResponse() {}
//should come from the "status" xml element
[XmlElement("status")]
public string Status { get; set; }
//should come from the "message" xml element (in our example - "OK")
[XmlElement("message")]
public string Message { get; set; }
//should come from the results/result/account element. in our example "12345"
[XmlIgnore()]
public string AccountNumber
{
get
{
Result r = FirstResult;
return (r != null) ? r.AccountNumber : null;
}
}
//should come from the "id" xml element (in our example - "f8d3eea1cbf6771a4bb02af3fb15253e")
[XmlIgnore()]
public string ResponseID
{
get
{
Result r = FirstResult;
return (r != null) ? r.ResponseID : null;
}
}
[XmlIgnore()]
public string To
{
get
{
Result r = FirstResult;
return (r != null) ? r.To : null;
}
}
[XmlIgnore()]
public string From
{
get
{
Result r = FirstResult;
return (r != null) ? r.From : null;
}
}
[XmlIgnore()]
public string ResultMessage
{
get
{
Result r = FirstResult;
return (r != null) ? r.Message : null;
}
}
[XmlArray("results"), XmlArrayItem("result", typeof(Result))]
public List<Result> Results
{
get { return (_Results); }
set { _Results = value; }
} private List<Result> _Results = new List<Result>();
[XmlIgnore()]
public Result FirstResult
{
get
{
return (_Results != null && _Results.Count > 0) ? _Results[0] : null;
}
}
}
[XmlType(TypeName = "result"), Serializable]
public class Result
{
public Result() {}
[XmlElement("account")]
public string AccountNumber { get; set; }
[XmlElement("to")]
public string To { get; set; }
[XmlElement("from")]
public string From { get; set; }
[XmlElement("message")]
public string Message { get; set; }
[XmlElement("id")]
public string ResponseID { get; set; }
}
}
Related
I have a POCO like this:
public class Process
{
public Process() { }
[DataMember(Name = "lang_code")]
public string LCode { get; set; }
[DataMember(Name = "data_currency")]
public string Currency { get; set; }
[DataMember(Name = "country_code")]
public string CCode { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
Now when I serialize my POCO I get json back like this which has field name:
{"LCode":"en-US","Currency":"USD","CCode":"IN"}
Is there any way to get it the way DataMember fields are after serializing POCO. Something like below:
{"lang_code":"en-US","data_currency":"USD","country_code":"IN"}
Below is the code we have:
ProcessStr = ExtractHeader(headers, PROCESS_HEADER);
Console.WriteLine(ProcessStr);
if (!string.IsNullOrWhiteSpace(ProcessStr))
{
Process = DeserializeJson<Process>(ProcessStr);
if (Process != null && !string.IsNullOrWhiteSpace(Process.Gold))
{
Process.Gold = HttpUtility.HtmlEncode(Process.Gold);
}
ProcessStr = Process.ToString();
Console.WriteLine(ProcessStr);
}
private T DeserializeJson<T>(string str) where T : new()
{
try
{
return Utf8Json.JsonSerializer.Deserialize<T>(str);
}
catch (Exception e)
{
return new T();
}
}
It looks like you are using two different packages, Newtonsoft.Json to serialize and Utf8Json to deserialize. They use different annotations. You can get it to work, but it might be simpler to choose one or the other.
Newtonsoft.Json uses the JsonProperty attribute whereas Utf8Json uses the DataMember one.
using System;
using System.Diagnostics;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Utf8Json;
namespace JSONPropertyTest
{
public class Process
{
public Process() { }
[JsonProperty("lang_code")]
[DataMember(Name = "lang_code")]
public string LCode { get; set; }
[JsonProperty("data_currency")]
[DataMember(Name = "data_currency")]
public string Currency { get; set; }
[JsonProperty("country_code")]
[DataMember(Name = "country_code")]
public string CCode { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
class Program
{
static private T DeserializeJson<T>(string str) where T : new()
{
try
{
return Utf8Json.JsonSerializer.Deserialize<T>(str);
}
catch (Exception e)
{
return new T();
}
}
static void Main(string[] args)
{
var test = new Process { LCode = "en-US",Currency = "USD", CCode = "IN" };
var json = test.ToString();
Console.WriteLine($"serialized={test}");
var deserialized = DeserializeJson<Process>(json);
Debug.Assert(test.CCode == deserialized.CCode);
Debug.Assert(test.LCode == deserialized.LCode);
Debug.Assert(test.Currency == deserialized.Currency);
Console.WriteLine($"deserialized={deserialized}");
}
}
}
To just use Utf8Json you need to update your ToString method, which is the only one in the code you've shown that relies on Newtonsoft.Json. That would look like this:
public class Process
{
public Process() { }
[DataMember(Name = "lang_code")]
public string LCode { get; set; }
[DataMember(Name = "data_currency")]
public string Currency { get; set; }
[DataMember(Name = "country_code")]
public string CCode { get; set; }
public override string ToString()
{
return Utf8Json.JsonSerializer.ToJsonString(this);
}
}
I see this question often enough, but nobody's title really seems to depict their question. I get a large response object back from a Web API that contains general response information, along with the data object I want to deserialize.
Full XML:
<?xml version="1.0"?>
<root>
<status>
<apiErrorCode>0</apiErrorCode>
<apiErrorMessage/>
<dbErrorCode>0</dbErrorCode>
<dbErrorMessage/>
<dbErrorList/>
</status>
<data>
<modelName>ReportXDTO</modelName>
<modelData>
<id>1780</id>
<reportTitle>Access Level (select) with Door Assignment</reportTitle>
<hasParameters>true</hasParameters>
<parameters>
<dataType>STRING</dataType>
<title>Access Level:</title>
<index>1</index>
<allowMulti>true</allowMulti>
<selectSql>SELECT DISTINCT [Name] FROM dbo.[Levels] WHERE [PrecisionFlag] = '0' ORDER BY [Name] </selectSql>
<values>
<value>Door 1</value>
<used>1</used>
</values>
<values>
<value>Door 2</value>
<used>1</used>
</values>
<values>
<value>Door 3</value>
<used>1</used>
</values>
</parameters>
<sourceSql>SELECT [Name], [SData] FROM [Schedules]</sourceSql>
<report/>
</modelData>
<itemReturned>1</itemReturned>
<itemTotal>1</itemTotal>
</data>
<listInfo>
<pageIdRequested>1</pageIdRequested>
<pageIdCurrent>1</pageIdCurrent>
<pageIdFirst>1</pageIdFirst>
<pageIdPrev>1</pageIdPrev>
<pageIdNext>1</pageIdNext>
<pageIdLast>1</pageIdLast>
<itemRequested>1</itemRequested>
<itemReturned>1</itemReturned>
<itemStart>1</itemStart>
<itemEnd>1</itemEnd>
<itemTotal>1</itemTotal>
</listInfo>
</root>
I only want to deserialize the modelData element. The modelData object type is dynamic, depending on the API call.
I deserialize xml in other applications, and created the following method, but don't know how to specifically ONLY get the modelData element:
public static T ConvertXmltoClass<T>(HttpResponseMessage http, string elementName) where T : new()
{
var newClass = new T();
try
{
var doc = JsonConvert.DeserializeXmlNode(http.Content.ReadAsStringAsync().Result, "root");
XmlReader reader = new XmlNodeReader(doc);
reader.ReadToFollowing(elementName);
//The xml needs to show the proper object name
var xml = reader.ReadOuterXml().Replace(elementName, newClass.GetType().Name);
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
{
var serializer = new XmlSerializer(typeof(T));
newClass = (T)serializer.Deserialize(stream);
}
}
catch (Exception e)
{
AppLog.LogException(System.Reflection.MethodBase.GetCurrentMethod().Name, e);
}
return newClass;
}
I have updated this thread multiple times now, to stay current. I started updating it with the first solution. But that solution by itself didn't solve the problem. With the code how it is right now, I get no exceptions, but don't get the xml deserialized to my object. Instead I get a new, blank object. Thoughts?
THOUGH the object type can change, here is my current object I am dealing with: (PLEASE NOTE, that I deserialize the exact xml in modelData, in the Web API)
namespace WebApiCommon.DataObjects
{
[Serializable]
public class ReportXDto
{
public ReportXDto()
{
Parameters = new List<ReportParameterXDto>();
}
public int Id { get; set; }
public string ReportTitle { get; set; }
public bool HasParameters { get; set; } = false;
public List<ReportParameterXDto> Parameters { get; set; }
public string SourceSql { get; set; }
public DataTable Report { get; set; }
}
[Serializable]
public class ReportXDto
{
public ReportXDto()
{
Parameters = new List<ReportParameterXDto>();
}
public int Id { get; set; }
public string ReportTitle { get; set; }
public bool HasParameters { get; set; } = false;
public List<ReportParameterXDto> Parameters { get; set; }
public string SourceSql { get; set; }
public DataTable Report { get; set; }
}
[Serializable]
public class ReportParameterValuesXDto
{
public string Value { get; set; } = "";
public bool Used { get; set; } = false;
}
}
Firstly, XmlSerializer is case sensitive. Thus your property names need to match the XML element names exactly -- unless overridden with an attribute that controls XML serialization such as [XmlElement(ElementName="id")]. To generate a data model with the correct casing I used http://xmltocsharp.azurewebsites.net/ which resulted in:
public class ReportParameterValuesXDto
{
[XmlElement(ElementName="value")]
public string Value { get; set; }
[XmlElement(ElementName="used")]
public string Used { get; set; }
}
public class ReportParametersXDto
{
[XmlElement(ElementName="dataType")]
public string DataType { get; set; }
[XmlElement(ElementName="title")]
public string Title { get; set; }
[XmlElement(ElementName="index")]
public string Index { get; set; }
[XmlElement(ElementName="allowMulti")]
public string AllowMulti { get; set; }
[XmlElement(ElementName="selectSql")]
public string SelectSql { get; set; }
[XmlElement(ElementName="values")]
public List<ReportParameterValuesXDto> Values { get; set; }
}
public class ReportXDto
{
[XmlElement(ElementName="id")]
public string Id { get; set; }
[XmlElement(ElementName="reportTitle")]
public string ReportTitle { get; set; }
[XmlElement(ElementName="hasParameters")]
public string HasParameters { get; set; }
[XmlElement(ElementName="parameters")]
public ReportParametersXDto Parameters { get; set; }
[XmlElement(ElementName="sourceSql")]
public string SourceSql { get; set; }
[XmlElement(ElementName="report")]
public string Report { get; set; }
}
(After generating the model, I modified the class names to match your naming convention.)
Given the correct data model, you can deserialize directly from a selected XmlNode using an XmlNodeReader as shown in How to deserialize a node in a large document using XmlSerializer without having to re-serialize to an intermediate XML string. The following extension method does the trick:
public static partial class XmlExtensions
{
public static IEnumerable<T> DeserializeElements<T>(this XmlNode root, string localName, string namespaceUri)
{
return new XmlNodeReader(root).DeserializeElements<T>(localName, namespaceUri);
}
public static IEnumerable<T> DeserializeElements<T>(this XmlReader reader, string localName, string namespaceUri)
{
var serializer = XmlSerializerFactory.Create(typeof(T), localName, namespaceUri);
while (!reader.EOF)
{
if (!(reader.NodeType == XmlNodeType.Element && reader.LocalName == localName && reader.NamespaceURI == namespaceUri))
reader.ReadToFollowing(localName, namespaceUri);
if (!reader.EOF)
{
yield return (T)serializer.Deserialize(reader);
// Note that the serializer will advance the reader past the end of the node
}
}
}
}
public static class XmlSerializerFactory
{
// To avoid a memory leak the serializer must be cached.
// https://stackoverflow.com/questions/23897145/memory-leak-using-streamreader-and-xmlserializer
// This factory taken from
// https://stackoverflow.com/questions/34128757/wrap-properties-with-cdata-section-xml-serialization-c-sharp/34138648#34138648
readonly static Dictionary<Tuple<Type, string, string>, XmlSerializer> cache;
readonly static object padlock;
static XmlSerializerFactory()
{
padlock = new object();
cache = new Dictionary<Tuple<Type, string, string>, XmlSerializer>();
}
public static XmlSerializer Create(Type serializedType, string rootName, string rootNamespace)
{
if (serializedType == null)
throw new ArgumentNullException();
if (rootName == null && rootNamespace == null)
return new XmlSerializer(serializedType);
lock (padlock)
{
XmlSerializer serializer;
var key = Tuple.Create(serializedType, rootName, rootNamespace);
if (!cache.TryGetValue(key, out serializer))
cache[key] = serializer = new XmlSerializer(serializedType, new XmlRootAttribute { ElementName = rootName, Namespace = rootNamespace });
return serializer;
}
}
}
Then you would deserialize as follows:
var modelData = doc.DeserializeElements<ReportXDto>("modelData", "").FirstOrDefault();
Working sample .Net fiddle here.
For Huge xml files always use XmlReader so you do not get an out of memory issue. See code below to get the element as a string :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
//or Create(Stream)
XmlReader reader = XmlReader.Create(FILENAME);
reader.ReadToFollowing("modelData");
if (!reader.EOF)
{
string modelDataStr = reader.ReadOuterXml();
}
}
}
}
I am trying to create a function to parse an XML file like this:
<?xml version="1.0" encoding="utf-8"?>
<list name="Grocery List" author="Ian" desc="Saturday grocery list">
<item color="black" done="false">Milk</item>
<item color="black" done="false">Eggs</item>
<item color="blue" done="false">Water</item>
</list>
It parses the attributes correctly, but it fails to return the values of the list items. Here is the function and class it uses:
class List
{
public string[] listItems;
public string[] colorArray;
public string[] doneArray;
public string listName;
public string listAuthor;
public string listDesc;
public string err;
}
Reader definition:
class ListReader
{
public List doListParse(string filename)
{
List l = new List();
int arrayCount = 0;
try
{
XmlReader r = XmlReader.Create(filename);
while (r.Read())
{
if (r.NodeType == XmlNodeType.Element && r.Name == "list")
{
//Get the attributes of the list
l.listName = r.GetAttribute("name");
l.listAuthor = r.GetAttribute("author");
l.listDesc = r.GetAttribute("desc");
while (r.NodeType != XmlNodeType.EndElement)
{
r.Read();
if (r.Name == "item")
{
r.Read();
if (r.NodeType == XmlNodeType.Text)
{
//Get The Attributes
l.colorArray[arrayCount] = r.GetAttribute("color");
l.doneArray[arrayCount] = r.GetAttribute("done");
//Get The Content
l.listItems[arrayCount] = r.Value.ToString();
arrayCount++;
}
r.Read();
}
}
}
}
}
catch (Exception e)
{
l.err = e.ToString();
}
return l;
}
}
When I execute the program, it gives this exception:
System.NullReferenceException: Object reference not set to an instance of an object.
What is going on here?
I'd recommend you using a serializer. The XmlSerializer class is pretty decent. It will simplify your code.
So start by defining the models that will map to this XML structure:
[XmlRoot("list")]
public class GroceryList
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlAttribute("author")]
public string Author { get; set; }
[XmlAttribute("desc")]
public string Description { get; set; }
[XmlElement("item")]
public Item[] Items { get; set; }
}
public class Item
{
[XmlAttribute("color")]
public string Color { get; set; }
[XmlAttribute("done")]
public bool Done { get; set; }
[XmlText]
public string Value { get; set; }
}
and then simply deserialize the XML:
class Program
{
static void Main()
{
var serializer = new XmlSerializer(typeof(GroceryList));
using (var reader = XmlReader.Create("groceriesList.xml"))
{
var list = (GroceryList)serializer.Deserialize(reader);
// you could access the list items here
}
}
}
You can use Linq To Xml.
var xElem = XDocument.Parse(xml).Element("list"); //or XDocument.Load(filename)
var list = new
{
Name = xElem.Attribute("name").Value,
Author = xElem.Attribute("author").Value,
Desc = xElem.Attribute("desc").Value,
Items = xElem.Elements("item")
.Select(e => new{
Color = e.Attribute("color").Value,
Done = (bool)e.Attribute("done"),
Value = e.Value,
})
.ToList()
};
so i have this problem i`m trying to serialize my classes to the point that they will look like this:
<orders>
<order>
<ordersID>22070</ordersID>
<ordersTotal>53.00</ordersTotal>
<prod>
<productCount>1</productCount>
<productPrice>2.00</productPrice>
<productPricePromo>0.00</productPricePromo>
<productDiscount>0</productDiscount>
<productName>Шампоан против косопад Loreal Density Advanced 500 мл.</productName>
<productNumber>30055</productNumber>
</prod>
<prod>
<productCount>1</productCount>
<productPrice>6.00</productPrice>
<productPricePromo>0.00</productPricePromo>
<productDiscount>0</productDiscount>
<productName>Маска за суха коса Loreal Интенс Рипер 200 мл.</productName>
<productNumber>30107</productNumber>
</prod>
</order>
</orders>
But whatever i try e end up like this:
<?xml version="1.0" encoding="UTF-8"?>
<orders xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<order>
<order>
<ordersID>0</ordersID>
<ordersTotal>0</ordersTotal>
<products>
<progducts>
<productCount>0</productCount>
<productPrice>0</productPrice>
<productPricePromo>0</productPricePromo>
<productDiscount>0</productDiscount>
<productNumber>0</productNumber>
</progducts>
<progducts>
<productCount>0</productCount>
<productPrice>0</productPrice>
<productPricePromo>0</productPricePromo>
<productDiscount>0</productDiscount>
<productNumber>0</productNumber>
</progducts>
</products>
</order>
</order>
</orders>
The problem is the names of the second and third class i`m using is geting listed as tags aswell inside the xml. So my question is: is there any way around this?
Here is my code aswell.
Classes:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace testXML
{
[Serializable]
public class orders
{
private List <order> m_order = new List <order>();
[XmlArrayItem(typeof(order))]
public List<order> order
{
get { return m_order; }
set { m_order = value; }
}
}
[Serializable]
public class order
{
public int ordersID { get; set; }
public double ordersTotal { get; set; }
private List<progducts> prod = new List<progducts>();
[XmlArrayItem(typeof(progducts))]
public List<progducts> products
{
get { return prod; }
set { prod = value; }
}
}
[Serializable]
public class progducts
{
public string productName { get; set; }
public int productCount { get; set; }
public double productPrice { get; set; }
public double productPricePromo { get; set; }
public double productDiscount { get; set; }
public Int64 productNumber { get; set; }
}
}
And here is the execution code:
orders f = new orders();
order or = new order();
progducts p1 = new progducts();
progducts p2 = new progducts();
f.order.Add(or);
or.products.Add(p1);
or.products.Add(p2);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(orders));
TextWriter writer = new StreamWriter("Family.xml");
xmlSerializer.Serialize(writer, f);
writer.Close();
Thank you for any help in advance!
Replace the [XmlArrayItem(typeof(order))] with [XmlElement("order")] and [XmlArrayItem(typeof(progducts))] with [XmlElement("prod")]. That will remove one level when serializing the lists.
Just add another attributes to your property order like this:
[XmlArray("orders")]
[XmlArrayItem("order", typeof(order))]
public List<order> order
{
get { return m_order; }
set { m_order = value; }
}
That should work.
If you use the following classes which were generated using xsd.exe:
using System.Xml.Serialization;
using System;
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=false)]
public partial class orders
{
private ordersOrder orderField;
public ordersOrder order
{
get
{
return this.orderField;
}
set
{
this.orderField = value;
}
}
}
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
public partial class ordersOrder
{
private int ordersIDField;
private double ordersTotalField;
private ordersOrderProd[] prodField;
public int ordersID
{
get
{
return this.ordersIDField;
}
set
{
this.ordersIDField = value;
}
}
public double ordersTotal
{
get
{
return this.ordersTotalField;
}
set
{
this.ordersTotalField = value;
}
}
[System.Xml.Serialization.XmlElementAttribute("prod")]
public ordersOrderProd[] prod
{
get
{
return this.prodField;
}
set
{
this.prodField = value;
}
}
}
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
public partial class ordersOrderProd
{
private int productCountField;
private double productPriceField;
private double productPricePromoField;
private double productDiscountField;
private string productNameField;
private Int64 productNumberField;
public int productCount
{
get
{
return this.productCountField;
}
set
{
this.productCountField = value;
}
}
public double productPrice
{
get
{
return this.productPriceField;
}
set
{
this.productPriceField = value;
}
}
public double productPricePromo
{
get
{
return this.productPricePromoField;
}
set
{
this.productPricePromoField = value;
}
}
public double productDiscount
{
get
{
return this.productDiscountField;
}
set
{
this.productDiscountField = value;
}
}
public string productName
{
get
{
return this.productNameField;
}
set
{
this.productNameField = value;
}
}
public Int64 productNumber
{
get
{
return this.productNumberField;
}
set
{
this.productNumberField = value;
}
}
}
Then the following code:
var orders = new orders
{
order = new ordersOrder
{
ordersID = 1,
ordersTotal = 1,
prod = new ordersOrderProd[]
{
new ordersOrderProd
{
productCount = 1,
productDiscount = 8.4,
productName = "Widget",
productNumber = 987987,
productPrice = 78.9,
productPricePromo = 68.75
}
}
}
};
XmlSerializer xmlSerializer = new XmlSerializer(typeof(orders));
TextWriter writer = new StreamWriter(".\\Family.xml");
xmlSerializer.Serialize(writer, orders);
writer.Close();
Gives you the following output:
<?xml version="1.0" encoding="utf-8"?>
<orders xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<order>
<ordersID>1</ordersID>
<ordersTotal>1</ordersTotal>
<prod>
<productCount>1</productCount>
<productPrice>78.9</productPrice>
<productPricePromo>68.75</productPricePromo>
<productDiscount>8.4</productDiscount>
<productName>Widget</productName>
<productNumber>987987</productNumber>
</prod>
</order>
</orders>
You can use a serialization attribute to change the names of the XML elements or attributes you want to represent your class structure. See MSDN
I wanna serialize this class:
[Serializable]
[XmlRoot(ElementName = "Rates")]
public class CbrRate : IRate
{
public CbrRate()
{
}
public CbrRate(DateTime date, ICurrency currency, decimal rate)
{
Currency = currency;
Date = date;
Rate = rate;
}
[XmlIgnore]
public string SrcName
{
get { return "CBR"; }
}
[XmlElement(ElementName = "RequestDate")]
public DateTime Date { get; set; }
[XmlIgnore]
public ICurrency Currency { get; set; }
[XmlElement(ElementName = "Direction")]
public string Direction
{
get { return "RUR=>" + CodeChar.Trim(); }
}
[XmlElement(ElementName = "RateValue")]
public decimal Rate { get; set; }
[XmlElement(ElementName = "RateBase")]
public decimal BaseRate
{
get { return Math.Round(Rate/Nominal, 4); }
}
[XmlElement(ElementName = "RateCross")]
public decimal CrossRate
{
get { return Math.Round(1.00M/BaseRate, 4); }
}
[XmlElement(ElementName = "CodeNum")]
public int CodeNum
{
get { return Currency.CodeNumIso; }
}
[XmlElement(ElementName = "CodeISO")]
public string CodeChar
{
get { return Currency.CodeCharIso; }
}
[XmlElement(ElementName = "CurrencyName")]
public string Name
{
get { return Currency.Name; }
}
[XmlElement(ElementName = "Nominal")]
public decimal Nominal
{
get { return Currency.Nominal; }
}
}
public static XDocument Serialize<T>(this T source)
{
var target = new XDocument();
var s = new XmlSerializer(typeof (T));
using (var writer = target.CreateWriter())
{
s.Serialize(writer, source);
writer.Close();
}
return target;
}
But, that I have:
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfCbrRate xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<CbrRate>
<RequestDate>2011-09-05T18:49:55.1195696+04:00</RequestDate>
<RateValue>31.0539</RateValue>
</CbrRate>
...
How I can create correct xml, like this:
<ArrayOfRates>
<Rates>
<RequestDate></RequestDate>
<Direction></Direction>
<RateValue></RateValue>
<RateBase></RateBase>
...
First of all, .Net XmlSerializer will only serialize read/write properties (or fields). That's why only RequestDate and RateValue are serialized.
In order to achieve the XML structure you mentioned, you need to create a wrapper class as Roel said.
So, assuming you are serializing a List<CbrRate >, you will need to create a wrapper class for the list to have it serialized as you want it. Something like this:
[XmlRoot("root")]
public class ListOfRates
{
[XmlArray("ArrayOfRates")]
[XmlArrayItem("Rate")]
public List<CbrRate> Rates { get; set; }
}
this will produce the xml you want. Or you can play around with the attributes a little but if you don't want to have a root:
[XmlRoot("ArrayOfRates")]
public class ListOfRates
{
[XmlArrayItem("Rate")]
public List<CbrRate> Rates { get; set; }
}
the two attributes XmlArray and XmlArrayItem are key here. If you don't provide a name for the xml element, it will default to the property name.