Serialize XML from C# class, need to set namespaces - c#

I'm having trouble getting my top-level element to look exactly like this using the XmlSerializer (and C# attributes):
<rootObject xmlns="http://www.example.com/xmlschemas/nonStandardSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.com/xmlschemas/nonStandardSchema1.xsd">
<otherSerializedObjects />
</rootObject>
Currently, the closest I've gotten is this:
<rootObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
d1p1:schemaLocation="http://www.example.com/xmlschemas/nonStandardSchema1.xsd"
xmlns:d1p1="xsi"
xmlns="http://www.example.com/xmlschemas/nonStandardSchema">
<otherSerializedObjects />
</rootObject>
C#
[XmlRoot("rootObject", Namespace = "http://www.example.com/xmlschemas/nonStandardSchema")]
public class RootObject
{
[XmlAttribute("schemaLocation", Namespace = "xsi")]
public string SchemaLocation { get; set; }
[XmlElement("Image")]
public Object[] OtherSerializedObjects { get; set; }
public RootObject()
{
OtherSerializedObjects = new Object[]{};
}
}
public class Program
{
static void Main(string[] args)
{
var rootObject = new RootObject
{
SchemaLocation = "http://www.example.com/xmlschemas/nonStandardSchema1.xsd",
OtherSerializedObjects = new object[]{}
};
var serializer = new XmlSerializer(typeof(RootObject));
var stringWriter = new StringWriter();
serializer.Serialize(stringWriter, rootObject);
Console.WriteLine(stringWriter.ToString());
}
}
Any ideas?

