I have a very very strange problem. I would like to serialize a object via xml.
Ok so I started creating a XmlSerializer and there was no error - nothing.
But if i deserialize... the properties are just not set. Another strange thing is that the xml seems to be ok.
this is a part of my code the rest are just methods...
[XmlInclude(typeof(TestMapper))]
[XmlInclude(typeof(TestSource))]
[XmlInclude(typeof(TestTarget))]
public abstract class ElementBase : IElement
{
[XmlIgnore]
public IElement this[int index]
{
get
{
return Childs.ElementAt(index).Value;
}
}
List<ElementBase> _childSerializable;
[XmlArrayItem(typeof(ElementBase))]
public List<ElementBase> ChildSerializable
{
get
{
_childSerializable = Childs.Select(x => x.Value).ToList();
return _childSerializable;
}
set
{
_childSerializable = value;
foreach (ElementBase element in _childSerializable)
Childs.Add(element.Index, element);
}
}
protected Dictionary<object, ElementBase> _childs;
[XmlIgnore]
public Dictionary<object, ElementBase> Childs
{
get
{
return _childs ?? (_childs = new Dictionary<object ,ElementBase>());
}
set
{
_childs = value;
}
}
object _index = -1;
public object Index
{
get
{
return _index;
}
set
{
_index = value;
}
}
And this is how i serialize:
TestTarget target = new TestTarget();
TestSource source = new TestSource() { Value = "Hallo", Index = 1};
TestSource source1 = new TestSource() { Value = " Welt", Index = 2};
TestMapper mapper = new TestMapper();
target.ConnectChild(mapper);
mapper.ConnectChild(source);
mapper.ConnectChild(source1);
target.Execute();
System.IO.MemoryStream memStream = new System.IO.MemoryStream();
XmlSerializer serializer = new XmlSerializer(target.GetType());
serializer.Serialize(memStream, target);
memStream.Position = 0;
object obj = serializer.Deserialize(memStream);
target = obj as TestTarget;
memStream.Position = 0;
Console.ReadKey();
Ok so now and this is the final result:
<?xml version="1.0"?>
<TestTarget xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ChildSerializable>
<ElementBase xsi:type="TestMapper">
<ChildSerializable>
<ElementBase xsi:type="TestSource">
<ChildSerializable />
<Index xsi:type="xsd:int">1</Index>
<Value xsi:type="xsd:string">Hallo</Value>
</ElementBase>
<ElementBase xsi:type="TestSource">
<ChildSerializable />
<Index xsi:type="xsd:int">2</Index>
<Value xsi:type="xsd:string"> Welt</Value>
</ElementBase>
</ChildSerializable>
<Index xsi:type="xsd:int">-1</Index>
</ElementBase>
</ChildSerializable>
<Index xsi:type="xsd:int">-1</Index>
</TestTarget>
So does anyone has any idea what happend wrong. I am working since a few hours on it but i can t find anything :((
It seems to be related to the fact that you are using a List<ElementBase>. If you use a simple array like ElementBase[], the items are deserialized.
There are some explanation here, but nothing I found satisfactory.
Related
I am working on a small project to learn how to serialize an object in C#.NET I created the following class that is the class I am attempting to serialize:
public class Object
{
private Dictionary<string, int> dictionaryStringInt;
public Object()
{
this.dictionaryStringInt = new Dictionary<string, int>();
}
public void AddToDictionary(string s, int i)
{
this.dictionaryStringInt.Add(s, i);
}
public List<DictionaryEntry> DictionaryStringInt
{
get
{
List<DictionaryEntry> list = new List<DictionaryEntry>();
foreach (KeyValuePair<string, int> element in dictionaryStringInt)
{
list.Add(new DictionaryEntry(element.Key, element.Value));
}
return list;
}
set
{
Dictionary<string, int> dictionary = new Dictionary<string, int>();
foreach (DictionaryEntry entry in value)
{
dictionary.Add(entry.Key, entry.Value);
}
dictionaryStringInt = dictionary;
}
}
public class DictionaryEntry
{
private string key;
private int value;
public DictionaryEntry()
{
this.key = string.Empty;
this.value = 0;
}
public DictionaryEntry(string key, int value)
{
this.key = key;
this.value = value;
}
[XmlAttribute("Key")]
public string Key
{
get
{
return key;
}
set
{
key = value;
}
}
[XmlText]
public int Value
{
get
{
return value;
}
set
{
this.value = value;
}
}
}
}
And I have this code here that does the work:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
this.ClearTextBox();
SerialisationTest.Object #object = this.SetUpObject();
this.Serialize(#object);
#object = null;
#object = this.Deserialise(#"C:\Sources\SerialisationTest\test.xml");
this.textBox1.Text = #object.ToString();
this.DisplayXML(#object);
}
public void Serialize(SerialisationTest.Object #object)
{
XmlSerializer serializer = new XmlSerializer(typeof(SerialisationTest.Object));
using (TextWriter writer = new StreamWriter(#"C:\Sources\SerialisationTest\test.xml"))
{
serializer.Serialize(writer, #object);
}
}
public SerialisationTest.Object Deserialise(string path)
{
XmlSerializer serializer = new XmlSerializer(typeof(SerialisationTest.Object));
using (TextReader reader = new StreamReader(path))
{
return (SerialisationTest.Object)serializer.Deserialize(reader);
}
}
public void DisplayXML(SerialisationTest.Object #object)
{
XmlSerializer serializer = new XmlSerializer(typeof(SerialisationTest.Object));
using (StringWriter writer = new StringWriter())
{
serializer.Serialize(writer, #object);
this.textBox1.Text = writer.ToString();
}
}
public SerialisationTest.Object SetUpObject()
{
SerialisationTest.Object #object = new SerialisationTest.Object();
#object.AddToDictionary("1", 1);
#object.AddToDictionary("2", 2);
#object.AddToDictionary("3", 3);
return #object;
}
public void ClearTextBox()
{
this.textBox1.Text = "";
}
}
Now here is the result I see in my textbox:
<?xml version="1.0" encoding="utf-16"?>
<Object xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<DictionaryStringInt />
</Object>
Now most of the code is just me playing around trying to serialize things. But then I tried serializing the DictionaryStringInt and saw on MSDN that Dictionary cannot be serialize. So I tried to cheat the serializer by having the property return a list of DictionaryEntry and converting this list into a dictionary in the setter. But as you can see, the DictionaryStringInt's data is not serialized. I was hoping to received something like:
<?xml version="1.0" encoding="utf-16"?>
<Object xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<DictionaryStringInt>
<DictionaryEntry Key="1">1</DictionaryEntry>
<DictionaryEntry Key="2">2</DictionaryEntry>
<DictionaryEntry Key="3">3</DictionaryEntry>
</DictionaryStringInt>
</Object>
Does anyone know how to achieve this? (BTW I am aware that if I have multiple of the same key in the XML when deserializing the dictionary I'll get something like an KeyAlreadyPresentException of some sort and this is a behavior want)
Thanks!
Edit
I just found out that the "C:\Sources\SerialisationTest\test.xml" contains the right result.
So there seems to be a problem with the deserialisation or the displaying of the serialisation using the StringWriter.
Is there something I am doing wrong?
Edit2
Simplified the code
Edit3
I looked at the answer linked in the comment and I think I got what is going on... In one of the answers there is a comment that point that the setter wont be called and that I should use an array instead. I'm looking into this.
The problem is that you are losing the contents of your surrogate List<DictionaryEntry> DictionaryStringInt property during deserialization. The workaround is to change it to an array:
DictionaryEntry [] DictionaryStringInt
{
get
{
if (dictionaryStringInt == null)
return null;
List<DictionaryEntry> list = new List<DictionaryEntry>();
foreach (KeyValuePair<string, int> element in dictionaryStringInt)
{
list.Add(new DictionaryEntry(element.Key, element.Value));
}
return list.ToArray();
}
set
{
if (value == null)
return;
Dictionary<string, int> dictionary = new Dictionary<string, int>();
foreach (DictionaryEntry entry in value)
{
dictionary.Add(entry.Key, entry.Value);
}
dictionaryStringInt = dictionary;
}
}
For an explanation of why a List<DictionaryEntry> surrogate property fails during deserialization, see Cannot deserialize XML into a list using XML Deserializer. As explained there:
XmlSerializer calls the getter to get the list. If null, it allocates a list and sets it via the setter. It holds onto the list in some local variable while reading it.
It deserializes each list element, and adds it to the list it is holding.
And that's it. It never calls the containing class's list property setter afterwards. Thus the contents of the list are never populated into your dictionary.
But, since an array cannot be resized once allocated, it must be allocated and set back after its contents are read and deserialized. Thus, an array property can function as a surrogate during deserialization.
I have a bunch of C# classes, which are auto generated from an XSD. Then I generate XML files based on those C# classes. Nothing existing so far.
The problem:
The generated XML files are going through validation and the validation requires an extra attribute to all XML tags with xsi:nil="true". Basically the tags should look like : <testTag.01 xsi:nil="true" NV="123123" />, but I can't achieve that in C#. My code is:
if (myObject.TestTag.HasValue)
{
t.testTag01 = new testTag01();
t.testTag01.Value = myObject.TestTag.Value;
}
//else
//{
// t.testTag01 = new testTag01();
// t.testTag01.NV = "123123";//Not Recorded
//}
This code generates <testTag.01>SomeValue</testTag.01> or <testTag.01 xsi:nil="true"/>.
If I uncomment the ELSE, the result would be: <testTag.01>SomeValue</testTag.01> or <testTag.01 NV="123123" />.
So I have no idea how to get to the format, which is required by the validation tool. Any ideas ?
P.S.
Here is the auto-generated C# class:
/// [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd",
"4.0.30319.33440")] [System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true,
Namespace="http://www.blabla.org")]
public partial class testTag01 {
private string nvField;
private SomeEnum valueField;
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string NV {
get {
return this.nvField;
}
set {
this.nvField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlTextAttribute()]
public SomeEnum Value {
get {
return this.valueField;
}
set {
this.valueField = value;
}
} }
I wouldn't like to alter that part, but I understand it is impossible without doing it. Also I have tried to set SomeEnum to be Nullable. public SomeEnum? Value, but is throwing an exception:
Cannot serialize member 'Value' of type System.Nullable`1[]. XmlAttribute/XmlText cannot be used to encode complex types.
XmlSerializer doesn't directly support binding to elements that simultaneously have xsi:nil="true" along with other attribute values; see Xsi:nil Attribute Binding Support: The nil attribute and other attributes.
Thus, you need to emit the attribute manually.
If you want to be able to generate an element with no content and two attributes, one named NV and the other always being xsi:nil="true", you can modify your testTag01 class to have the NV property as well as a synthetic property having the correct namespace and name:
public class testTag01
{
[XmlAttribute]
public string NV { get; set; }
[XmlAttribute("nil", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string Nil { get { return "true"; } set { } }
}
If you sometimes want to have xsi:nil="true" but at other times want the element to have content corresponding to your SomeEnum, you need to do something a bit more complicated, since the xsi:nil="true" must be suppressed when the element has content:
public class testTag01
{
[XmlAttribute]
public string NV { get; set; }
[XmlAttribute("nil", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string Nil { get { return SomeEnum == null ? "true" : null; } set { } }
public bool ShouldSerializeNil() { return SomeEnum == null; }
[XmlIgnore]
public SomeEnum? SomeEnum { get; set; }
[XmlText]
public string SomeEnumText
{
get
{
if (SomeEnum == null)
return null;
return SomeEnum.Value.ToString();
}
set
{
// See here if one needs to parse XmlEnumAttribute attributes
// http://stackoverflow.com/questions/3047125/retrieve-enum-value-based-on-xmlenumattribute-name-value
value = value.Trim();
if (string.IsNullOrEmpty(value))
SomeEnum = null;
else
{
try
{
SomeEnum = (SomeEnum)Enum.Parse(typeof(SomeEnum), value, false);
}
catch (Exception)
{
SomeEnum = (SomeEnum)Enum.Parse(typeof(SomeEnum), value, true);
}
}
}
}
}
(An element that simultaneously has both xsi:nil="true" and content would be a violation of the XML standard; hopefully you don't have that.)
Then use it like:
public class TestClass
{
[XmlElement("testTag.01")]
public testTag01 TestTag { get; set; }
public static void Test()
{
Test(new TestClass { TestTag = new testTag01 { NV = "123123" } });
Test(new TestClass { TestTag = new testTag01 { NV = "123123", SomeEnum = SomeEnum.SomeValue } });
}
private static void Test(TestClass test)
{
var xml = test.GetXml();
var test2 = xml.LoadFromXML<TestClass>();
Console.WriteLine(test2.GetXml());
Debug.WriteLine(test2.GetXml());
if (test2.TestTag.NV != test.TestTag.NV)
{
throw new InvalidOperationException("test2.TestTag.NV != test.TestTag.NV");
}
}
}
The XML output looks like:
<TestClass xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<testTag.01 NV="123123" xsi:nil="true" />
</TestClass>
Or
<TestClass xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<testTag.01 NV="123123">SomeValue</testTag.01>
</TestClass>
Prototype fiddle using these extension methods:
public static class XmlSerializationHelper
{
public static T LoadFromXML<T>(this string xmlString, XmlSerializer serializer = null)
{
T returnValue = default(T);
using (StringReader reader = new StringReader(xmlString))
{
object result = (serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
if (result is T)
{
returnValue = (T)result;
}
}
return returnValue;
}
public static string GetXml<T>(this T obj, XmlSerializerNamespaces ns = null, XmlWriterSettings settings = null, XmlSerializer serializer = null)
{
using (var textWriter = new StringWriter())
{
settings = settings ?? new XmlWriterSettings() { Indent = true, IndentChars = " " }; // For cosmetic purposes.
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
(serializer ?? new XmlSerializer(typeof(T))).Serialize(xmlWriter, obj, ns);
return textWriter.ToString();
}
}
}
As expected there is no solution for that case out of the box, so I improvise a bit and achieved my goal in a post processing logic.
I am parsing the generated XML and if I am looking for a node with xsi:nil attribute, but without NV attribute - I add NV attribute with default value.
Same for the nodes with NV attribute, but no xsi:nil.
Here is the code:
XmlDocument doc = new XmlDocument();// instantiate XmlDocument and load XML from file
doc.Load("somepath.xml");
//Get the nodes with NV attribute(using XPath) and add xsi:nill to that nodes
XmlNodeList nodes = doc.SelectNodes("//*[#NV]");
foreach (XmlNode node in nodes)
{
XmlAttribute nilAttr = doc.CreateAttribute("nil", "http://www.w3.org/2001/XMLSchema-instance");
nilAttr.Value = "true";
node.Attributes.Append(nilAttr);
}
//Get the nodes with xsi:nill attribute(using XPath) and add NV with default value to that nodes
XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable);
nsManager.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
XmlNodeList nilNodes = doc.SelectNodes("//*[#xsi:nil]", nsManager);
foreach (XmlNode node in nilNodes)
{
XmlAttribute nvAttr = doc.CreateAttribute("NV");
nvAttr.Value = "7701003";
node.Attributes.Append(nvAttr);
}
doc.Save("somepath.xml");
The upper answer makes totally sense, but since these classes are auto-generated I will do it my way with the post processing, cause if the provider changes the XSD schema, my solution doesn't need any extra work. Thanks anyway.
There are various types, in a special case which can be configured in different ways. How to serialize them?
[Serializable]
[XmlRoot("RootXml", Namespace = "")]
public class RootXml
{
object _schemaVersion;
[XmlElement("SchemaVersion")]
public object SchemaVersion
{
get { return _schemaVersion; }
set { _schemaVersion = value; }
}
List<object> _test;
[XmlElement("Test")]
public List<object> Test
{
get { return _test; }
set { _test = value; }
}
public RootXml()
{
}
}
I.e. root can include different objects, and they have to be serialized...
I have a xml-format approximately of such
look:
<?xml version="1.0" encoding="windows-1251"?>
<RootXml>
<SchemaVersion Number="" />
<Report Code="">
<Period Code="" Date="">
<Source ClassCode="" Code="">
<Form Code="">
<Column Num="1" Name="" />
<Column Num="2" Name="" />
<Column Num="3" Name="" />
<Document>
<Data code="11" />
<Data code="12">
<Px Num="1" Value="1" />
<Px Num="2" Value="1" />
<Px Num="4" Value="2" />
<Px Num="5" Value="2" />
</Data>
<Data code="13" />
</Document>
</Form>
</Source>
</Period>
</Report>
</RootXml>
In which some elements can change a little (Document, Document with tags, Document with the status, etc.),
included in others (for example, report incl. in scheme) ... and do not know how to change in the future.
I want to construct a set of "formats" which will also have various components, to be substituted...
Maybe for this purpose you shouldn't use serialization, and to define
set of attributes, and a reflection to process objects and to form xml (approximately just as XmlSerializer)???
You are trying to serialize and deserialize data with polymorphic fields. You have a few options here:
If you know in advance all possible types that might be encountered in a polymorphic field, you can use attributes to tell XmlSerializer how to serialize and deserialize each type. In particular, for a polymorphic field, apply [XmlElement("DerivedElementName", Type = typeof(DerivedElementType))] for every derived type that might be encountered.
For instance, simplifying your RootXml class somewhat, the following allows for two different types of report to be serialized:
[XmlRoot("Report", Namespace = "")]
public class Report
{
[XmlAttribute]
public string Code { get; set; }
[XmlElement]
public decimal TotalCost { get; set; }
}
[XmlRoot("DifferentReport", Namespace = "fuuuu")]
public class DifferentReport
{
public DifferentReport() { }
public DifferentReport(string code, string value)
{
this.Code = code;
this.Value = value;
}
[XmlAttribute]
public string Code { get; set; }
[XmlText]
public string Value { get; set; }
}
[XmlRoot("RootXml", Namespace = "")]
public class RootXml
{
public RootXml() { }
object _test;
[XmlElement("Report", Type=typeof(Report))]
[XmlElement("DifferentReport", Type = typeof(DifferentReport))]
public object Data
{
get { return _test; }
set { _test = value; }
}
}
And then later, both of the following can be serialized and deserialized:
var root1 = new RootXml { Data = new Report { Code = "a code", TotalCost = (decimal)101.01 } };
var root2 = new RootXml { Data = new DifferentReport { Code = "a different code", Value = "This is the value of the report" } };
Note that you can use the same technique with polymorphic lists, in which case the serializer will expect sequences of elements with the specified names:
[XmlRoot("RootXml", Namespace = "")]
public class RootXml
{
public RootXml() { }
List<object> _test;
[XmlElement("Report", Type=typeof(Report))]
[XmlElement("DifferentReport", Type = typeof(DifferentReport))]
public List<object> Data
{
get { return _test; }
set { _test = value; }
}
}
If the XML could be anything and you don't know what it might contain (because you must deserialize XML from the future versions and reserialize it without data loss, for example) you may need to load your XML into an XDocument then manually search for data using Linq-to-XML. For information on how to do this, see here: Basic Queries (LINQ to XML).
You could adopt a hybrid approach where you load the XML into an XDocument, then deserialize and serialize familiar portions with XmlSerializer, using the following extension methods:
public static class XObjectExtensions
{
public static T Deserialize<T>(this XContainer element)
{
return element.Deserialize<T>(new XmlSerializer(typeof(T)));
}
public static T Deserialize<T>(this XContainer element, XmlSerializer serializer)
{
using (var reader = element.CreateReader())
{
object result = serializer.Deserialize(reader);
if (result is T)
return (T)result;
}
return default(T);
}
public static XElement Serialize<T>(this T obj, bool omitStandardNamespaces = true)
{
return obj.Serialize(new XmlSerializer(obj.GetType()), omitStandardNamespaces);
}
public static XElement Serialize<T>(this T obj, XmlSerializer serializer, bool omitStandardNamespaces = true)
{
var doc = new XDocument();
using (var writer = doc.CreateWriter())
{
XmlSerializerNamespaces ns = null;
if (omitStandardNamespaces)
(ns = new XmlSerializerNamespaces()).Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
serializer.Serialize(writer, obj, ns);
}
return doc.Root;
}
}
Then use them to pick out and deserialize known portions of your XML as follows:
var doc = XDocument.Parse(xml);
var reportElement = doc.Root.Element("Report");
if (reportElement != null)
{
var report1 = doc.Root.Element("Report").Deserialize<Report>();
// Do something with the report.
// Create a different report
var differentReport = new DifferentReport { Code = report1.Code + " some more code", Value = "This is the value of the report" };
var differentElement = differentReport.Serialize();
reportElement.AddAfterSelf(differentElement);
reportElement.Remove();
}
OK, given that you are using c# 2.0, you can load your Xml into an XmlDocument and use it as described here: Process XML Data Using the DOM Model. This is a precursor API to Linq-to-XML and is somewhat harder to work with -- but nevertheless totally functional.
You can also adopt the hybrid approach and use XmlSerializer to deserialize and re-serialize known chunks of an XmlDocument. Here are some extension methods for this purpose -- but since you're using c# 2.0, you must remove the this keyword:
public static class XmlNodeExtensions
{
public static XmlElement SerializeToXmlElement<T>(this T o, XmlElement parent)
{
return SerializeToXmlElement(o, parent, new XmlSerializer(o.GetType()));
}
public static XmlElement SerializeToXmlElement<T>(this T o, XmlElement parent, XmlSerializer serializer)
{
int oldCount = parent.ChildNodes.Count;
XPathNavigator navigator = parent.CreateNavigator();
using (XmlWriter writer = navigator.AppendChild())
{
writer.WriteComment(""); // Kludge suggested here: https://social.msdn.microsoft.com/Forums/en-US/9ff20a3c-913d-4c6f-a18a-c10040290862/how-to-xmlserialize-directly-into-xmldocument?forum=asmxandxml
serializer.Serialize(writer, o);
}
XmlElement returnedElement = null;
for (int i = parent.ChildNodes.Count - 1; i >= oldCount; i--)
{
XmlComment comment = parent.ChildNodes[i] as XmlComment;
if (comment != null)
{
parent.RemoveChild(comment);
}
else
{
returnedElement = (parent.ChildNodes[i] as XmlElement) ?? returnedElement;
}
}
return returnedElement;
}
public static XmlDocument SerializeToXmlDocument<T>(this T o)
{
return SerializeToXmlDocument(o, new XmlSerializer(o.GetType()));
}
public static XmlDocument SerializeToXmlDocument<T>(this T o, XmlSerializer serializer)
{
XmlDocument doc = new XmlDocument();
using (XmlWriter writer = doc.CreateNavigator().AppendChild())
serializer.Serialize(writer, o);
return doc;
}
public static T Deserialize<T>(this XmlElement element)
{
return Deserialize<T>(element, new XmlSerializer(typeof(T)));
}
public static T Deserialize<T>(this XmlElement element, XmlSerializer serializer)
{
using (var reader = new XmlNodeReader(element))
return (T)serializer.Deserialize(reader);
}
}
Given those methods, you can do things like:
// Load the document from XML
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
// Find all nodes with name "Report"
foreach (XmlElement reportNode in doc.SelectNodes("/RootXml/Report"))
{
// Deserialize as a Report
Report report = XmlNodeExtensions.Deserialize<Report>(reportNode);
// Do something with it
// Create a new Report, based on the original report.
DifferentReport differentReport = new DifferentReport(report.Code + " some more code", "This is the value of the report"); ;
// Add the new report to the children of RootXml
XmlElement newNode = XmlNodeExtensions.SerializeToXmlElement(differentReport, (XmlElement)reportNode.ParentNode);
}
As you can see this is quite similar to what is possible with Linq-to-XML.
How can I change XML-element name for field inherited from base class while doing serialization?
For example I have next base class:
public class One
{
public int OneField;
}
Serialization code:
static void Main()
{
One test = new One { OneField = 1 };
var serializer = new XmlSerializer(typeof (One));
TextWriter writer = new StreamWriter("Output.xml");
serializer.Serialize(writer, test);
writer.Close();
}
I get what I need:
<?xml version="1.0" encoding="utf-8"?>
<One xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<OneField>1</OneField>
</One>
Now I have created new class inherited from A with additional field and custom XML element name for it:
public class Two : One
{
[XmlElement("SecondField")]
public int TwoField;
}
Serialization code:
static void Main()
{
Two test = new Two { OneField = 1, TwoField = 2 };
var serializer = new XmlSerializer(typeof (Two));
TextWriter writer = new StreamWriter("Output.xml");
serializer.Serialize(writer, test);
writer.Close();
}
As a result I get next output:
<?xml version="1.0" encoding="utf-8"?>
<Two xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<OneField>1</OneField>
<SecondField>2</SecondField>
</Two>
The problem is that I want to change OneField in this output to FirstField without touching base class code (because I will use it too and the names must be original). How can I accomplish this?
Thank you.
Try this:
public class Two : One
{
private static XmlAttributeOverrides xmlOverrides;
public static XmlAttributeOverrides XmlOverrides
{
get
{
if (xmlOverrides == null)
{
xmlOverrides = new XmlAttributeOverrides();
var attr = new XmlAttributes();
attr.XmlElements.Add(new XmlElementAttribute("FirstField"));
xmlOverrides.Add(typeof(One), "OneField", attr);
}
return xmlOverrides;
}
}
[XmlElement("SecondField")]
public string TwoField;
}
And your serializer init is a lot easier:
var xmls = new System.Xml.Serialization.XmlSerializer(typeof(Two), Two.XmlOverrides);
Here's a workaround: Override the fields in the subclass and mark the overriden field with whatever name you need. For example,
class One
{
public int OneField { get; set; }
}
class Two : One
{
[XmlElement("FirstField")]
public new int OneField
{
get { return base.OneField; }
set { base.OneField = value; }
}
}
I am using VSTS2008 + C# + .Net 3.0. I am using below code to serialize XML, and my object contains array type property, and I want to add an additional elements' layer ("MyInnerObjectProperties" element layer in my expected results below, and I want to make "MyInnerObjectProperties" element as parent element for all MyInnerObjectProperty element). Any ideas?
Current serialized XML,
<?xml version="1.0"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyObjectProperty>
<MyInnerObjectProperty>
<ObjectName>Foo Type</ObjectName>
</MyInnerObjectProperty>
<MyInnerObjectProperty>
<ObjectName>Goo Type</ObjectName>
</MyInnerObjectProperty>
</MyObjectProperty>
</MyClass>
Expected serialized XML,
<?xml version="1.0"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyObjectProperty>
<MyInnerObjectProperties>
<MyInnerObjectProperty>
<ObjectName>Foo Type</ObjectName>
</MyInnerObjectProperty>
<MyInnerObjectProperty>
<ObjectName>Goo Type</ObjectName>
</MyInnerObjectProperty>
</MyInnerObjectProperties>
</MyObjectProperty>
</MyClass>
Current code,
public class MyClass
{
private MyObject[] _myObjectProperty;
[XmlElement(IsNullable=false)]
public MyObject[] MyObjectProperty
{
get
{
return _myObjectProperty;
}
set
{
_myObjectProperty = value;
}
}
}
public class MyObject
{
private MyInnerObject[] _myInnerObjectProperty;
[XmlElement(IsNullable = false)]
public MyInnerObject[] MyInnerObjectProperty
{
get
{
return _myInnerObjectProperty;
}
set
{
_myInnerObjectProperty = value;
}
}
}
public class MyInnerObject
{
public string ObjectName;
}
public class Program
{
static void Main(string[] args)
{
XmlSerializer s = new XmlSerializer(typeof(MyClass));
FileStream fs = new FileStream("foo.xml", FileMode.Create);
MyClass instance = new MyClass();
instance.MyObjectProperty = new MyObject[1];
instance.MyObjectProperty[0] = new MyObject();
instance.MyObjectProperty[0].MyInnerObjectProperty = new MyInnerObject[2];
instance.MyObjectProperty[0].MyInnerObjectProperty[0] = new MyInnerObject();
instance.MyObjectProperty[0].MyInnerObjectProperty[0].ObjectName = "Foo Type";
instance.MyObjectProperty[0].MyInnerObjectProperty[1] = new MyInnerObject();
instance.MyObjectProperty[0].MyInnerObjectProperty[1].ObjectName = "Goo Type";
s.Serialize(fs, instance);
return;
}
}
make use of the XmlArrayItemAttribute
[XmlArray("MyInnerObjectProperties")]
[XmlArrayItemAttribute("MyInnerObjectProperty", typeof(MyInnerObject), IsNullable = false)]
public MyInnerObject[] MyInnerObjectProperty
{
get
{
return _myInnerObjectProperty;
}
set
{
_myInnerObjectProperty = value;
}
}
...
[XmlArray(IsNullable = false)]
[XmlArrayItem("MyInnerObjectProperties")]
public MyObject[] MyObjectProperty
{
get
{
return _myObjectProperty;
}
set
{
_myObjectProperty = value;
}
}
...