I've an xml document like this
<?xml version="1.0"?>
<data>
<myassembly name="t1" folder="1">
<myassembly name="t1.1" folder="0" />
<myassembly name="t1.2" folder="0">
<myassembly name="t1.2.1" folder="0"/>
</myassembly>
<myassembly name="t2" folder="0"/>
<myassembly name="t3" folder="0">
<myassembly name="t3.1" folder="0"/>
<myassembly name="t3.2" folder="0"/>
</myassembly>
</myassembly>
</data>
And two classes to read the xml data:
class data{
[XmlElement("myassembly")]
MyAssembly myassembly;
}
class MyAssembly{
[XmlAttribute("name")]
string name;
[XmlAttribute("folder")]
string folder;
[XmlArrayItem("myassembly")]
MyAssembly[] myassembly;
}
I want to have this array list structure:
data:
assembly:
-name: t1
-folder: 1
-myassembly[4]:
[0]-name: t1.1
[0]-folder: 0
[0]-myassembly: null
[1]-name: t1.2
[1]-folder: 0
[1]-myassembly: [4]
[0]-name: t1.2.1
[0]-folder: 0
[0]-myassembly: null
[2]-name: t2
[2]-folder: 0
[2]-myassembly: null
[3]-name: t3
[3]-folder: 0
[3]-myassembly: [2]
[0]-name: t3.1
[0]-folder: 0
[0]-myassembly: null
[1]-name: t3.2
[1]-folder: 0
[1]-myassembly: null
But: with my attributes, i can't get this array list.
I hope, I have described it sufficiently.
regard
raiserle
Assuming you are using XmlSerializer as seems likely, you have the following issues:
XmlSerializer only serializes public types and members. All of your types and members are private.
The public MyAssembly[] myassembly member of MyAssembly needs to be marked with [XmlElement("myassembly")]. This indicates that the array should be serialized as a sequence of elements named <myassembly> without an outer container element. By default an outer container element is used when serializing a collection.
Thus your classes should be (converting public fields to properties):
public class data
{
[XmlElement("myassembly")]
public MyAssembly myassembly { get; set; }
}
public class MyAssembly
{
[XmlAttribute("name")]
public string name { get; set; }
[XmlAttribute("folder")]
public string folder { get; set; }
[XmlElement("myassembly")]
public MyAssembly[] myassembly { get; set; }
}
Then you can serialize and deserialize using these extension methods:
public static class XmlSerializationHelper
{
public static T LoadFromXML<T>(this string xmlString)
{
using (StringReader reader = new StringReader(xmlString))
{
return (T)new XmlSerializer(typeof(T)).Deserialize(reader);
}
}
public static string GetXml<T>(this T obj, bool omitStandardNamespaces = false)
{
XmlSerializerNamespaces ns = null;
if (omitStandardNamespaces)
{
ns = new XmlSerializerNamespaces();
ns.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
}
using (var textWriter = new StringWriter())
{
var settings = new XmlWriterSettings() { Indent = true, IndentChars = " " }; // For cosmetic purposes.
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
new XmlSerializer(obj.GetType()).Serialize(xmlWriter, obj, ns);
return textWriter.ToString();
}
}
}
Using them as follows:
var data = xmlString.LoadFromXML<data>();
Sample fiddle.
Related
I have a class that could be serialized on its own, for example:
[XmlRoot("NameOfMyRoot", Namespace = "myNamespace")]
public class Inner
{
public Inner(){}
public string SomeString { get; set; }
}
Which is perfectly serializing into (I am using ns alias for myNamespace, see full demo)
<?xml version="1.0" encoding="utf-16"?>
<NameOfMyRoot xmlns:ns="myNamespace">
<ns:SomeString></ns:SomeString>
</NameOfMyRoot>
Now I want this object to be part of another one (the wrapper):
[XmlRoot("Root")]
public class Outer<T>
{
public T Property{ get; set; }
public Outer(){}
public Outer(T inner)
{
Property = inner;
}
}
Which gives me this:
<?xml version="1.0" encoding="utf-16"?>
<Root xmlns:ns="myNamespace">
<Property>
<ns:SomeString></ns:SomeString>
</Property>
</Root>
What I want is just embedding inner object as-is into its parent, like so:
<?xml version="1.0" encoding="utf-16"?>
<Root>
<NameOfMyRoot xmlns:ns="myNamespace">
<ns:SomeString></ns:SomeString>
</NameOfMyRoot>
</Root>
Notice that namespace should not move to root, and I can't specify element's name, since there will be many different types.
Fiddle with full example.
Of cource, I can just serialize them separately and combine through some nasty string manipulation, but I hope there is a neat way to achieve this somehow.
There's no easy way to do that. You can do it at runtime, though:
// perhaps discover these details at runtime
var attribs = new XmlAttributeOverrides();
attribs.Add(typeof(Outer<Inner>), "Property", new XmlAttributes
{
XmlElements = { new XmlElementAttribute {ElementName = "NameOfMyRoot" } }
});
attribs.Add(typeof(Inner), "SomeString", new XmlAttributes
{
XmlElements = { new XmlElementAttribute { Namespace = "myNamespace"} }
});
var ser = new XmlSerializer(typeof(Outer<Inner>), attribs);
var obj = new Outer<Inner> { Property = new Inner { SomeString = "abc" } };
ser.Serialize(Console.Out, obj);
Note that you must store and re-use such a serializer; for simple new XmlSerializer() examples, the serializer is cached internally and re-used; however, for non-trivial cases like this, a new dynamic type is emitted each time, so you will leak memory (it won't be unloaded).
That gives you:
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<NameOfMyRoot>
<SomeString xmlns="myNamespace">abc</SomeString>
</NameOfMyRoot>
</Root>
If you don't want the two xmlns alias declaration, those can be removed separately, but they do not change the meaning, at least to a compliant reader. Likewise, the ns can be added as an alias:
var obj = new Outer<Inner> { Property = new Inner { SomeString = "abc" } };
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
ns.Add("ns", "myNamespace");
ser.Serialize(Console.Out, obj, ns);
which gives:
<Root xmlns:ns="myNamespace">
<NameOfMyRoot>
<ns:SomeString>abc</ns:SomeString>
</NameOfMyRoot>
</Root>
As an alternative approach:
using System;
using System.Xml;
using System.Xml.Serialization;
[XmlRoot("NameOfMyRoot")]
public class Inner
{
public Inner() { }
[XmlElement(Namespace = "myNamespace")]
public string SomeString { get; set; }
}
static class Program
{
static void Main()
{
using (XmlWriter writer = XmlWriter.Create(Console.Out))
{
writer.WriteStartDocument(false);
writer.WriteStartElement("Root");
var ser = new XmlSerializer(typeof(Inner));
var obj = new Inner { SomeString = "abc" };
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
ns.Add("ns", "myNamespace");
ser.Serialize(writer, obj, ns);
writer.WriteEndElement();
writer.WriteEndDocument();
}
}
}
What I've done here is to manually write the root node, and only get the XmlSerializer to write the inner content. Note that I had to change the attributes aroung SomeString to make it work in the way you expect (where it is the string that has the namespace, not the object).
TLDR version
I am serializing objects into XML to match a schema provided by a third party. Their validator requires one of the child objects to have a namespace explicitly declared which matches it's ancestor's namespace . The data is complex enough that I don't want to roll my own serializer for this purpose. How can I force the XMLSerializer class to explicitly render a namespace even though it is technically redundant?
Full version
I am running into an issue where the CoreItemsMkt namespace is not rendered by the XMLSerializer. I believe that this is because both the attribute and the namespaces exactly match the ancestor's namespace that it is inheriting from, therefore the serializer omits it - however, the site validator that this file gets submitted to requires it.
For example:
<?xml version="1.0" encoding="utf-8"?>
<FSAMarketsFeed xmlns="http://www.fsa.gov.uk/XMLSchema/FSAMarketsFeed-v1-2">
<FSAFeedHeader xmlns="http://www.fsa.gov.uk/XMLSchema/FSAFeedCommon-v1-2">
[...contents omitted, this item appears once...]
</FSAFeedHeader>
<FSAMarketsFeedMsg>
<CoreItemsMkt xmlns="http://www.fsa.gov.uk/XMLSchema/FSAMarketsFeed-v1-2"> <!--//This namespace is the issue//-->
[...contents omitted, this item appears multiple times...]
</CoreItemsMkt?
</FSAMarketsFeedMsg>
<FSAMarketsFeedMsg>
<CoreItemsMkt xmlns="http://www.fsa.gov.uk/XMLSchema/FSAMarketsFeed-v1-2"> <!--//This namespace is the issue//-->
[...contents omitted, this item appears multiple times...]
</CoreItemsMkt?
</FSAMarketsFeedMsg>
I'm serializing with a method like this:
var path = GetFilePath();
var ns = new XmlSerializerNamespaces();
ns.Add("", "http://www.fsa.gov.uk/XMLSchema/FSAMarketsFeed-v1-2");
var ser = new XmlSerializer(typeof(FSAMarketsFeed));
var settings = new XmlWriterSettings
{ Encoding = Encoding.UTF8, Indent = true, IndentChars = "\t", NamespaceHandling = NamespaceHandling.Default };
using (var writer = XmlWriter.Create(path, settings))
{
ser.Serialize(writer, GetDataToSerialize(), ns);
}
My root class is defined as:
[XmlType(AnonymousType = true)]
[XmlRoot(Namespace = "http://www.fsa.gov.uk/XMLSchema/FSAMarketsFeed-v1-2", IsNullable = false)]
public class FSAMarketsFeed
{
public FSAMarketsFeed()
{
FSAMarketsFeedMsg = new FSAMarketsFeedMsg[0];
}
[XmlElement("FSAFeedHeader", IsNullable = true, Namespace = "http://www.fsa.gov.uk/XMLSchema/FSAFeedCommon-v1-2")]
public FSAFeedHeader FeedHeader { get; set; }
[XmlElement("FSAMarketsFeedMsg")]
public FSAMarketsFeedMsg[] FSAMarketsFeedMsg { get; set; }
}
The working feed header class:
[XmlType(AnonymousType = true)]
public class FSAFeedHeader
{
[XmlElement("FeedTargetSchemaVersion", IsNullable = true)]
public string FeedTargetSchemaVersion { get; set; }
[XmlElement("Submitter", IsNullable = true)]
public Submitter Submit { get; set; }
[XmlElement("ReportDetails", IsNullable = true)]
public ReportDetails ReportDetail { get; set; }
}
The parent Feed Message Class:
[XmlType(AnonymousType = true)]
public class FSAMarketsFeedMsg
{
[XmlElement("CoreItemsMkt", IsNullable = true, Namespace = "http://www.fsa.gov.uk/XMLSchema/FSAMarketsFeed-v1-2")]
public CoreItemsMkt CoreMarket { get; set; }
[XmlElement("Transaction", IsNullable = true)]
public Transaction Trans { get; set; }
}
Finally, the CoreItemsMkt class which is failing to render its namespace:
[XmlType(Namespace = "http://www.fsa.gov.uk/XMLSchema/FSAMarketsFeed-v1-2", AnonymousType = true)]
public class CoreItemsMkt
{
//[... Children omitted ...]]
}
Tried so far:
Using XMmlType(AnonymousType = true) to try to break the inheritance chain
Explicitly setting xmlns as an XmlAttributeAttribute w/ a hard coded value.
Setting and removing XmlType(Namespace = "http://www.fsa.gov.uk/XMLSchema/FSAMarketsFeed-v1-2") on CoreItemsMkt
Adding and removing XmlElement(Namespace = "the value") on the FSAMArketsFeedMsg's property.
Implementing ISerializable on CoreItmsMkt (Couldn't quite figure out how to get that to work though.)
Stack overflow searches - I've found 1 similar question that was answered with "This is unsupported, change your output namespace." Unfortunately, that answer doesn't work for me.
So, without hand rendering this, is there any way to force the XmlSerializer class to render those namespace attributes on CoreItmsMkt?
Try to use custom XML writer.
public class CustomWriter : XmlTextWriter
{
public CustomWriter(TextWriter writer) : base(writer) { }
public CustomWriter(Stream stream, Encoding encoding) : base(stream, encoding) { }
public CustomWriter(string filename, Encoding encoding) : base(filename, encoding) { }
public override void WriteStartElement(string prefix, string localName, string ns)
{
base.WriteStartElement(prefix, localName, ns);
if (localName == "CoreItemsMkt")
{
base.WriteAttributeString("xmlns",
"http://www.fsa.gov.uk/XMLSchema/FSAMarketsFeed-v1-2");
//base.WriteAttributeString("xmlns", ns);
}
}
}
The custom writer forcibly adds the required attribute to every element with the CoreItemsMkt name.
Usage
using (var customWriter = new CustomWriter(path, Encoding.UTF8))
{
customWriter.Formatting = Formatting.Indented;
customWriter.Indentation = 1;
customWriter.IndentChar = '\t';
ser.Serialize(customWriter, GetDataToSerialize(), ns);
}
You would like to be able to force XmlSerializer to emit redundant xmlns= attributes when serializing specified nested elements. Unfortunately, I don't know of any API to make this happen automatically. You also wrote The data is complex enough that I don't want to roll my own serializer for this purpose so you don't want to have to implement IXmlSerializable on FSAMarketsFeedMsg. (ISerializable is not used by XmlSerializer so implementing it will not help.) Thus you're going to want to do something "semi-manual". There are at least a couple of options for this.
Option 1: Serialize to a temporary XDocument then fix the attributes.
With this solution, you serialize to a temporary XDocument in memory, then add an XAttribute for each desired redundant xmlns=, as follows:
// Generate the temporary XDocument
var ns = Namespaces.GetFSAMarketsFeedNamespace();
var doc = data.SerializeToXDocument(null, ns);
var root = doc.Root;
// Add redundate xmlns= attributes
var name = XName.Get("CoreItemsMkt", Namespaces.FSAMarketsFeed);
var query = doc.Descendants(name); // Could be a more complex query, possibly even an XPath query.
foreach (var element in query)
{
if (!element.Attributes().Any(a => a.IsNamespaceDeclaration))
{
var prefix = element.GetPrefixOfNamespace(element.Name.Namespace);
if (string.IsNullOrEmpty(prefix))
element.Add(new XAttribute("xmlns", element.Name.NamespaceName));
else
element.Add(new XAttribute(XNamespace.Xmlns + prefix, element.Name.NamespaceName));
}
}
// Write the XDocument to disk.
Using the static extension classes:
public static class Namespaces
{
public const string FSAMarketsFeed = #"http://www.fsa.gov.uk/XMLSchema/FSAMarketsFeed-v1-2";
public const string FSAFeedCommon = #"http://www.fsa.gov.uk/XMLSchema/FSAFeedCommon-v1-2";
public static XmlSerializerNamespaces GetFSAMarketsFeedNamespace()
{
var ns = new XmlSerializerNamespaces();
ns.Add("", Namespaces.FSAMarketsFeed);
return ns;
}
}
public static class XObjectExtensions
{
public static T Deserialize<T>(this XContainer element, XmlSerializer serializer)
{
using (var reader = element.CreateReader())
{
serializer = serializer ?? new XmlSerializer(typeof(T));
object result = serializer.Deserialize(reader);
if (result is T)
return (T)result;
}
return default(T);
}
public static XDocument SerializeToXDocument<T>(this T obj, XmlSerializer serializer, XmlSerializerNamespaces ns)
{
var doc = new XDocument();
using (var writer = doc.CreateWriter())
{
serializer = serializer ?? new XmlSerializer(obj.GetType());
serializer.Serialize(writer, obj, ns);
}
return doc;
}
public static XElement SerializeToXElement<T>(this T obj, XmlSerializer serializer, XmlSerializerNamespaces ns)
{
var doc = obj.SerializeToXDocument(serializer, ns);
var element = doc.Root;
if (element != null)
element.Remove();
return element;
}
}
Which produces the XML:
<FSAMarketsFeed xmlns="http://www.fsa.gov.uk/XMLSchema/FSAMarketsFeed-v1-2">
<FSAFeedHeader xmlns="http://www.fsa.gov.uk/XMLSchema/FSAFeedCommon-v1-2">
<FeedTargetSchemaVersion>value of FeedTargetSchemaVersion</FeedTargetSchemaVersion>
</FSAFeedHeader>
<FSAMarketsFeedMsg>
<CoreItemsMkt xmlns="http://www.fsa.gov.uk/XMLSchema/FSAMarketsFeed-v1-2" />
</FSAMarketsFeedMsg>
</FSAMarketsFeed>
Option 2: Do a nested serialization of CoreMarket using [XmlAnyElement] on its containing type.
Using an [XmlAnyElement] property, a type can serialize and deserialize any arbitrary child element. You can use this functionality to do a nested serialization of CoreMarket with the necessary namespace declarations included.
To do this, modify FSAMarketsFeedMsg as follows:
[XmlType(AnonymousType = true)]
public class FSAMarketsFeedMsg
{
[XmlIgnore]
public CoreItemsMkt CoreMarket { get; set; }
[XmlAnyElement(Name = "CoreItemsMkt", Namespace = Namespaces.FSAMarketsFeed)]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
public XElement CoreMarketXml
{
get
{
return (CoreMarket == null ? null : XObjectExtensions.SerializeToXElement(CoreMarket,
XmlSerializerFactory.Create(typeof(CoreItemsMkt), "CoreItemsMkt", Namespaces.FSAMarketsFeed),
Namespaces.GetFSAMarketsFeedNamespace()));
}
set
{
CoreMarket = (value == null ? null : XObjectExtensions.Deserialize<CoreItemsMkt>(value,
XmlSerializerFactory.Create(typeof(CoreItemsMkt), "CoreItemsMkt", Namespaces.FSAMarketsFeed)));
}
}
// Remainder of properties are left unchanged.
}
In addition to the static extension classes from Option 1, you will need the following to avoid a substantial memory leak:
public static class XmlSerializerFactory
{
static readonly Dictionary<Tuple<Type, string, string>, XmlSerializer> table;
static readonly object padlock;
static XmlSerializerFactory()
{
table = new Dictionary<Tuple<Type, string, string>, XmlSerializer>();
padlock = new object();
}
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)
{
var key = Tuple.Create(serializedType, rootName, rootNamespace);
XmlSerializer serializer;
if (!table.TryGetValue(key, out serializer))
{
var attr = (string.IsNullOrEmpty(rootName) ? new XmlRootAttribute() { Namespace = rootNamespace } : new XmlRootAttribute(rootName) { Namespace = rootNamespace });
serializer = table[key] = new XmlSerializer(serializedType, attr);
}
return serializer;
}
}
}
Note that the [XmlAnyElement] property will be called for all unknown elements, so if your XML for some reason has unexpected elements, you may get an exception thrown from XObjectExtensions.Deserialize because the root element name is wrong. You may want to catch and ignore exceptions from this method if that is a possibility.
Serialize to disk as you are currently doing. The redundant xmlns= attributes will be present as in Option 1.
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.
Can some one help for a strange Serialization issue which I getting in a one environment and it working fine in all environments..but it failed a particular environment..so I specified as strange here
Code
public SomeType[] Deserialize(string xml)
{
XmlSerializer serializer = new XmlSerializer(typeof(SomeType[]));
StringReader stringReader = new StringReader(xml);
SomeType[] types = (SomeType[])serializer.Deserialize(stringReader);
stringReader.Close();
return types;
}
Serialized XML data:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfSomeType>
<SomeType>
<Field1>val</Field1>
<Field2>val</Field2>
<Field3>val</Field3>
</SomeType>
</ArrayOfSomeType>
And the exception is:
System.InvalidCastException: Unable to cast object of type 'SomeType[]' to type 'SomeType'.
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterSomeType.Write11_SomeType(Object o)
The error here is in the Serialize method that you don't show. Your Deserialize method works without error. The following Serialize method works fine:
static string Serialize(SomeType[] values)
{
using (var sw = new StringWriter())
{
var serializer = new XmlSerializer(typeof(SomeType[]));
serializer.Serialize(sw, values);
return sw.ToString();
}
}
If I had to guess, you have the following:
var serializer = new XmlSerializer(typeof(SomeType));
If you want the exact same output without unnecessary namespace alias declarations, change the method to include:
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("","");
serializer.Serialize(sw, values, ns);
Edit showing the current code working fine:
using System;
using System.IO;
using System.Xml.Serialization;
public class SomeType
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
}
public class MyTest
{
public static SomeType[] Deserialize(string xml)
{
XmlSerializer serializer = new XmlSerializer(typeof(SomeType[]));
StringReader stringReader = new StringReader(xml);
SomeType[] types = (SomeType[])serializer.Deserialize(stringReader);
stringReader.Close();
return types;
}
public static void Main()
{
string xml = #"<?xml version=""1.0"" encoding=""utf-8""?>
<ArrayOfSomeType>
<SomeType>
<Field1>val</Field1>
<Field2>val</Field2>
<Field3>val</Field3>
</SomeType>
</ArrayOfSomeType>";
var items = Deserialize(xml);
foreach (var item in items)
{
Console.WriteLine("{0}, {1}, {2}",
item.Field1, item.Field2, item.Field3);
}
}
}
I need to generate an XML that looks like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<inboundMessage xmlns="http://www.myurl.net">
<header>
<password>mypwd</password>
<subscriberId>myuser</subscriberId>
</header>
<message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="myType">
<eventDate>2012-09-05T12:13:45.561-05:00</eventDate>
<externalEventId />
<externalId>SomeIdC</externalId>
</message>
</inboundMessage>
The problem is that I don't know how to include the xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="myType" in my tag. My class that I need to serialize is this:
[XmlType("inboundMessage")]
[XmlRoot(Namespace = "http://www.myurl.net")]
public class InboundMessage
{
[XmlElement(ElementName = "header")]
public Header _header;
[XmlElement(ElementName = "message")]
public List<MyType> _messages;
}
What XmlAttributes do I need to add to my "_messages" member to make it serialize the way I want?
TIA,
Ed
Use XmlAttribute like this:
public class MyType
{
[XmlAttribute("type", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string Type { get; set; }
}
A coleague of mine came up with a somewhat similar solution, yet more complete. MyType had two properties added:
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces Namespaces { get; set; }
[XmlAttribute("type", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string Type
{
get { return _type; }
set { _type = value; }
}
with _type being defined like this:
private string _type = "myType";
Then the serialization was done in a different way:
// declare an XmlAttributes object, indicating that the namespaces declaration should be kept
var atts = new XmlAttributes { Xmlns = true };
// declare an XmlAttributesOverrides object and ...
var xover = new XmlAttributeOverrides();
// ... add the XmlAttributes object with regard to the "Namespaces" property member of the "Message" type
xover.Add(typeof(MyType), "Namespaces", atts);
// set the Namespaces property for each message in the payload body
var messageNamespaces = new XmlSerializerNamespaces();
messageNamespaces.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
foreach (var message in myInboundMessage._messages)
{
message.Namespaces = messageNamespaces;
}
// create a serializer
var serializer = new XmlSerializer(object2Serialize.GetType(), xover);
// add the namespaces for the root element
var rootNamespaces = new XmlSerializerNamespaces();
rootNamespaces.Add("", "http://www.myurl.net");
// serialize and extract the XML as text
string objectAsXmlText;
using (var stream = new MemoryStream())
using (var xmlTextWriter = new XmlTextWriter(stream, null))
{
serializer.Serialize(xmlTextWriter, object2Serialize, rootNamespaces);
stream.Seek(0, SeekOrigin.Begin);
var buffer = new byte[stream.Length];
stream.Read(buffer, 0, (int)stream.Length);
objectAsXmlText = Encoding.UTF8.GetString(buffer);
}
Finally, the InboundMessage was decorated like this:
[XmlRoot("inboundMessage", Namespace = "http://www.myurl.net")]
With this approach I get exactely what I need.