Firstly, the [XmlAttribute(Namespace=X)] attribute on your schemaLocation field/property needs to have the full namespace for the value of X, not the local namespace shortcut. Incidentally, this can be a property rather than a field. Using a field for this purpose wastes memory.
Secondly, to eliminate the standard xmlns:xsd="http://www.w3.org/2001/XMLSchema", use an XmlSerializer.Serialize overload where you pass in an XmlSerializerNamespaces with just the namespaces you want.
Thus:
[XmlRoot("rootObject", Namespace = "http://www.example.com/xmlschemas/nonStandardSchema")]
public class RootObject
{
public static XmlSerializerNamespaces GetAdditionalNamespaces()
{
XmlSerializerNamespaces xsNS = new XmlSerializerNamespaces();
xsNS.Add("", "http://www.example.com/xmlschemas/nonStandardSchema");
xsNS.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
return xsNS;
}
[XmlAttribute("schemaLocation", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string XSDSchemaLocation
{
get
{
return "http://www.example.com/xmlschemas/nonStandardSchema1.xsd";
}
set
{
// Do nothing - fake property.
}
}
[XmlElement("Image")]
public Object[] OtherSerializedObjects { get; set; }
public RootObject()
{
OtherSerializedObjects = new Object[]{};
}
}
And then use it like:
var rootObject = new RootObject
{
OtherSerializedObjects = new object[]{}
};
var serializer = new XmlSerializer(typeof(RootObject));
var stringWriter = new StringWriter();
var ns = RootObject.GetAdditionalNamespaces();
var settings = new XmlWriterSettings() { Indent = true, IndentChars = " " }; // For cosmetic purposes.
using (var xmlWriter = XmlWriter.Create(stringWriter, settings))
{
serializer.Serialize(xmlWriter, rootObject, ns);
}
Console.WriteLine(stringWriter.ToString());
Example fiddle.

Namespace needs to be the entire namespace, not the shortened version.
[XmlAttribute("schemaLocation", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
In my testing it automatically used xsi.

Related

DataContract serializer not deserializing into a list repeating XML elements without container element

We are facing a situation where the DataContract serializer (a WCF service) is not deserializing repeating XML elements without container element into a list; the list property ends up with zero elements.
The datacontracts we have:
[DataContract]
public class Requestor
{
[DataMember(IsRequired = true, Order = 0)]
public string RequestorRole;
[DataMember(IsRequired = true, Order = 1)]
public string RequestorGivenName;
[DataMember(IsRequired = true, Order = 2)]
public string RequestorSurName;
[DataMember(IsRequired = false, Order = 3)]
public List<RequestorIdentification> RequestorIdentification { get; set; }
}
[DataContract]
public class RequestorIdentification
{
[DataMember(IsRequired = true, Order = 0)]
public string IdentificationID;
[DataMember(IsRequired = true, Order = 1)]
public string IdentificationCategoryCode;
}
The XML we want to deserialize:
<Requestor xmlns="http://schemas.datacontract.org/2004/07/Serialization">
<RequestorRole>Physicians</RequestorRole>
<RequestorGivenName>Rich</RequestorGivenName>
<RequestorSurName>Smith</RequestorSurName>
<RequestorIdentification>
<IdentificationID>AB1234567</IdentificationID>
<IdentificationCategoryCode>DEA</IdentificationCategoryCode>
</RequestorIdentification>
<RequestorIdentification>
<IdentificationID>0123456789</IdentificationID>
<IdentificationCategoryCode>NPI</IdentificationCategoryCode>
</RequestorIdentification>
</Requestor>
How can we resolve the issue using the DataContract serializer?
So far we have found no way to make it work, and this page https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/collection-types-in-data-contracts was not of much help.
The same issue was resolved for the XmlSerializer in this question:
Deserializing into a List without a container element in XML
To deserialize the XML shown in the OP, one can use XmlElement. However, this requires the use of XmlSerializer instead of DataContractSerializer for serialization and deserialization.
According to this post
The DataContractSerializer does not support the programming model used
by the XmlSerializer and ASP.NET Web services. In particular, it does
not support attributes like XmlElementAttribute and
XmlAttributeAttribute. To enable support for this programming model,
WCF must be switched to use the XmlSerializer instead of the
DataContractSerializer.
The code below shows how one can deserialize the XML shown in the OP.
Create a Windows Forms App (.NET Framework)
Add Reference (System.Runtime.Serialization)
VS 2019
In VS menu, click Project
Select Add Reference...
Click Assemblies
Check System.Runtime.Serialization
Click OK
The following using directives are used in the code below:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
Create a class (name: RequestorRequestorIdentification.cs)
[DataContract(Name = "RequestorIdentification")]
public class RequestorRequestorIdentification
{
[DataMember]
public string IdentificationID { get; set; }
[DataMember]
public string IdentificationCategoryCode { get; set; }
}
Create a class (name: Requestor.cs)
[DataContract(Name ="Requestor")]
public class Requestor
{
[DataMember]
public string RequestorRole { get; set; }
[DataMember]
public string RequestorGivenName { get; set; }
[DataMember]
public string RequestorSurName { get; set; }
[XmlElement]
public List<RequestorRequestorIdentification> RequestorIdentification { get; set; } = new List<RequestorRequestorIdentification>();
}
Note: The use of XmlElement requires the use of XmlSerializer instead of DataContractSerializer for serialization and deserialization.
Create a class (name: HelperXml.cs)
public class HelperXml
{
public static T DeserializeXMLFileToObject<T>(string xmlFilename)
{
//usage: Requestor requestor = DeserializeXMLFileToObject<Requestor>(xmlFilename);
if (String.IsNullOrEmpty(xmlFilename))
throw new Exception("Error: xmlFilename is null or empty.");
return DeserializeXMLToObject<T>(File.ReadAllBytes(xmlFilename));
}
public static T DeserializeXMLToObject<T>(string xmlText)
{
//usage: Requestor requestor = DeserializeXMLToObject<Requestor>(xmlText);
//create new instance
using (System.IO.StringReader reader = new System.IO.StringReader(xmlText))
{
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T), "http://schemas.datacontract.org/2004/07/Serialization");
return (T)serializer.Deserialize(reader);
}
}
public static T DeserializeXMLToObject<T>(byte[] xmlBytes)
{
//usage: Requestor requestor = DeserializeXMLToObject<Requestor>(File.ReadAllBytes(xmlFilename));
//create new instance of MemoryStream
using (MemoryStream ms = new MemoryStream(xmlBytes))
{
using (XmlTextReader reader = new XmlTextReader(ms))
{
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T), "http://schemas.datacontract.org/2004/07/Serialization");
return (T)serializer.Deserialize(reader);
}
}
}
public static void SerializeObjectToXMLFile(object obj, string xmlFilename)
{
//Usage: Class1 myClass1 = new Class1();
//SerializeObjectToXMLFile(myClass1, xmlFilename);
if (String.IsNullOrEmpty(xmlFilename))
throw new Exception("Error: xmlFilename is null or empty.");
//Xml writer settings
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
settings.Encoding = Encoding.UTF8;
settings.Indent = true;
using (XmlWriter writer = XmlWriter.Create(xmlFilename, settings))
{
//specify namespaces
System.Xml.Serialization.XmlSerializerNamespaces ns = new System.Xml.Serialization.XmlSerializerNamespaces();
ns.Add(string.Empty, "http://schemas.datacontract.org/2004/07/Serialization");
//create new instance
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType(), "http://schemas.datacontract.org/2004/07/Serialization");
//write to XML file
serializer.Serialize(writer, obj, ns);
}
}
}
Usage (deserialize):
private Requestor _requestor = null;
...
//deserialize
//_requestor = HelperXml.DeserializeXMLFileToObject<Requestor>(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Test.xml"));
//_requestor = HelperXml.DeserializeXMLToObject<Requestor>(System.Text.Encoding.UTF8.GetBytes(xmlText));
_requestor = HelperXml.DeserializeXMLToObject<Requestor>(xmlText);
Usage (serialize):
private Requestor _requestor = null;
...
//create new instance
_requestor = new Requestor();
_requestor.RequestorRole = "Physicians";
_requestor.RequestorGivenName = "Rich";
-requestor.RequestorSurName = "Smith";
_requestor.RequestorIdentification.Add(new RequestorRequestorIdentification() { IdentificationID = "AB1234567", IdentificationCategoryCode = "DEA" });
_requestor.RequestorIdentification.Add(new RequestorRequestorIdentification() { IdentificationID = "0123456789", IdentificationCategoryCode = "NPI" });
using (SaveFileDialog sfd = new SaveFileDialog())
{
sfd.Filter = "XML File (*.xml)|*.xml";
sfd.FileName = "Test.xml";
if (sfd.ShowDialog() == DialogResult.OK)
{
HelperXml.SerializeObjectToXMLFile(_requestor, sfd.FileName);
Debug.WriteLine($"Info: Saved to '{sfd.FileName}'");
}
}
Additional Resources:
Types Supported by the Data Contract Serializer
DataContractAttribute Class
Using Data Contracts
Using the XmlSerializer Class
Collection Types in Data Contracts
Data Member Order

