I am serialising an object to XML and I get the output like so :
<?xml version="1.0" encoding="utf-8"?>
<SOrd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
However I would like it to be like so :
<SOrd xmlns:SOrd="http://..." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://....xsd">
How can I do this?
I have tried adding attributes to the root object before serialisation and also this :
XmlSerializerNamespaces xmlNameSpace = new XmlSerializerNamespaces();
xmlNameSpace.Add("xmlns:SOrd", "http://...");
xmlNameSpace.Add("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
xmlNameSpace.Add("xsi:schemaLocation", "http://....xsd");
XmlSerializer xs = new XmlSerializer(ord.GetType());
TextWriter writer = new StreamWriter(outputPath, false);
xs.Serialize(writer, ord, xmlNameSpace);
writer.Close();
But I get the exception "The ':' character, hexadecimal value 0x3A, cannot be included in a name."
the prefic can't contain the ":", take out the first part xmlns:
here is your code slighly changed:
XmlSerializerNamespaces xmlNameSpace = new XmlSerializerNamespaces();
xmlNameSpace.Add("SOrd", "http://...");
xmlNameSpace.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
xmlNameSpace.Add("schemaLocation", "http://....xsd");
XmlSerializer xs = new XmlSerializer(ord.GetType());
TextWriter writer = new StreamWriter(outputPath, false);
xs.Serialize(writer, ord, xmlNameSpace);
writer.Close();
make sure to add the required attributes for each class since the serialization attributes are not inhereted. for more about the inheretence of attributes check: How to deserialize concrete implementation of abstract class from XML
EDIT
you can achieve the xsi:shcemaLocation Like that:
[XmlRoot(ElementName = "FooData", Namespace = "http://foo.bar", DataType = "schemaLocation")]
public class Foo
{
[System.Xml.Serialization.XmlAttributeAttribute(AttributeName = "schemaLocation", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string schemaLocation = "http://example";
}
Related
I have a scenario where I may need to serialize an object as a root element into an XML file or if a file is provided that already has an array of objects I need to serialize the object into the array.
Because the object may be a root I have decorated it with the System.Xml.Serialization.XmlRootAttribute.
When I serialize the object to an XElement I get the root version of the object. When I add the XElement to the array it retains the namespace attributes and can not be deserialized properly.
Here is a portion of the class:
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:www.agxml.org:schemas:all:4:0", TypeName = "CommodityMovement")]
[System.Xml.Serialization.XmlRootAttribute(ElementName ="CommodityMovement", Namespace="urn:www.agxml.org:schemas:all:4:0", IsNullable=false)]
public partial class CommodityMovementType {
...
}
When I create a file with a list it works just fine.
private static String CreateMessageFile<T>(String filePath, T message)
{
var xmlDoc = new XDocument();
// serialize XML to string to the xmlEntity document
using (var writer = new StringWriter())
{
var entitySerializer = new XmlSerializer(typeof(List<T>));
var list = new List<T>();
list.Add(message);
entitySerializer.Serialize(writer, list);
xmlDoc.Add(XElement.Parse(writer.ToString()));
}
xmlDoc.Save(filePath);
}
This will serialize an array with one message:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfCommodityMovement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<CommodityMovement>
...
</CommodityMovement>
</ArrayOfCommodityMovement>
However, I may read that file and add a message to that array.
private static String CreateMessageFile<T>(String filePath, T message)
{
var xmlDoc = new XDocument();
using var sourceStream = File.Open(filePath,FileMode.Open);
xmlDoc.Add(XElement.Load(sourceStream));
using (var writer = new StringWriter())
{
var entitySerializer = new XmlSerializer(entity.DotNetType);
// entitySerializer.Serialize(writer, list);
entitySerializer.Serialize(writer, message);
xmlDoc.Root.Add(XElement.Parse(writer.ToString()));
}
xmlDoc.Save(filePath);
}
This produces the the following XML:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfCommodityMovement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<CommodityMovement>
...
</CommodityMovement>
<CommodityMovement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:www.agxml.org:schemas:all:4:0">
...
</CommodityMovement>
</ArrayOfCommodityMovement>
When I attempt to deserialize the ArrayOfCommodityMovement it only deserializes the first CommodityMovement message.
If I open the generated XML file and remove the namespace attributes from the second CommodityMovement element then it will deserialize correctly. Here is the test I am using to define "correctly".
XDocument xDocument = XDocument.Load(filePath);
var descendants = xDocument.Descendants("CommodityMovement");
Assert.Equal(2, descendants.Count());
var entitySerializer = new XmlSerializer(typeof(List<CommodityMovementType>));
var commodityMovementList = entitySerializer.Deserialize(xDocument.CreateReader()) as List<CommodityMovementType>;
Assert.NotEmpty(commodityMovementList);
Assert.Equal(2, commodityMovementList.Count);
So how can I deserialize the object and insert the resulting element into an existing array and make sure the attributes aren't added?
BTW, I need to keep the System.Xml.Serialization.XmlRootAttribute because depending on configuration I need to be able to generate one message per file and the CommodityMovement then becomes a root element.
Note:
This isn't my exact code. It is a simplified example.
Thanks in advance for any help.
I found the solution is to remove the attributes and namespace if this is not the first element in the array. To see if it is the first I check to see if the document root is null:
if (xmlDoc.Root is null)
If it is not null, I serialize the element, remove it's attributes and namespace and add the element to the root element:
else
{
var element = SerializeEntityAsElement(entity, masterData);
// remove attributes and namespace so element can be added to existing array
element.RemoveAttributes();
element.Name = element.Name.LocalName;
xmlDoc.Root.Add(element);
}
Now my test will pass:
XDocument xDocument = XDocument.Load(filePath);
var descendants = xDocument.Descendants("CommodityMovement");
Assert.Equal(2, descendants.Count());
var entitySerializer = new XmlSerializer(typeof(List<CommodityMovementType>));
var commodityMovementList = entitySerializer.Deserialize(xDocument.CreateReader()) as List<CommodityMovementType>;
Assert.NotEmpty(commodityMovementList);
Assert.Equal(2, commodityMovementList.Count);
StringBuilder sb = new StringBuilder();
XmlWriter writer = XmlWriter.Create(sb);
XmlSerializer serializer = new XmlSerializer(typeof(OpenShipments));
var ns = new XmlSerializerNamespaces();
ns.Add("x-schema:", #"x-schema:C:\UPSLabel\OpenShipments.xdr");
serializer.Serialize(writer, OS, ns);
xmlString = sb.ToString();
getting error object reference not found because i add namespace programatically.
basically in my xml namespace will look like below one
<OpenShipments xmlns="x-schema:C:\UPSLabel\OpenShipments.xdr">
here i add the line ns.Add("x-schema:", #"x-schema:C:\UPSLabel\OpenShipments.xdr");
and for the above line i am getting error....what is my mistake. just can not figure out. please help me to construct the namespace.
Try like this:
var sb = new StringBuilder();
var myns = #"x-schema:C:\UPSLabel\OpenShipments.xdr";
using (var writer = XmlWriter.Create(sb))
{
var serializer = new XmlSerializer(typeof(OpenShipments), myns);
var ns = new XmlSerializerNamespaces();
ns.Add(string.Empty, myns);
serializer.Serialize(writer, OS, ns);
xmlString = sb.ToString();
}
will generate:
<OpenShipments xmlns="x-schema:C:\UPSLabel\OpenShipments.xdr">
...
</OpenShipments>
The line:
ns.Add("x-schema:", #"x-schema:C:\UPSLabel\OpenShipments.xdr");
is adding an alias, i.e. allowing the serializer to use x-schema: inside the xml. However this doesn't make your object use this namespace; for that, you need (on your type):
[XmlRoot(Namespace=#"x-schema:C:\UPSLabel\OpenShipments.xdr")]
public class OpenShipments {...}
(or something equivalent, perhaps using XmlAttributeOverrides)
Note that with the alias added, you will get:
<x-schema:OpenShipments xmlns:x-schema="x-schema:C:\UPSLabel\OpenShipments.xdr" />
To get it without an alias, you want:
ns.Add("", #"x-schema:C:\UPSLabel\OpenShipments.xdr");
which gives output:
<OpenShipments xmlns="x-schema:C:\UPSLabel\OpenShipments.xdr" />
I would like to create the XML string with special characters handling. However it turned out to be too complicated and causing issues by generating wrong XML.
Now i was thinking to build the string using some object from System.xml and then stringify() or get string from it. This will i guess help me from special character cases.
//Psudo code
xmlDoc doc = new XMLDoc();
Element ele= new Element("xyz");
ele.value(Oob.property)
doc.appendNode(ele);
...
doc.getXMLString();
Can some one please let me know how to do this in C# .NET2.0+ .
I find XmlTextWriter more intuitive than XmlDocument for editing.
e.g.:
string xmlString = null;
using(StringWriter sw = new StringWriter())
{
XmlTextWriter writer = new XmlTextWriter(sw);
writer.Formatting = Formatting.Indented; // if you want it indented
writer.WriteStartDocument(); // <?xml version="1.0" encoding="utf-16"?>
writer.WriteStartElement("TAG"); //<TAG>
// <SUBTAG>value</SUBTAG>
writer.WriteStartElement("SUBTAG");
writer.WriteString("value");
writer.WriteEndElement();
// <SUBTAG attr="hello">world</SUBTAG>
writer.WriteStartElement("SUBTAG");
writer.WriteStartAttribute("attr");
writer.WriteString("hello");
writer.WriteEndAttribute();
writer.WriteString("world");
writer.WriteEndElement();
writer.WriteEndElement(); //</TAG>
writer.WriteEndDocument();
xmlString = sw.ToString();
}
after this code xmlString will contain:
<?xml version="1.0" encoding="utf-16"?>
<TAG>
<SUBTAG>value</SUBTAG>
<SUBTAG attr="hello">world</SUBTAG>
</TAG>
ADDITIONAL INFO:
using XmlDocument would be:
XmlDocument doc = new XmlDocument();
XmlNode tagNode = doc.CreateNode(XmlNodeType.Element, "TAG", null);
doc.AppendChild(tagNode);
XmlNode subTagNode1 = doc.CreateNode(XmlNodeType.Element, "SUBTAG", null);
tagNode.AppendChild(subTagNode1);
XmlText subTagNode1Value = doc.CreateTextNode("value");
subTagNode1.AppendChild(subTagNode1Value);
XmlNode subTagNode2 = doc.CreateNode(XmlNodeType.Element, "SUBTAG", null);
tagNode.AppendChild(subTagNode2);
XmlAttribute subTagNode2Attribute = doc.CreateAttribute("attr");
subTagNode2Attribute.Value = "hello";
subTagNode2.Attributes.SetNamedItem(subTagNode2Attribute);
XmlText subTagNode2Value = doc.CreateTextNode("world");
subTagNode2.AppendChild(subTagNode2Value);
string xmlString = null;
using(StringWriter wr = new StringWriter())
{
doc.Save(wr);
xmlString = wr.ToString();
}
You can also refer to this community wiki question, which leads to easier-to-read syntax when you need to build an xml stream programatically.
You can then just call the .ToString() method to get a clean escaped representation of your XML stream.
var xmlString = new XElement("Foo",
new XAttribute("Bar", "some & value with special characters <>"),
new XElement("Nested", "data")).ToString();
And you would get in xmlString:
<Foo Bar="some & value with special characters <>">
<Nested>data</Nested>
</Foo>
I have an XML document format from a legacy system that I have to support in a future application. I want to be able to both serialize and deserialize the XML between XML and C# objects, however, using the objects generated by xsd.exe, the C# serialization includes the xmlns:xsi..., xsi:... etc XML attributes on the root element of the document that gets generated. Is there anyway to disable this so that absolutely no XML attribute nodes get put out in the resulting XML ? The XML document should be elements only.
Duplicate? XmlSerializer: remove unnecessary xsi and xsd namespaces
Yes, use the XmlSerializerNamespaces class.
Example:
var s= new System.Xml.Serialization.XmlSerializer(typeof(TypeToSerialize));
var ns= new System.Xml.Serialization.XmlSerializerNamespaces();
ns.Add( "", "");
System.IO.StreamWriter writer= System.IO.File.CreateText(filePath);
s.Serialize(writer, objectToSerialize, ns);
writer.Close();
See also: XmlSerializer: remove unnecessary xsi and xsd namespaces
There is no way to force XML Serializer to ignore xsi attributes (unless you implement IXmlSerializable and force custom serialization or use XmlAttributeOverrides). However the only time xsi: attributes show up is when you have a nullable element. If you do need to use nullable elements you can of course post-process the XML to remove all xsi: occurences. However if you do this think about how you will deserialize the XML back into an object, if xsi:nil is missing on an element and the element is defined as a nullable integer you will run into an exception.
#Cheeso, please correct me if i am wrong.
I have the following code.
public class TestSer
{
public int? MyProperty { get; set; }
}
TestSer ser = new TestSer();
ser.MyProperty = null;
StringBuilder bldr = new StringBuilder();
var ns = new System.Xml.Serialization.XmlSerializerNamespaces();
ns.Add("", "");
XmlSerializer s = new XmlSerializer(typeof(TestSer));
using (StringWriter writer = new StringWriter(bldr))
{
s.Serialize(writer, ser, ns);
}
I get the following output.
<?xml version="1.0" encoding="utf-16"?>
<TestSer>
<MyProperty d2p1:nil="true" xmlns:d2p1="http://www.w3.org/2001/XMLSchema-instance" />
</TestSer>
This isn't exactly element only as the question asks for.
I have a situation where I have an xml file that I don't want to modify.
The AddAnnotation function in XElement class provides an option to add memory-only data which is not serialized and not part of the XML.
I want to be able to save these annotations (for example: to another xml file) and then to deserialize both the xml and the annotations in order to get the same object I had.
I don't want to change the original xml and that's the reason that I use annotations.
To summarize, I want to be able to add custom data to an xml file. This data won't be a part of the xml when I serialize it or it will be a part of the xml but I would be able to retrieve the original xml easily.
Do you have any recommendation how I can do such a thing?
Edit: Should I use xml processing instructions? Are processing instructions intended for this kind of usage?
It sounds to me like the simplest approach would be to use regular nodes, but in a different xml namespace - i.e.
<foo standardAttrubute="abc" myData:customAttribute="def">
<standardElement>ghi</standardElement >
<myData:customElement>jkl</myData:customElement>
</foo>
(where myData is an xmlns alias for the namespace-uri)
In many cases, readers are only checking for data in their namespace (or the default/blank namespace) - values in custom namespaces are generally skipped.
To get pack the original xml, one simple approach would be to run it through an xslt that only respects the default/original namespace.
XNamespace myData = XNamespace.Get("http://mycustomdata/");
XElement el = new XElement("foo",
new XAttribute(XNamespace.Xmlns + "myData", myData.NamespaceName),
new XAttribute("standardAttribute", "abc"),
new XAttribute(myData + "customAttribute", "def"),
new XElement("standardElement", "ghi"),
new XElement(myData + "customAttribute", "jkl"));
string s = el.ToString();
To remove such data from an XElement, perhaps:
static void Strip(XElement el, XNamespace ns) {
List<XElement> remove = new List<XElement>();
foreach (XElement child in el.Elements()) {
if (child.Name.Namespace == ns) {
remove.Add(child);
} else {
Strip(child, ns);
}
}
remove.ForEach(child => child.Remove());
foreach (XAttribute child in
(from a in el.Attributes()
where a.Name.Namespace == ns
select a).ToList()) {
child.Remove();
}
}
The original question used the word "Serialize" but then also mentioned XElement and annotation. To me these are two different things.
If you really want to use the XmlSerializer:
What I would do is use XmlAttributeOverrides, to differentiate the serialization.
With the XmlAttributeOverrides you can programmatically, at runtime, override the xml serialization attributes that decorate your types.
Within your type, you can have a field/property that is intended to hold the annotation/documentation. Decorate that with XmlIgnore. Then, create one instance of the XmlSerializer that accepts no overrides. The annotation will not be serialized or de-serialized. Create another instance of the XmlSerializer for that type, using an XmlAttributeOverrides object. Specify an override for the XmlIgnore'd property (use XmlElementAttribute), as well as overrides for any attributes on any of the other members (use XmlIgnore=true).
Serialize the instance twice, one with each serializer.
Edit: here's the code:
public class DTO
{
[XmlIgnore]
public string additionalInformation;
[XmlElement(Order=1)]
public DateTime stamp;
[XmlElement(Order=2)]
public string name;
[XmlElement(Order=3)]
public double value;
[XmlElement(Order=4)]
public int index;
}
public class OverridesDemo
{
public void Run()
{
DTO dto = new DTO
{
additionalInformation = "This will bbe serialized separately",
stamp = DateTime.UtcNow,
name = "Marley",
value = 72.34,
index = 7
};
// ---------------------------------------------------------------
// 1. serialize normally
// this will allow us to omit the xmlns:xsi namespace
var ns = new XmlSerializerNamespaces();
ns.Add( "", "" );
XmlSerializer s1 = new XmlSerializer(typeof(DTO));
var builder = new System.Text.StringBuilder();
var settings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
Console.WriteLine("\nSerialize using the in-line attributes: ");
using ( XmlWriter writer = XmlWriter.Create(builder, settings))
{
s1.Serialize(writer, dto, ns);
}
Console.WriteLine("{0}",builder.ToString());
Console.WriteLine("\n");
// ---------------------------------------------------------------
// ---------------------------------------------------------------
// 2. serialize with attribute overrides
// use a non-empty default namespace
ns = new XmlSerializerNamespaces();
string myns = "urn:www.example.org";
ns.Add( "", myns);
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attrs = new XmlAttributes();
// override the (implicit) XmlRoot attribute
XmlRootAttribute attr1 = new XmlRootAttribute
{
Namespace = myns,
ElementName = "DTO-Annotations",
};
attrs.XmlRoot = attr1;
overrides.Add(typeof(DTO), attrs);
// "un-ignore" the first property
// define an XmlElement attribute, for a type of "String", with no namespace
var a2 = new XmlElementAttribute(typeof(String)) { ElementName="note", Namespace = myns };
// add that XmlElement attribute to the 2nd bunch of attributes
attrs = new XmlAttributes();
attrs.XmlElements.Add(a2);
attrs.XmlIgnore = false;
// add that bunch of attributes to the container for the type, and
// specifically apply that bunch to the "additionalInformation" property
// on the type.
overrides.Add(typeof(DTO), "additionalInformation", attrs);
// now, XmlIgnore all the other properties
attrs = new XmlAttributes();
attrs.XmlIgnore = true;
overrides.Add(typeof(DTO), "stamp", attrs);
overrides.Add(typeof(DTO), "name", attrs);
overrides.Add(typeof(DTO), "value", attrs);
overrides.Add(typeof(DTO), "index", attrs);
// create a serializer using those xml attribute overrides
XmlSerializer s2 = new XmlSerializer(typeof(DTO), overrides);
Console.WriteLine("\nSerialize using the override attributes: ");
builder.Length = 0;
using ( XmlWriter writer = XmlWriter.Create(builder, settings))
{
s2.Serialize(writer, dto, ns);
}
Console.WriteLine("{0}",builder.ToString());
Console.WriteLine("\n");
// ---------------------------------------------------------------
}
}
output, using the in-line attributes:
<DTO>
<stamp>2009-06-30T02:17:35.918Z</stamp>
<name>Marley</name>
<value>72.34</value>
<index>7</index>
</DTO>
output, using the override attributes:
<DTO-Annotations xmlns="urn:www.example.org">
<note>This will bbe serialized separately</note>
</DTO-Annotations>