XmlIgnoreAttribute ignoring both property's IsEnabled and isEnabledFieldSpecified bacause they have similar names. How to fix this problem? As result in i property secondCar i have IsEnabled=false but i expect to get IsEnabled=true. Maybe this is duplicate question but i can not find answer on stack.
class Program
{
static void Main(string[] args)
{
Car firstCar = new Car() { IsEnabled = true };
Car secondCar = new Car() { IsEnabled = false };
secondCar = XmlUtility.XmlStr2Obj<Car>(XmlUtility.Obj2XmlStr(firstCar));
}
}
[Serializable]
public class Car
{
private bool isEnabledFieldSpecified;
private bool isEnabledField;
[XmlAttributeAttribute()]
public bool IsEnabled
{
get
{
return this.isEnabledField;
}
set
{
this.isEnabledField = value;
}
}
[XmlIgnoreAttribute()]
public bool IsEnabledSpecified
{
get
{
return this.isEnabledFieldSpecified;
}
set
{
this.isEnabledFieldSpecified = value;
}
}
}
namespace Utils
{
public class XmlUtility
{
public static string Obj2XmlStr(object obj, string nameSpace)
{
if (obj == null) return string.Empty;
XmlSerializer sr = SerializerCache.GetSerializer(obj.GetType());
StringBuilder sb = new StringBuilder();
StringWriter w = new StringWriter(sb, CultureInfo.InvariantCulture);
sr.Serialize(
w,
obj,
new XmlSerializerNamespaces(
new[]
{
new XmlQualifiedName("", nameSpace)
}
));
return sb.ToString();
}
public static string Obj2XmlStr(object obj)
{
if (obj == null) return string.Empty;
XmlSerializer sr = SerializerCache.GetSerializer(obj.GetType());
StringBuilder sb = new StringBuilder();
StringWriter w = new StringWriter(sb, CultureInfo.InvariantCulture);
sr.Serialize(
w,
obj,
new XmlSerializerNamespaces(new[] { new XmlQualifiedName(string.Empty) }));
return sb.ToString();
}
public static T XmlStr2Obj<T>(string xml)
{
if (xml == null) return default(T);
if (xml == string.Empty) return (T)Activator.CreateInstance(typeof(T));
StringReader reader = new StringReader(xml);
XmlSerializer sr = SerializerCache.GetSerializer(typeof(T));
return (T)sr.Deserialize(reader);
}
public static XmlElement XmlStr2XmlDom(string xml)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
return doc.DocumentElement;
}
public static XmlElement Obj2XmlDom(object obj, string nameSpace)
{
return XmlStr2XmlDom(Obj2XmlStr(obj, nameSpace));
}
}
internal class SerializerCache
{
private static readonly Hashtable Hash = new Hashtable();
public static XmlSerializer GetSerializer(Type type)
{
XmlSerializer res;
lock (Hash)
{
res = Hash[type.FullName] as XmlSerializer;
if (res == null)
{
res = new XmlSerializer(type);
Hash[type.FullName] = res;
}
}
return res;
}
}
}
Try to set IsEnabledSpecified property to true in both objects. Like this:
Car firstCar = new Car() { IsEnabled = true, IsEnabledSpecified = true };
Car secondCar = new Car() { IsEnabled = false, IsEnabledSpecified = true };
This way serializer would know, that isEnabled property should be serialized.
IsEnabledSpecified property has special meaning to the serializer: it wouldn't be serialized itself(because of XmlIgnore attribute), but it controls would xml element it related to appear("Specified" property set to true) in the xml payload or not("Specified" property set to false).
Related
I'm trying to test the serialization for webrequests.
I'm doing a unit test where I:
- create a mock response from the server
- deserialize that response
- Compare initial object with deserialized one
The issue is one of my arrays in only partially populated where instead of all the elements, it only has one, the last one.
Deserialization has to be made by hand because of xml schema limitations.
Item is a partial class to separate the DTO from the xml operations
I have tried to change the attributes of the array property to
[XmlArray("items")]
[XmlArrayItemAttribute("item")]
I have tested just the serialization-deserialization of an individual item and it works.
I have checked both the xml resulting from the original serialization and the xml which is deseriliazed and they are equal.
First the item itself:
[XmlRoot("item")]
public partial class InvoiceItem
{
[XmlElement(ElementName = "name")]
public string Name { get; set; }
}
Now the array:
[XmlArray("items")]
[XmlArrayItemAttribute("item")]
public InvoiceItem[] InvoiceItems
{
get
{
return this.invoiceItems;
}
set
{
this.invoiceItems = value;
}
}
Finally the deserializator:
public void ReadXml(XmlReader reader)
{
Regex regexTaxName = new Regex(#"tax\d_name");
Regex regexTaxType = new Regex(#"tax\d_type");
Regex regexTaxPercent = new Regex(#"tax\d_percent");
Regex regexTaxNumber = new Regex(#"\d");
List<Tax> taxesList = new List<Tax>();
while (reader.Read())
{
Debug.WriteLine(reader.GetAttribute("name"));
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name.Equals("name", StringComparison.Ordinal))
{
reader.Read();
this.Name = reader.Value;
}
else if (reader.Name.Equals("type", StringComparison.Ordinal))
{
ProductType value = ProductType.Product;
reader.Read();
if (Enums.TryParse<ProductType>(reader.Value, out value))
{
this.Type = value;
}
}
else if (reader.Name.Equals("description", StringComparison.Ordinal))
{
reader.Read();
this.Description = reader.Value;
}
else if (reader.Name.Equals("unit_cost", StringComparison.Ordinal))
{
float value = 0;
reader.Read();
if (float.TryParse(reader.Value, out value))
{
this.UnitCost = value;
}
}
else if (reader.Name.Equals("quantity", StringComparison.Ordinal))
{
int value = 0;
reader.Read();
if (int.TryParse(reader.Value, out value))
{
this.Quantity = value;
}
}
else if (reader.Name.Equals("discount", StringComparison.Ordinal))
{
float value = 0;
reader.Read();
if (float.TryParse(reader.Value, out value))
{
this.Discount = value;
}
}
else if (reader.Name.Equals("discount_type", StringComparison.Ordinal))
{
NumericalSignificance value = NumericalSignificance.Percent;
reader.Read();
if (Enums.TryParse<NumericalSignificance>(reader.Value, out value))
{
this.DiscountType = value;
}
}
else if (regexTaxName.IsMatch(reader.Name))
{
int taxNumber = int.Parse(regexTaxNumber.Match(reader.Name).Value, CultureInfo.CurrentCulture);
if (taxesList.Count < taxNumber)
{
reader.Read();
Tax newTax = new Tax();
newTax.Name = reader.Value;
taxesList.Add(newTax);
}
else
{
reader.Read();
taxesList[taxNumber-1].Name = reader.Value;
}
}
else if (regexTaxPercent.IsMatch(reader.Name))
{
int taxNumber = int.Parse(regexTaxNumber.Match(reader.Name).Value, CultureInfo.CurrentCulture);
if (taxesList.Count > taxNumber)
{
Tax newTax = new Tax();
float value = 0;
reader.Read();
if (float.TryParse(reader.Value, out value))
{
newTax.TaxPercent = value;
}
taxesList.Add(newTax);
}
else
{
float value = 0;
reader.Read();
if (float.TryParse(reader.Value, out value))
{
taxesList[taxNumber-1].TaxPercent = value;
}
}
}
else if (regexTaxType.IsMatch(reader.Name))
{
int taxNumber = int.Parse(regexTaxNumber.Match(reader.Name).Value, CultureInfo.CurrentCulture);
if (taxesList.Count > taxNumber)
{
Tax newTax = new Tax();
NumericalSignificance value = NumericalSignificance.Percent;
reader.Read();
if (Enums.TryParse<NumericalSignificance>(reader.Value, out value))
{
newTax.Type = value;
}
taxesList.Add(newTax);
}
else
{
NumericalSignificance value = NumericalSignificance.Percent;
reader.Read();
if (Enums.TryParse<NumericalSignificance>(reader.Value, out value))
{
taxesList[taxNumber-1].Type = value;
}
}
}
}
}
taxesArr = taxesList.ToArray();
}
The problems is on the items array where the end object only has the final object instead of all the original ones.
EDIT:
An example that shows the issue:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
[XmlRoot("invoice")]
public class Invoice
{
[XmlArray("items")]
private InvoiceExampleItem[] items;
[XmlArray("items")]
public InvoiceExampleItem[] Items
{
get { return this.items; }
set { this.items = value; }
}
}
[XmlRoot("item", Namespace = "")]
public partial class InvoiceExampleItem : IXmlSerializable
{
[XmlElement(ElementName = "name")]
public string Name { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name.Equals("name", StringComparison.Ordinal))
{
reader.Read();
this.Name = reader.Value;
}
}
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteElementString("name", this.Name);
}
}
[TestClass]
public class ExampleTest : AutoMoqTest
{
[TestMethod]
public void ExampleDeserialization()
{
InvoiceExampleItem item1 = new InvoiceExampleItem()
{
Name = "item1"
};
InvoiceExampleItem item2 = new InvoiceExampleItem()
{
Name = "item2"
};
InvoiceExampleItem item3 = new InvoiceExampleItem()
{
Name = "item3"
};
Invoice mockInvoice = new Invoice()
{
Items = new InvoiceExampleItem[] { item1, item2, item3 }
};
XmlDocument mockInvoiceSerialized = SerializeInvoice(mockInvoice);
XmlDocument mockResponseXml = GenerateXmlResponse(mockInvoiceSerialized);
GetInvoiceResponse response = DeserializeXML<GetInvoiceResponse>(mockResponseXml.OuterXml);
Invoice resultInvoice = response.Invoice;
if (mockInvoice.Items.Length != resultInvoice.Items.Length)
{
throw new Exception("wrong number of items");
}
}
public XmlDocument SerializeInvoice(Invoice invoiceToSerialize)
{
XmlDocument toReturn = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(typeof(Invoice));
XmlReaderSettings settings = new XmlReaderSettings();
XmlDocument itemsDocument = GetTemplateXML();
InvoiceExampleItem[] items = invoiceToSerialize.Items;
MemoryStream memStm = null, tempStream = null;
try
{
memStm = tempStream = new MemoryStream();
using (StreamWriter sw = new StreamWriter(memStm, Encoding.UTF8))
{
// Serialize object into raw xml
memStm = null;
serializer.Serialize(sw, invoiceToSerialize);
sw.Flush();
// parse raw xml into Xml document
tempStream.Position = 0;
settings.IgnoreWhitespace = true;
var xtr = XmlReader.Create(tempStream, settings);
toReturn.Load(xtr);
}
}
finally
{
if (memStm != null)
{
memStm.Dispose();
}
}
return toReturn;
}
public static T DeserializeXML<T>(string responseString)
where T : class
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (StringReader sr = new StringReader(responseString))
{
return (T)xmlSerializer.Deserialize(sr);
}
}
public XmlDocument GetTemplateXML()
{
XmlDocument toReturn = new XmlDocument();
var decl = toReturn.CreateXmlDeclaration("1.0", "utf-8", string.Empty);
toReturn.AppendChild(decl);
return toReturn;
}
private XmlDocument GenerateXmlResponse(XmlDocument innerXMLDocument)
{
XmlDocument toReturn = GetTemplateXML();
XmlElement requestElement = toReturn.CreateElement("response");
requestElement.SetAttribute("status", "success");
requestElement.InnerXml = innerXMLDocument.DocumentElement.OuterXml;
toReturn.AppendChild(requestElement);
return toReturn;
}
/// <summary>
/// Web response from creating a invoice
/// </summary>
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", ElementName = "response")]
public class GetInvoiceResponse
{
/// <summary>
/// Gets or sets the response status
/// </summary>
/// <value>
/// reponse Status
/// </value>
[XmlAttribute("status")]
public string ResponseStatus { get; set; }
/// <summary>
/// Gets or sets the new invoice id
/// </summary>
/// <value>
/// generated by invoicera for this response
/// </value>
[XmlElement(ElementName = "invoice")]
public Invoice Invoice { get; set; }
}
}
The solution was to create a class for the array and implement the IXMLSeriazable interface in that class and removing the interface from the item class.
Then, when the xml in the items class, I cycle the tags and create an item individually, adding it to the array next.
For some reason, the method was not exiting when it processed each <\item> tag, so I added a condition to exit the cycle.
Here is the complete code:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
[XmlRoot("invoice")]
public class Invoice
{
public Items items { get; set; }
}
[XmlRoot("item", Namespace = "")]
public partial class InvoiceExampleItem
{
[XmlElement(ElementName = "name")]
public string Name { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name.Equals("name", StringComparison.Ordinal))
{
reader.Read();
this.Name = reader.Value;
return;
}
}
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteElementString("name", this.Name);
}
}
public class Items : IXmlSerializable
{
[XmlElement("item")]
public List<InvoiceExampleItem> list = new List<InvoiceExampleItem>();
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
while (reader.ReadToFollowing("item"))
{
InvoiceExampleItem currentItem = new InvoiceExampleItem();
currentItem.Name = reader.Value;
list.Add(currentItem);
}
}
public void WriteXml(XmlWriter writer)
{
foreach(InvoiceExampleItem item in list)
{
writer.WriteStartElement("item");
item.WriteXml(writer);
writer.WriteEndElement();
}
}
}
[TestClass]
public class ExampleTest : AutoMoqTest
{
[TestMethod]
public void ExampleDeserialization()
{
/**/
InvoiceExampleItem item1 = new InvoiceExampleItem()
{
Name = "item1"
};
InvoiceExampleItem item2 = new InvoiceExampleItem()
{
Name = "item2"
};
InvoiceExampleItem item3 = new InvoiceExampleItem()
{
Name = "item3"
};
Items items = new Items();
InvoiceExampleItem item21 = new InvoiceExampleItem()
{
Name = "item1"
};
InvoiceExampleItem item22 = new InvoiceExampleItem()
{
Name = "item2"
};
InvoiceExampleItem item23 = new InvoiceExampleItem()
{
Name = "item3"
};
items.list.Add(item21);
items.list.Add(item22);
items.list.Add(item23);
Invoice mockInvoice = new Invoice()
{
items = items
};
/**/
XmlDocument mockInvoiceSerialized = SerializeInvoice(mockInvoice);
XmlDocument mockResponseXml = GenerateXmlResponse(mockInvoiceSerialized);
GetInvoiceResponse test = new GetInvoiceResponse();
GetInvoiceResponse response = DeserializeXML<GetInvoiceResponse>(mockResponseXml.OuterXml);
Invoice resultInvoice = response.Invoice;
mockResponseXml.Save("C:\\Users\\360Imprimir\\Desktop\\mockResponseXml");
mockInvoiceSerialized.Save("C:\\Users\\360Imprimir\\Desktop\\mockInvoiceSerialized.Xml");
if (mockInvoice.items.list.Count != resultInvoice.items.list.Count)
{
throw new Exception("wrong number of items");
}
}
public XmlDocument SerializeInvoice(Invoice invoiceToSerialize)
{
XmlDocument toReturn = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(typeof(Invoice));
XmlReaderSettings settings = new XmlReaderSettings();
XmlDocument itemsDocument = GetTemplateXML();
MemoryStream memStm = null, tempStream = null;
try
{
memStm = tempStream = new MemoryStream();
using (StreamWriter sw = new StreamWriter(memStm, Encoding.UTF8))
{
// Serialize object into raw xml
memStm = null;
serializer.Serialize(sw, invoiceToSerialize);
sw.Flush();
// parse raw xml into Xml document
tempStream.Position = 0;
settings.IgnoreWhitespace = true;
var xtr = XmlReader.Create(tempStream, settings);
toReturn.Load(xtr);
}
}
finally
{
if (memStm != null)
{
memStm.Dispose();
}
}
return toReturn;
}
public static T DeserializeXML<T>(string responseString)
where T : class
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (StringReader sr = new StringReader(responseString))
{
return (T)xmlSerializer.Deserialize(sr);
}
}
public XmlDocument GetTemplateXML()
{
XmlDocument toReturn = new XmlDocument();
var decl = toReturn.CreateXmlDeclaration("1.0", "utf-8", string.Empty);
toReturn.AppendChild(decl);
return toReturn;
}
private XmlDocument GenerateXmlResponse(XmlDocument innerXMLDocument)
{
XmlDocument toReturn = GetTemplateXML();
XmlElement requestElement = toReturn.CreateElement("response");
requestElement.SetAttribute("status", "success");
requestElement.InnerXml = innerXMLDocument.DocumentElement.OuterXml;
toReturn.AppendChild(requestElement);
return toReturn;
}
/// <summary>
/// Web response from creating a invoice
/// </summary>
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", ElementName = "response")]
public class GetInvoiceResponse
{
/// <summary>
/// Gets or sets the response status
/// </summary>
/// <value>
/// reponse Status
/// </value>
[XmlAttribute("status")]
public string ResponseStatus { get; set; }
/// <summary>
/// Gets or sets the new invoice id
/// </summary>
/// <value>
/// generated by invoicera for this response
/// </value>
[XmlElement(ElementName = "invoice")]
public Invoice Invoice { get; set; }
}
}
I have the following XML:
<?xml version="1.0" encoding="utf-8"?>
<CallEvents xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<CallEvent>
<Time>2014-02-24T06:44:27.12</Time>
<Type>Inner</Type>
<Fs>
<StrPair>
<Key>Name</Key>
<Value>Call1</Value>
</StrPair>
<StrPair>
<Key>Owner</Key>
<Value>Ali</Value>
</StrPair>
</Fs>
</CallEvent>
<CallEvent>
<Time>2014-02-24T06:44:29.089</Time>
<Type>Outer</Type>
<Fs>
<StrPair>
<Key>Name</Key>
<Value>Call2</Value>
</StrPair>
<StrPair>
<Key>Id</Key>
<Value>3242</Value>
</StrPair>
<StrPair>
<Key>Another</Key>
<Value>123</Value>
</StrPair>
</Fs>
</CallEvent>
</CallEvents>
I tried to deserialize it, but it doesn't want to deserialize list Fs. I get CallEvents with CallEvent items, and members of CallEvent filled with correct values except list Fs. The list Fs is empty. Why?
What do I do wrong?
class Program
{
static void Main(string[] args)
{
string xmlFile = "call_events.xml";
CallEvents events = CallEvents.OpenFromXmlFile(xmlFile);
Console.ReadKey();
}
}
[Serializable]
public class CallEvent
{
[XmlElement]
public DateTime Time;
[XmlElement]
public CallEventType Type;
public CallEvent()
{
this.Fields = new Dictionary<string, string>();
}
[XmlArray("Fs"), XmlArrayItem("StrPair")]
public List<StrPair> Fs
{
get
{
var list = new List<StrPair>();
foreach (var pair in Fields)
{
list.Add(new StrPair(pair.Key, pair.Value));
}
return list;
}
set
{
Fields.Clear();
foreach (var dictPair in value)
{
Fields.Add(dictPair.Key, dictPair.Value);
}
}
}
[XmlIgnore]
public Dictionary<string, string> Fields;
public void ParseFields(List<LogMessage> eventLogMessages)
{
int eventLogMessagesCount = eventLogMessages.Count;
this.Fields.Clear();
for (int i = 0; i < eventLogMessagesCount; i++)
{
LogMessage logMessage = eventLogMessages[i];
int pos = logMessage.Message.IndexOf(": ");
if(pos == -1)
continue;
string fieldName = logMessage.Message.Substring(0, pos);
pos+=2;
string fieldValue = logMessage.Message.Substring(pos);
if (this.Fields.ContainsKey(fieldName))
{
this.Fields[fieldName] += ("\r\n" + fieldValue);
}
else
{
this.Fields.Add(fieldName, fieldValue);
}
}
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0} {1} | ", Type, Time);
foreach (var pair in Fields)
{
sb.AppendFormat("{0}: {1}, ", pair.Key, pair.Value);
}
return sb.ToString();
}
[Serializable]
public class StrPair
{
[XmlElement]
public string Key;
[XmlElement]
public string Value;
public StrPair() { }
public StrPair(string key, string value)
{
Key = key;
Value = value;
}
}
}
[XmlRoot("CallEvents")]
public class CallEvents : List<CallEvent>
{
static public CallEvents OpenFromXmlFile(string xmlFileName)
{
CallEvents callEvents;// = new CallEvents();
XmlSerializer ser = new XmlSerializer(typeof(CallEvents));
XmlReader xmlReader = new XmlTextReader(xmlFileName);
try
{
callEvents = (CallEvents)ser.Deserialize(xmlReader);
}
finally
{
xmlReader.Close();
}
return callEvents;
}
public void SaveToXmlFile(string xmlFileName)
{
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Encoding = System.Text.Encoding.UTF8;
xmlWriterSettings.Indent = true;
XmlSerializer ser = new XmlSerializer(this.GetType());
XmlWriter xmlWriter = null;
tryAgain:
try
{
xmlWriter = XmlTextWriter.Create(xmlFileName, xmlWriterSettings);
ser.Serialize(xmlWriter, this);
}
catch (Exception ex)
{
System.Windows.Forms.DialogResult dr = System.Windows.Forms.MessageBox.Show("Couldn't serialize to XML. Details: " + ex.Message, "Error", System.Windows.Forms.MessageBoxButtons.RetryCancel, System.Windows.Forms.MessageBoxIcon.Warning);
if (dr == System.Windows.Forms.DialogResult.Retry)
{
goto tryAgain;
}
}
finally
{
if (xmlWriter != null)
{
xmlWriter.Close();
}
}
}
}
I havn'e used this in a while so the code might need some touching up.
CallEvents events;
using(XmlReader reader = XmlReader.Create("call_events.xml"))
{
XmlDeserializer deSerializer = new XmlDeserializer(typeof(CallEvents));
events = (CallEvents)deSerializer.Deserialize(reader);
}
Is it possible Deserialize unknown XML to object like below?
var xml = #"<Students><Student><Name>Arul</Name><Mark>90</Mark></Student></Students>";
var serializer = new XmlSerializer(typeof(DynamicObject));
dynamic students = serializer.Deserialize(new XmlTextReader(new StringReader(xml)));
You may want to try this.
string xml = #"<Students>
<Student ID=""100"">
<Name>Arul</Name>
<Mark>90</Mark>
</Student>
<Student>
<Name>Arul2</Name>
<Mark>80</Mark>
</Student>
</Students>";
dynamic students = DynamicXml.Parse(xml);
var id = students.Student[0].ID;
var name1 = students.Student[1].Name;
foreach(var std in students.Student)
{
Console.WriteLine(std.Mark);
}
public class DynamicXml : DynamicObject
{
XElement _root;
private DynamicXml(XElement root)
{
_root = root;
}
public static DynamicXml Parse(string xmlString)
{
return new DynamicXml(XDocument.Parse(xmlString).Root);
}
public static DynamicXml Load(string filename)
{
return new DynamicXml(XDocument.Load(filename).Root);
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;
var att = _root.Attribute(binder.Name);
if (att != null)
{
result = att.Value;
return true;
}
var nodes = _root.Elements(binder.Name);
if (nodes.Count() > 1)
{
result = nodes.Select(n => n.HasElements ? (object)new DynamicXml(n) : n.Value).ToList();
return true;
}
var node = _root.Element(binder.Name);
if (node != null)
{
result = node.HasElements || node.HasAttributes ? (object)new DynamicXml(node) : node.Value;
return true;
}
return true;
}
}
--EDIT--
To make it work with xml namespaces, I added RemoveNamespaces method.
public class DynamicXml : DynamicObject
{
XElement _root;
private DynamicXml(XElement root)
{
_root = root;
}
public static DynamicXml Parse(string xmlString)
{
return new DynamicXml(RemoveNamespaces(XDocument.Parse(xmlString).Root));
}
public static DynamicXml Load(string filename)
{
return new DynamicXml(RemoveNamespaces(XDocument.Load(filename).Root));
}
private static XElement RemoveNamespaces(XElement xElem)
{
var attrs = xElem.Attributes()
.Where(a => !a.IsNamespaceDeclaration)
.Select(a => new XAttribute(a.Name.LocalName, a.Value))
.ToList();
if (!xElem.HasElements)
{
XElement xElement = new XElement(xElem.Name.LocalName, attrs);
xElement.Value = xElem.Value;
return xElement;
}
var newXElem = new XElement(xElem.Name.LocalName, xElem.Elements().Select(e => RemoveNamespaces(e)));
newXElem.Add(attrs);
return newXElem;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;
var att = _root.Attribute(binder.Name);
if (att != null)
{
result = att.Value;
return true;
}
var nodes = _root.Elements(binder.Name);
if (nodes.Count() > 1)
{
result = nodes.Select(n => n.HasElements ? (object)new DynamicXml(n) : n.Value).ToList();
return true;
}
var node = _root.Element(binder.Name);
if (node != null)
{
result = node.HasElements || node.HasAttributes ? (object)new DynamicXml(node) : node.Value;
return true;
}
return true;
}
}
I would like to XML serialize instances of my object Exception and store it in the XMLNode[] Nodes property of another object ExceptionReport.
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Xml.Serialization.XmlSchemaProviderAttribute("ExportSchema")]
[System.Xml.Serialization.XmlRootAttribute(IsNullable = false)]
public partial class ExceptionReport : object, System.Xml.Serialization.IXmlSerializable
{
public System.Xml.XmlNode[] Nodes { get; set; }
public void ReadXml(System.Xml.XmlReader reader)
{
this.Nodes = System.Runtime.Serialization.XmlSerializableServices.ReadNodes(reader);
}
public void WriteXml(System.Xml.XmlWriter writer)
{
System.Runtime.Serialization.XmlSerializableServices.WriteNodes(writer, this.Nodes);
}
}
public class Exception
{
public string ExceptionText;
public string exceptionCode;
public string locator;
}
How would i go about doing this so the result would be something like this:
<ExceptionReport xmlns="http://www.opengis.net/ows" >
<Exception exceptionCode="1">my first instance</Exception>
<Exception exceptionCode="2">my second instance</Exception>
</ExceptionReport>
So far i have the following but i need to know how to serialize these objects and store them in the ExceptionReport Nodes array.
ExceptionReport er = new ExceptionReport();
Exception exception_item1 = new Exception();
exception_item1.ExceptionText = "my first instance";
exception_item1.exceptionCode = "1";
Exception exception_item2 = new Exception();
exception_item2.ExceptionText = "my second instance";
exception_item2.exceptionCode = "2";
List<Exception> exceptions = new List<Exception>( exception_item1, exception_item2 );
[XmlRoot("ExceptionReport")]
public partial class ExceptionReport
{
[XmlElement("Exception")]
public List<Exception> Nodes { get; set; }
public ExceptionReport()
{
Nodes = new List<Exception>();
}
}
public class Exception
{
[XmlText]
public string ExceptionText;
[XmlAttribute("exceptionCode")]
public int ExceptionCode;
[XmlAttribute("locator")]
public string Locator;
}
Then to serialize, I use the following extensions:
public static bool XmlSerialize<T>(this T item, string fileName)
{
return item.XmlSerialize(fileName, true);
}
public static bool XmlSerialize<T>(this T item, string fileName, bool removeNamespaces)
{
object locker = new object();
XmlSerializerNamespaces xmlns = new XmlSerializerNamespaces();
xmlns.Add(string.Empty, string.Empty);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
lock (locker)
{
using (XmlWriter writer = XmlWriter.Create(fileName, settings))
{
if (removeNamespaces)
{
xmlSerializer.Serialize(writer, item, xmlns);
}
else { xmlSerializer.Serialize(writer, item); }
writer.Close();
}
}
return true;
}
public static T XmlDeserialize<T>(this string s)
{
object locker = new object();
StringReader stringReader = new StringReader(s);
XmlTextReader reader = new XmlTextReader(stringReader);
try
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
lock (locker)
{
T item = (T)xmlSerializer.Deserialize(reader);
reader.Close();
return item;
}
}
finally
{
if (reader != null)
{ reader.Close(); }
}
}
public static T XmlDeserialize<T>(this FileInfo fileInfo)
{
string xml = string.Empty;
using (FileStream fs = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read))
{
using (StreamReader sr = new StreamReader(fs))
{
return sr.ReadToEnd().XmlDeserialize<T>();
}
}
}
Use like this:
ExceptionReport report = new ExceptionReport();
report.Nodes.Add(new Exception { ExceptionText = "my first instance", ExceptionCode = 1, Locator = "loc1" });
report.Nodes.Add(new Exception { ExceptionText = "my second instance", ExceptionCode = 2 });
report.XmlSerialize("C:\\test.xml");
I tested and it came out like you wanted. Hope it helps...
PS - The extensions came from my library on codeproject: http://www.codeproject.com/KB/dotnet/MBGExtensionsLibrary.aspx
Need your help in setting the xml attributes for XML deserialization.
This is my input xml:
<form>
<question id="QnA">
<answer>AnswerforA</answer>
</question>
<question id="QnB">
<answer>AnswerforB</answer>
</question>
<question id="QnC">
<answer>AnswerforC</answer>
</question>
</form>
The ids of each question element tag correspond to a class property and its value is the innertext of the corresponding answer element.
The .cs file will look like
public class Test
{
private string qnaAns;
private string qnbAns;
private string qncAns;
public string QnA
{
get{ return qnaAns;}
set{qnaAns = value;}
}
public string QnB
{
get{ return qnbAns;}
set{qnbAns = value;}
}
public string QnC
{
get{ return qncAns;}
set{qncAns = value;}
}
}
and I use the follwing code for deserialization
XmlSerializer ser = new XmlSerializer(typeof(Test));
XmlReader xr = new xmlReader(inputxml);
Test t = ser.Deserialize(xr) as Test;
Please let me know how to set the XML element/attribute for the Test class to achieve this.
Thanks for your time.
[XmlRoot("form")]
public class Form
{
[XmlElement("question")]
public List<Question> Questions { get; set; }
public Form()
{
Questions = new List<Question>();
}
}
public struct Question
{
[XmlAttribute("id")]
public string ID { get; set; }
[XmlElement("answer")]
public string Answer { get; set; }
}
Then to serialize, I use the following extensions:
public static bool XmlSerialize<T>(this T item, string fileName)
{
return item.XmlSerialize(fileName, true);
}
public static bool XmlSerialize<T>(this T item, string fileName, bool removeNamespaces)
{
object locker = new object();
XmlSerializerNamespaces xmlns = new XmlSerializerNamespaces();
xmlns.Add(string.Empty, string.Empty);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
lock (locker)
{
using (XmlWriter writer = XmlWriter.Create(fileName, settings))
{
if (removeNamespaces)
{
xmlSerializer.Serialize(writer, item, xmlns);
}
else { xmlSerializer.Serialize(writer, item); }
writer.Close();
}
}
return true;
}
public static T XmlDeserialize<T>(this string s)
{
object locker = new object();
StringReader stringReader = new StringReader(s);
XmlTextReader reader = new XmlTextReader(stringReader);
try
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
lock (locker)
{
T item = (T)xmlSerializer.Deserialize(reader);
reader.Close();
return item;
}
}
finally
{
if (reader != null)
{ reader.Close(); }
}
}
public static T XmlDeserialize<T>(this FileInfo fileInfo)
{
string xml = string.Empty;
using (FileStream fs = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read))
{
using (StreamReader sr = new StreamReader(fs))
{
return sr.ReadToEnd().XmlDeserialize<T>();
}
}
}
Hope this helps.
PS - The extensions came from my library on codeproject: http://www.codeproject.com/KB/dotnet/MBGExtensionsLibrary.aspx