XML response coming up with extra tags

I'm working on Web API services and returning XML as response.
Below is my model class
public class School
{
public List<StudentDetails> students {get;set;}
}
public class StudentDetails
{
public string Name {get;set;}
public int Age {get;set;}
}
My controller action method code
School test = new School();
StudentDetails s1 = new StudentDetails();
s1.Name = "ABC"; s1.Age=25;
StudentDetails s2 = new StudentDetails();
s2.Name = "DEF"; s2.Age=35;
test.students.Add(s1);
test.students.Add(s2);
return Request.CreateResponse(HttpStatusCode.OK, test);
My XML Response
<School xmln:i="http......> //not typing complete text here
<students>
<StudentDetails>
<Age>25</Age>
<Name>ABC</Name>
</StudentDetails>
<StudentDetails>
<Age>35</Age>
<Name>DEF</Name>
</StudentDetails>
</students>
</School>
Here in response, why am I getting <StudentDetails> tag ? Instead, I'm expecting <students> in place.
You can try to add a class decorations that will change serialized XML element names. Check it out below.
c#, version 1
void Main()
{
const string outputFile = #"e:\temp\SerializedFile.xml";
StudentDetails s1 = new StudentDetails();
s1.Name = "ABC"; s1.Age = 25;
StudentDetails s2 = new StudentDetails();
s2.Name = "DEF"; s2.Age = 35;
School school = new School();
school.students = new List<StudentDetails>();
school.students.Add(s1);
school.students.Add(s2);
// save new XML file, serialized from classes
using (StreamWriter sw = new StreamWriter(outputFile))
{
XmlSerializer x = new XmlSerializer(typeof(School));
// to remove not needed namespaces
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
// create new XML file
x.Serialize(sw, school, ns);
}
Console.WriteLine("Classes were serialized as '{0}' file.", outputFile);
}
[XmlRoot("school")]
public class School
{
[XmlElement(ElementName = "student")]
public List<StudentDetails> students { get; set; }
}
public class StudentDetails
{
public string Name { get; set; }
public int Age { get; set; }
}
Output
<?xml version="1.0" encoding="utf-8"?>
<school>
<student>
<Name>ABC</Name>
<Age>25</Age>
</student>
<student>
<Name>DEF</Name>
<Age>35</Age>
</student>
</school>
c#, version 2
void Main()
{
string outputXML = string.Empty;
StudentDetails s1 = new StudentDetails();
s1.Name = "ABC"; s1.Age = 25;
StudentDetails s2 = new StudentDetails();
s2.Name = "DEF"; s2.Age = 35;
School school = new School();
school.students = new List<StudentDetails>();
school.students.Add(s1);
school.students.Add(s2);
// save new XML file, serialized from classes
var settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
settings.ConformanceLevel = ConformanceLevel.Auto;
settings.IndentChars = "\t";
// to remove BOM
settings.Encoding = new UTF8Encoding(false);
using (var sw = new StringWriter())
{
using (XmlWriter xw = XmlWriter.Create(sw, settings))
{
XmlSerializer x = new XmlSerializer(typeof(School));
// to remove not needed namespaces
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
// create new XML file
x.Serialize(xw, school, ns);
}
outputXML = sw.ToString();
}
Console.WriteLine("Classes were serialized as an XML string:{1}{0}", outputXML, Environment.NewLine);
}
// Define other methods and classes here
[XmlRoot("school")]
public class School
{
[XmlElement(ElementName = "student")]
public List<StudentDetails> students { get; set; }
}
public class StudentDetails
{
public string Name { get; set; }
public int Age { get; set; }
}

