How to create XML with multiple namespace attributes in C# - c#

How can I generate, for example, this XML in C#
<?xml version='1.0'?>
<oneshot xmlns='http://www.w3.org/2002/xforms' xmlns:dm='http://mobileforms.foo.com/xforms' xmlns:h='http://www.w3.org/1999/xhtml' xmlns:xsd='http://www.w3.org/2001/XMLSchema'>
<dm:form_namespace>Foo</dm:form_namespace>
<Days>6</Days>
<Leave_Type>Option 3</Leave_Type>
</oneshot>
I'm specifically struggling with the xmlns:dm declaration. Any ideas?

Your best bet (read: minimal amount of hacks) is probably going to be a custom IXmlSerializable implementation; you can get part-way to what you want via combinations of XmlRootAttribute, XmlElementAttribute, etc, like so:
[Serializable]
[XmlRoot("oneshot")]
public class OneShot
{
[XmlElement("form_namespace", Namespace="http://mobileforms.foo.com/xforms")]
public string FormNamespace {get; set;}
[XmlElement("Days")]
public int Days {get; set;}
[XmlElement("Leave_Type")]
public string LeaveType {get; set;}
Which will generate something like:
<?xml version="1.0" encoding="utf-16"?>
<oneshot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<form_namespace xmlns="http://mobileforms.foo.com/xforms">Foo</form_namespace>
<Days>6</Days>
<Leave_Type>Option 3</Leave_Type>
</oneshot>
But if you implement IXmlSerializable, you have full control:
public class OneShot : IXmlSerializable
{
public string FormNamespace {get; set;}
public int Days {get; set;}
public string LeaveType {get; set;}
#region IXmlSerializable
public void WriteXml (XmlWriter writer)
{
writer.WriteStartElement("oneshot");
writer.WriteAttributeString("xmlns", null, "http://www.w3.org/2002/xforms");
writer.WriteAttributeString("xmlns:dm", null, "http://mobileforms.foo.com/xforms");
writer.WriteAttributeString("xmlns:h", null, "http://www.w3.org/1999/xhtml");
writer.WriteAttributeString("xmlns:xsd", null, "http://www.w3.org/2001/XMLSchema");
writer.WriteElementString("dm:form_namespace", null, FormNamespace);
writer.WriteElementString("Days", Days.ToString());
writer.WriteElementString("Leave_Type", LeaveType);
writer.WriteEndElement();
}
public void ReadXml (XmlReader reader)
{
// populate from xml blob
}
public XmlSchema GetSchema()
{
return(null);
}
#endregion
}
Which gives you:
<?xml version="1.0" encoding="utf-16"?>
<OneShot>
<oneshot xmlns="http://www.w3.org/2002/xforms" xmlns:dm="http://mobileforms.foo.com/xforms" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<dm:form_namespace>Foo</dm:form_namespace>
<Days>6</Days>
<Leave_Type>Option 3</Leave_Type>
</oneshot>
</OneShot>

One way to write XML with nodes in different namespaces is to use 4-argument version of XmlWriter.WriteElementString to explicitly specify namespace and prefixes the way you want:
var s = new StringWriter();
using (var writer = XmlWriter.Create(s))
{
writer.WriteStartElement("oneshot", "http://www.w3.org/2002/xforms");
writer.WriteElementString("dm", "form_namespace",
"http://mobileforms.foo.com/xforms","Foo");
// pick "http://www.w3.org/2002/xforms" by default for Days node
writer.WriteElementString("Days", "6");
// or you can explicitly specify "http://www.w3.org/2002/xforms"
writer.WriteElementString("Leave_Type",
"http://www.w3.org/2002/xforms", "Option 3");
writer.WriteEndElement();
}
Console.Write(s.ToString());
Note that your sample XML defines more prefixes than are used in the XML. If your requirement is to produce "text identical XML" (vs. identical from XML point of view, but not necessary represented with identical text) you may need to put more effort in adding namespace prefixes and xmlns attributes in places you need.
Note 2: creating XML object first (XDocument for modern/LINQ way, or XmlDocument if you like DOM more) may be easier approach.

Try using the XAML serializer from .NET 4.0+'s assembly System.Xaml.
You may need to add attributes to mark properties as content instead of XML attributes.

Thanks, all, for the help! It was a combination of two of the above answers that did it for me. I'm going to set the one that suggested the IXmlSerializable approach as that was the majority of the solution.
I had to declare the XMLRoot tag on the class name, remove WriteStartElement and WriteEndElement, and then use the four parameter declaration.
This is the class that worked in the end:
[Serializable]
[XmlRoot("oneshot")]
public class LeaveRequestPush : IXmlSerializable
{
public string FormNamespace { get; set; }
public int Days { get; set; }
public string LeaveType { get; set; }
#region IXmlSerializable
public void WriteXml(XmlWriter writer)
{
writer.WriteElementString("dm", "form_namespace", "http://mobileforms.devicemagic.com/xforms", FormNamespace);
writer.WriteElementString("Days", Days.ToString());
writer.WriteElementString("Leave_Type", LeaveType);
}
}
public void ReadXml (XmlReader reader)
{
// populate from xml blob
}
public XmlSchema GetSchema()
{
return(null);
}
Again, thanks all for the combined efforts. I would not have got this one by myself!

This code does the trick!
public void WriteXml(XmlWriter writer)
{
const string ns1 = "http://firstline.com/";
const string xsi = "http://www.w3.org/2001/XMLSchema-instance";
writer.WriteStartElement("myRoot", ns1);
writer.WriteAttributeString("SchemaVersion", "1.0");
writer.WriteAttributeString("xmlns", "xsi", "http://www.w3.org/2000/xmlns/", xsi);
writer.WriteAttributeString("xsi", "schemaLocation", xsi, ns1 + " schema1.xs");
writer.WriteStartElement("element1", ns1);
writer.WriteElementString("test1", ns1, "test value");
writer.WriteElementString("test2", ns1, "value 2");
writer.WriteEndElement();
writer.WriteEndElement();//to close classname that has root xml
}
Xml with multiple awesome namespaces!
<?xml version="1.0" encoding="utf-16"?>
<myClassNameWhereIXmlSerializableIsImplemented>
<myRoot SchemaVersion="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://firstline.com/ schema1.xs" xmlns="http://firstline.com/">
<element1>
<test1>test value</test1>
<test2>value 2</test2>
</element1>
</myRoot>
</myClassNameWhereIXmlSerializableIsImplemented>
There is something confusing about this line
writer.WriteAttributeString("xmlns", "xsi", "http://www.w3.org/2000/xmlns/", xsi);
if you give a random url instead of "http://www.w3.org/2000/xmlns/" , it will fail. The url that appear in xml is actually from "xsi" variable.
One more example to prove the point
writer.WriteAttributeString("xml", "base", "http://www.w3.org/XML/1998/namespace", base1);
where base1 = "<custom url>"
and if you want to output with a prefix like this <d:test1>somevalue<\d:test1> then writer.WriteElementString("d","test1", ns1, "somevalue"); if ns1 is not defined above then it will be added in xml output.
but for
<d:test1>
<blah>...
<\d:test1>
StartElement is needed writer.WriteStartElement("test1", ns1); to be closed when needed with writer.WriteEndElement();

Related

How to serialize an object containing an XML property

Note this is .NET 4.8
I have created this sample code to illustrate the problem
[XmlRoot(ElementName = "RESULT", Namespace = "", IsNullable = false)]
public class Result
{
public string Message { get; set; }
public XElement Stuff { get; set; }
public override string ToString()
{
var ser = new XmlSerializer(GetType());
using (var stream = new StringWriter())
{
ser.Serialize(stream, this);
return stream.ToString();
}
}
}
I will have some XML already that looks like this
<FOO>
<BAR>Hello World</BAR>
<BAR2>Hello World</BAR2>
<BAR3>Hello World</BAR3>
</FOO>
This is assigned to the XElement Stuff property and when an instance of Result is then serialized, you get this XML:
<?xml version="1.0" encoding="utf-16"?>
<RESULT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Message>Hello World</Message>
<Stuff>
<FOO>
<BAR>Hello World</BAR>
<BAR2>Hello World</BAR2>
<BAR3>Hello World</BAR3>
</FOO>
</Stuff>
</RESULT>
Question: Is there any way to get this result instead?
<?xml version="1.0" encoding="utf-16"?>
<RESULT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Message>Hello World</Message>
<FOO>
<BAR>Hello World</BAR>
<BAR2>Hello World</BAR2>
<BAR3>Hello World</BAR3>
</FOO>
</RESULT>
Note: FOO could be Stuff - I don't care (because I know how to change that name) I just don't want two levels of nested XML for that property in the serialised XML
You can play with the sample code here
If you're happy for the root name to be hard-coded, then you can write a wrapper type for the elements and implement IXmlSerializable within.
This is likely preferable to implementing in Result, as I imagine the real type would have more than 2 properties.
A quick and dirty example - I'll leave implementing ReadXml to you (if you need it):
public class ElementsWrapper : IXmlSerializable
{
public XElement[] Elements { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
}
public void WriteXml(XmlWriter writer)
{
foreach (var element in Elements)
{
element.WriteTo(writer);
}
}
}
And change your property in Result to:
public ElementsWrapper FOO { get; set; }
When used, the serialiser for Result will write <FOO>, then delegate to the custom serialisation, and then write </FOO>.
You can see an updated fiddle here.

Generating XML document with C#

I need to generate an XML document that follows this specifictaion
<productName locale="en_GB">Name</productName>
but using XMLSeralization I am getting the following
<productName locale="en_GB">
<Name>Name</Name>
</productName>
My C# code is like this:
[Serializable]
public class productName
{
public productName()
{
}
public string Name;
[XmlAttribute]
public string locale;
}
XmlAttribute is what is required to show the locale in the correct place, but I am unable to figure out how to export the Name field correctly.
Does anyone have an idea?
Thanks
EDIT:
This is the code to generate the XML
public static class XMLSerialize
{
public static void SerializeToXml<T>(string file, T value)
{
var serializer = new XmlSerializer(typeof(T));
using (var writer = XmlWriter.Create(file))
serializer.Serialize(writer, value);
}
public static T DeserializeFromXML<T>(string file)
{
XmlSerializer deserializer = new XmlSerializer(typeof(T));
TextReader textReader = new StreamReader(file);
T result;
result = (T)deserializer.Deserialize(textReader);
textReader.Close();
return result;
}
}
Instead of specifying Name as element specify it as text value by adding [XmlText] attribute
[XmlText]
public string Value { get; set; }
This contains not only a direct answer to your question, but more of a indirect answer of how to solve similar issues like this in the future.
Start the other way around, with your xml, write your xml exactly like you want it and go from there, like this:
// assuming data.xml contains the xml as you'd like it
> xsd.exe data.xml // will generate data.xsd, ie xsd-descriptor
> xsd.exe data.xsd /classes // will generate data.cs, ie c# classes
> notepad.exe data.cs // have a look at data.cs with your favorite editor
Now just have a look at data.cs, this will contain an enormous amount of attributes and stuff and the namespaces are probably wrong, but at least you know how to solve your particular xml-issue.
The direct answer is to use the XmlTextAttribute on the given property, preferably named Value since that is the convention I've seen so far.
[Serializable]
public class productName {
public productName() { }
[XmlText]
public string Value {get; set;}
[XmlAttribute]
public string locale {get; set;}
}

Getting information from my XML

I'm quite new to XML serialization/deserialization and I'm wondering how I should apply tags (XmlElement, XmlAttribute etc) to the following objects which I'm writing to XML files, to make it most optimal for me to later use LINQ or similar to get out the data I want. Let me give an example:
[XmlRoot("Root")]
public class RootObject
{
public Services Services { get; set; }
}
public class Services
{
public Service TileMapService { get; set; }
}
public class Service
{
public string Title { get; set; }
public string href { get; set; }
}
Here I've defined some properties which I'm going to write to XML with some values I'm gonna add later. At this point, I've hardcoded in the values in this method:
public static void RootCapabilities()
{
RootObject ro = new RootObject()
{
Services = new Services()
{
TileMapService = new Service()
{
Title = "Title",
href = "http://something"
}
}
};
Which gets me this XML output:
<?xml version="1.0" encoding="utf-8"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Services>
<TileMapService>
<Title>Title</Title>
<href>http://something</href>
</TileMapService>
</Services>
</Root>
My question is if this is a valid way of doing it or if I have to use the 'XmlElement' and 'XmlAttribute' tags to later deserialize this XML file and get the information out of it, that I want (for example 'title').
I haven't been sure how to write this question, so please let me know if it's too vague.
I would not use XML serialization if I were you. Any changes to your schema/object structure and you immediately lose backwards compatibility due to how XML serialization works as a technology.
Rather separate out the XML serialization from the actual class structure - this way you can make changes to the XML/object schema and write proper migrating functions to handle any changes you make in the future.
Rather use the LINQ-to-XML classes to construct your XML document and save it as they are the easiest way in .NET to convert object structures to XML in a decoupled manner.
if the output is what you expect I think that you shouldn't change anything.
You should use Decorators like XmlAttribute or XmlIgnore when you want change the default behavior (e.g don't include a field, include one field as an attribute...)
Their role is obtain full control of the serialization and avoid unexpected behaviors
If you don't want to worry about the nitty-gritty of the serialization, XmlSerializer has always treated me well.
public static void SerializeObject(T obj, string file)
{
XmlSerializer s = new XmlSerializer(typeof(T));
TextWriter w = new StreamWriter(file);
s.Serialize(w, obj);
w.Close();
}
public static T DeserializeObject(string file)
{
XmlSerializer s = new XmlSerializer(typeof(T));
using (StreamReader r = new StreamReader(file))
{
return (T)s.Deserialize(r);
}
}
This should really only be used with references types though (objects, not primitive types or structs). Deserialize can return null and casting that to a value type will bring nothing but heartache.

xsi and xsd namespaces missing when using custom IXmlSerializable

I am encountering the following problem. Whenever I use the default XML serialization in my C# class, the namespaces xsi and xsd are automatically added by the .NET serialization engine. However when the serialization is defined through IXmlSerializable, the namespaces are not added.
Example: this code:
class Program
{
static void Main(string[] args)
{
OutputSerialized(new Outer() { Inner = new Inner() });
OutputSerialized(new OuterCustom() { Inner = new Inner() });
}
static void OutputSerialized<T>(T t)
{
var sb = new StringBuilder();
using (var textwriter = new StringWriter(sb))
new XmlSerializer(typeof(T)).Serialize(textwriter, t);
Console.WriteLine(sb.ToString());
}
}
[Serializable] public class Inner { }
[Serializable] public class Outer { public Inner Inner { get; set; } }
public class OuterCustom : IXmlSerializable
{
public Inner Inner;
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteStartElement("Inner");
new XmlSerializer(typeof(Inner)).Serialize(writer, Inner);
writer.WriteEndElement();
}
public System.Xml.Schema.XmlSchema GetSchema() { return null; }
public void ReadXml(System.Xml.XmlReader reader) { /**/ }
}
produces the following output:
<?xml version="1.0" encoding="utf-16"?>
<Outer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Inner />
</Outer>
<?xml version="1.0" encoding="utf-16"?>
<OuterCustom>
<Inner>
<Inner xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
</Inner>
</OuterCustom>
You can see that the OuterCustom's serialized form is missing the xsd and xsi namespaces.
How can I make my OuterCustom behave the same way as Outer? Am I missing something in my code? Am I doing the custom serialization wrong?
There are a lot of questions here on SO about how to get rid of the additional namespaces, but it seems that no one asked about how to get them back.
First of all - there is no difference between mentioned versions of XML for compliant XML parser. It does not really matter if/how/where namespace prefixes defined as long as nodes have correct namespaces associated.
As this question is explicitly about style where to put namespace declaration one makes own call what is nice and what is not. In this particular case goal is to avoid duplicated xmlns attributes and move them higher in the tree.
One can write xmlns attributes wherever needed as long they don't conflict on the same node. Use XmlWriter.WriteAttributeString method to add them:
writer.WriteAttributeString(
"xmlns", "prefix", null, "urn:mynamespace");
To avoid duplicate declarations - check if prefix already defined for given namespace using XmlWriter.LookupPrefix
Sample code to ensure xsd and xsi prefixes (by Vlad):
const string xsiNamespace = System.Xml.Schema.XmlSchema.InstanceNamespace;
const string xsdNamespace = System.Xml.Schema.XmlSchema.Namespace;
public static void EnsureDefaultNamespaces(System.Xml.XmlWriter writer)
{
if (writer.LookupPrefix(xsiNamespace) == null)
writer.WriteAttributeString("xmlns", "xsi", null, xsiNamespace);
if (writer.LookupPrefix(xsdNamespace) == null)
writer.WriteAttributeString("xmlns", "xsd", null, xsdNamespace);
}
Alternative approach if more changes desired to achieve nice looking XML is to allow serialization to finish and than process XML to adjust prefixes definitions (i.e. normalize to desired prefixes, collect all namespaces and define all on top). One can either read XML with C# code or even create XSLT transformation.

how to serialize class objects to xml document

I hope to find a solution from you. What I need is to serialize ValidatorList class object into an xml document. How to do this?
I tried like this:
XmlSerializer _serializer = new XmlSerializer(list);
But I don't know how to make output of xml for list that has several classes.
C#
_list= new ListVal();
Type _type = typeof(ras);
_list.Add(new RequiredField
{
Property = _type.GetProperty("CustRef")
}.Add(new AsciiVal()));
_list.Add(new RequiredField
{
Property = _type.GetProperty("ctr")
}.Add(new StringLengthVal
{
min= 3,
max= 3
}));
[Serializable]
public class Field
{
public Field Next
{
get;
set;
}
public Field TypeName
{
get;
set;
}
public Field PropertyName
{
get;
set;
}
}
public class RequiredField : Field
{
//TODO
}
public class AsciiVal: Field
{
//TODO
}
public class StringLengthVal: Field
{
//TODO
}
public class ListVal: List<Field>
{
//TODO
}
I have something close, but not exactly the Xml you want. In actual fact I think you'll see that the Xml produced below makes a bit more sense than what you have.
To get you started, you control the serialization and deserialization using attributes in the System.Xml.Serialization namespace. A few useful ones to read up on are
XmlRootAttribute
XmlElementAttribute
XmlAttributeAttribute
XmlIncludeAttribute
So I mocked up some code which closely matches your own. Notice the addition of some attributes to instruct the serializer how I want the Xml to be laid out.
[XmlInclude(typeof(AsciiValidator))]
[XmlInclude(typeof(RequiredValidator))]
[XmlInclude(typeof(StringLengthValidator))]
public class FieldValidator
{
[XmlElement("Next")]
public FieldValidator Next
{
get;
set;
}
[XmlElement("PropertyName")]
public string PropertyName
{
get;
set;
}
}
public class AsciiValidator: FieldValidator
{
}
public class RequiredValidator: FieldValidator
{
}
public class StringLengthValidator: FieldValidator
{
[XmlElement]
public int MinLength{get;set;}
[XmlElement]
public int MaxLength{get;set;}
}
[XmlRoot("ValidatorList")]
public class ValidatorList : List<FieldValidator>
{
}
Point of interest; Every class inheriting FieldValidator must be added to the list of known types using XmlIncludeAttribute so the serializer knows what to do with them)
Then I created an example object map:
var test = new ValidatorList();
test.Add(
new RequiredValidator()
{
PropertyName="CustRef",
Next = new AsciiValidator()
});
test.Add(
new RequiredValidator()
{
PropertyName="CurrencyIndicator",
Next = new StringLengthValidator(){
MinLength=3,
MaxLength = 10
}
});
Finally I told the serializer to serialize it (and output the result to the console)
var ser = new XmlSerializer(typeof(ValidatorList));
ser.Serialize(Console.Out,test);
This was the result:
<?xml version="1.0" encoding="utf-8"?>
<ValidatorList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FieldValidator xsi:type="RequiredValidator">
<Next xsi:type="AsciiValidator" />
<PropertyName>CustRef</PropertyName>
</FieldValidator>
<FieldValidator xsi:type="RequiredValidator">
<Next xsi:type="StringLengthValidator">
<MinLength>3</MinLength>
<MaxLength>10</MaxLength>
</Next>
<PropertyName>CurrencyIndicator</PropertyName>
</FieldValidator>
</ValidatorList>
Not a million miles away from what you wanted. There is the need to output certain things in a certain way (eg xsi:type tells the serializer how to deserialize back to the object map). I hope this gives you a good start.
Here is a live, working example: http://rextester.com/OXPOB95358
Deserialization can be done by calling the Deserialize method on the XmlSerializer.
For example, if your xml is in a string:
var ser = new XmlSerializer(typeof(ValidatorList));
var test = "<..../>" // Your Xml
var xmlReader = XmlReader.Create(new StringReader(test));
var validatorList = (ValidatorList)ser.Deserialize(xmlReader);
There are many overrides of Deserialize which take differing inputs depending if the data is in a Stream an existing reader, or saved to a file.
You have to decorate the class that contains the _validators field with the KonwnType attribute
[Serializable]
[KwownType(typeof(RequiredFieldValidator)]
[KwownType(typeof(AsciValidator)]
public class MySerialisableClass
I have several SO answers that detail how to serialize objects using XML. I'll provide links below.
However, since you're looking for a rather simple serialization of your object, you may want to read up on the DataContractSerializer. It's much less complicated than the old .NET 1.x XML Serialization.
Here's the list of SO answers:
Omitting all xsi and xsd namespaces when serializing an object in .NET?
XmlSerializer: remove unnecessary xsi and xsd namespaces
Suppress xsi:nil but still show Empty Element when Serializing in .Net
Using XmlAttributeOverrides on Nested Properties
Even though many of these have to do with XML serialization and namespaces, they contain complete examples of serializing an object to XML using .NET 1.x XML Serialization.

Categories