C# Object Serialization to XML namespace and prefix in child element only

How to get a namespace set at a child level element when serializing an object to XML with C#?
The XML I want to end up with this as the XML output:
<clsPerson>
<FirstName>Jeff</FirstName>
<MI>J</MI>
<LastName>Jefferson</LastName>
<ns1:Addresses xmlns="www.whatever.co.uk">
<Home>Here</Home>
<Work>There</Work>
</Addresses>
</clsPerson>
The code I'm using is mostly taken from other questions, and is as follows:
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
public class clsPerson
{
public string FirstName;
public string MI;
public string LastName;
[XmlElement(Namespace = "www.whatever.co.uk")]
public clsPlaces Addresses;
public clsPerson()
{
clsPlaces clsPlaces = new clsPlaces();
}
}
[XmlRoot(Namespace = "")]
public class clsPlaces
{
public string Home { get; set; }
public string Work { get; set; }
}
class class1
{
static void Main(string[] args)
{
clsPerson p = new clsPerson();
var xml = "";
p.FirstName = "Jeff";
p.MI = "J";
p.LastName = "Jefefrson";
p.Addresses = new clsPlaces();
p.Addresses.Home = "Here";
p.Addresses.Work = "There";
var emptyNamepsaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
var serializer = new XmlSerializer(p.GetType());
var settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
emptyNamepsaces.Add("ns", "www.whatever.co.uk");
using (var stream = new StringWriter())
using (var writer = XmlWriter.Create(stream, settings))
{
serializer.Serialize(writer, p, emptyNamepsaces);
xml = stream.ToString();
}
Console.WriteLine(xml)
Console.ReadLine();
}
}
The above gives:
<clsPerson xmlns:ns="www.whatever.co.uk">
<FirstName>Jeff</FirstName>
<MI>J</MI>
<LastName>Jefefrson</LastName>
<ns:Addresses>
<Home>Here</Home>
<Work>There</Work>
</ns:Addresses>
How would I get the xmlns on the element only? I have tried everything I can see search stackoverflow, and not been able to find/understand the answer.
Thanks
Remove this - adding the namespace will ensure the prefix gets added to the root:
emptyNamepsaces.Add("ns", "www.whatever.co.uk");
The declaration for addresses will get added automatically. You also need to remove the empty namespace from clsPlaces, else the namspaces for Home and Work will be incorrect:
public class clsPlaces
{
public string Home { get; set; }
public string Work { get; set; }
}
The output will then look like this:
<clsPerson>
<FirstName>Jeff</FirstName>
<MI>J</MI>
<LastName>Jefefrson</LastName>
<Addresses xmlns="www.whatever.co.uk">
<Home>Here</Home>
<Work>There</Work>
</Addresses>
</clsPerson>
See this fiddle for a demo.

C# update XMLNS of XmlElemt

I have a class like:
[XmlRoot(ElementName = "Root", Namespace = "https://NS.com")]
public class Root
{
[XmlElement(Namespace = "https://NS2.com")]
public Header header { set; get; }
public Body{ set; get; }
}
I serialize the obj to XML:
using (MemoryStream ms = new MemoryStream())
{
using (XmlWriter wr = XmlWriter.Create(ms))
{
serializer.Serialize(wr, obj, ns);
}
ms.Position = 0;
XmlDocument doc.Load(ms);
ms.Close();
}
I couldn't find any way to change the nameSpace of Root and Header to newNS, and newHeadNS. Is there a way to change namespace in the doc? Any help would be greatly appreciated
You can use the XmlAttributeOverrides to override that.
Initialize your serializer as such :
var overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Root), new XmlAttributes() { XmlRoot = new XmlRootAttribute() { Namespace = "https://NS.new" } });
var header = new XmlAttributes();
header.XmlElements.Add(new XmlElementAttribute() { Namespace = "https://NS.new/head" });
overrides.Add(typeof(Root), "Header", header);
var serializer = new XmlSerializer(typeof(Root), overrides);
And, the following to serialize :
serializer.Serialize(wr, obj);

xml output from class

I'm trying to generate xml output (based on a class) that looks like this:
<cdsXMLEnvelope>
<TestValue1>x1</TestValue1>
<inner>
<eTestValue1>e1</eTestValue1>
</inner>
<inner>
<eTestValue1>e1</eTestValue1>
</inner>
<inner>
<eTestValue1>e1</eTestValue1>
</inner>
</cdsXMLEnvelope>
[System.Xml.Serialization.XmlRootAttribute("cdsXMLEnvelope", Namespace = "", IsNullable = false)]
[XmlTypeAttribute(Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
//public class cdsXMLEnvelope : cdsXMLinfo
public class cdsXMLEnvelope
{
[XmlElementAttribute(ElementName = "eTestValue1")]
public string eTestValue1 { get; set; }
[System.Xml.Serialization.XmlArrayItem("inner")]
public cdsXMLinfo[] cdsXMLinfo { get; set; }
}
//[XmlTypeAttribute(Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public class cdsXMLinfo
{
[XmlElementAttribute(ElementName = "TestValue1")]
public string TestValue1 { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute()]
public string TestValue3 { get; set; }
}
please help me hook this code up, change the class etc-- I keep trying a variety of ways & I get object not found & misc errors when trying to serialize it to xml
also can I avoid using an array for the inner elements? do I use lists?
I'll try to refine this question as we go, I know people hate these long ?'s
Thanks!!
{
cdsXMLEnvelope x = new cdsXMLEnvelope();
x.eTestValue1 = "x1";
x.cdsXMLinfo = new cdsXMLinfo[1];
x.cdsXMLinfo[0].TestValue1 = "xi1";
//x.cdsXMLinner[0].TestValue1 = "xi2";
XmlSerializer writer = new XmlSerializer(x.GetType());
StreamWriter file = new StreamWriter("x.xml");
writer.Serialize(file, x);
file.Close();
}
I think this gives what you want, however your example was not quite consistent:
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace Serialize
{
class Program
{
static void Main( string[] args )
{
var envelope = new CdsXmlEnvelope
{
TestValue1 = "x1",
InnerList = new List<CdsXmlInfo>
{
new CdsXmlInfo {TestValue1 = "e1"},
new CdsXmlInfo {TestValue1 = "e1"},
new CdsXmlInfo {TestValue1 = "e1"},
}
};
var ns = new XmlSerializerNamespaces();
ns.Add( string.Empty, string.Empty );
var serializer = new XmlSerializer( typeof( CdsXmlEnvelope ) );
using( var stream = new MemoryStream() )
{
var settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
settings.Indent = true;
var writer = XmlWriter.Create( stream, settings );
serializer.Serialize( writer, envelope, ns );
stream.Position = 0;
Console.WriteLine( new StreamReader( stream ).ReadToEnd() );
}
Console.ReadLine();
}
}
[XmlRoot( "cdsXMLEnvelope" )]
public class CdsXmlEnvelope
{
[XmlElement( "TestValue1" )]
public string TestValue1 { get; set; }
[XmlElement( "inner" )]
public List<CdsXmlInfo> InnerList { get; set; }
}
public class CdsXmlInfo
{
[XmlElement( "TestValue1" )]
public string TestValue1 { get; set; }
[XmlAttribute]
public string TestValue3 { get; set; }
}
}
giving output:
<cdsXMLEnvelope>
<TestValue1>x1</TestValue1>
<inner>
<TestValue1>e1</TestValue1>
</inner>
<inner>
<TestValue1>e1</TestValue1>
</inner>
<inner>
<TestValue1>e1</TestValue1>
</inner>
</cdsXMLEnvelope>
If you have the schema, you could try using Xsd.exe to generate the C# classes for you.

Categories

Resources