XML serialisation namespace on the wrong element - c#

I'm posting XML to an API which has been serialised from a C# class at my end, everything looks correct apart from where a particular namespace is when the XML is generated but I am not sure why.
I need the a namespace to be be on the appointmentAvailability element. Ignore the escape backslashes that's just because I copied the xml out of the visual studio debugger.
Where am I going wrong?
function to test
var a = new AppointmentAvailability
{
TimeSlot = "AM"
};
var aa = new AppointmentAvailabilityContainer();
aa.appointmentAvailability = a;
var nd = new Dictionary<string, string>();
nd.Add("a", "http://maindomain/Appointments");
var x = XmlManager.SerializeThis<AppointmentAvailabilityContainer>(aa,nd);
var checkappointmentavailability = FluidWeb.CheckAppointmentAvailability(a);
generic serialise function
public static string SerializeThis<T>(object obj, Dictionary<string, string> dictionary)
{
try
{
XmlDocument xd = new XmlDocument();
string xml = "";
var xs = new XmlSerializer(obj.GetType());
XmlSerializerNamespaces xsn = new XmlSerializerNamespaces();
foreach (var item in dictionary)
{
xsn.Add(item.Key, item.Value);
}
using (MemoryStream ms = new MemoryStream())
{
xs.Serialize(ms, obj, xsn);
ms.Position = 0;
xd.Load(ms);
xml = xd.InnerXml;
}
return xml;
}
catch(XmlException x)
{
var xml = "Could not serialise.";
return xml;
}
}
classes
[XmlRoot("AppointmentAvailability", Namespace = "http://maindomain")]
public class AppointmentAvailabilityContainer
{
[XmlElement("appointmentAvailability")]
public AppointmentAvailability appointmentAvailability { get; set; }
}
[XmlRoot("appointmentAvailability", Namespace = "http://maindomain/Appointments")]
[XmlType("a")]
public class AppointmentAvailability
{
[XmlElement("TimeSlot")]
public string TimeSlot { get; set; }
}
xml generated
<?xml version=\"1.0\"?>
<AppointmentAvailability xmlns:a=\"http://maindomain/Appointments\" xmlns=\"http://maindomain">
<appointmentAvailability>
<a:TimeSlot>AM</a:TimeSlot>
</appointmentAvailability>
</AppointmentAvailability>
xml trying to achieve
<?xml version=\"1.0\"?>
<AppointmentAvailability xmlns=\"http://maindomain">
<appointmentAvailability xmlns:a=\"http://maindomain/Appointments\">
<a:TimeSlot>AM</a:TimeSlot>
</appointmentAvailability>
</AppointmentAvailability>

Related

C# Serialize object to SOAP string array issue

I have an issue where trying to serialize an object containing a string array to soap causes an exception in my application. I am doing the following to create the soap formatter:
XmlTypeMapping mapping = new SoapReflectionImporter().ImportTypeMapping(obj.GetType());
XmlSerializer serializer = new XmlSerializer(mapping);
when I call Serialize on the serializer I get the following exception. "Token StartElement in state Epilog would result in an invalid XML document."
However if I just want regular xml and create my XmlSerializer like this:
XmlSerializer serializer = new XmlSerializer(obj.GetType());
Everything works fine and the xml contains the string array.
I have a full example below that reproduces the issue on my machine if someone could take a look I would be very grateful as I am out of ideas!
static void Main(string[] args)
{
GetAlarmEventTypesResponse bob = new GetAlarmEventTypesResponse();
bob.GetAlarmEventTypesTypes = new string[] { "bob", "bob1", "bob2" };
bob.version = "2.0";
// works
string xml = GetRegularDocument(bob);
Console.WriteLine(xml);
// throws exception
string soap = GetSoapDocument(bob);
Console.WriteLine(soap);
}
//------------------------------------------------------------------------------
[System.Xml.Serialization.SoapTypeAttribute(Namespace = "http://example/common/dataexchange/2011/05")]
public class GetAlarmEventTypesResponse
{
public GetAlarmEventTypesResponse()
{
version = "1.2";
}
[System.Xml.Serialization.XmlArrayItemAttribute("Type", IsNullable = false)]
public string[] GetAlarmEventTypesTypes { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute()]
public string version { get; set; }
}
//------------------------------------------------------------------------------
public static string GetRegularDocument(object obj)
{
string document = null;
XmlSerializer serializer = new XmlSerializer(obj.GetType());
using (StringWriter textWriter = new StringWriter())
{
serializer.Serialize(textWriter, obj);
document = textWriter.ToString();
}
return document;
}
//------------------------------------------------------------------------------
public static string GetSoapDocument(object obj)
{
string document = null;
XmlTypeMapping mapping = new SoapReflectionImporter().ImportTypeMapping(obj.GetType());
XmlSerializer serializer = new XmlSerializer(mapping);
using (StringWriter textWriter = new StringWriter())
{
serializer.Serialize(textWriter, obj);
document = textWriter.ToString();
}
return document;
}

C# Deserialization failure on enabling schema validation

I serialized a class to XML. But deserialization to the same class type is failing when schema validation is enabled.
Here is what I'm doing:
creating an object from the serializable class
Serializing that object to XML
Gets the schema from the that object
Adds that schema to validation
deserialize with out validation
deserialize with XMLschema validation
In step six, it is failing...
Here in this code sample, method with validation is failing:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace Deserialize
{
public class Program
{
static string filepath = "TestSerilize.xml";
private static object oSchema;
private static XmlReaderSettings oXmlReaderSettings;
static void Main(string[] args)
{
MyObject oMyobject = new MyObject();
oMyobject.MyObjectType = "MyCustomType";
List<Items> olistItems = new List<Items>();
Items oItems = new Items();
oItems.key = "test123";
oItems.value = "testvalue";
olistItems.Add(oItems);
oMyobject.Items = olistItems;
Saveobjecttofile(oMyobject, filepath);
dynamic objDeserialized = null;
objDeserialized = GetObjFormfileWithoutValidation(filepath, oMyobject.GetType());
objDeserialized = GetObjFormfileWithValidation(filepath, oMyobject.GetType());
}
private static dynamic GetObjFormfileWithValidation(string filepath, Type type)
{
XmlReaderSettings oXmlReaderSettings = new XmlReaderSettings();
oXmlReaderSettings.ValidationType = ValidationType.Schema;
dynamic oSchema = GetSchemaFromType(type);
oXmlReaderSettings.Schemas.Add(oSchema);
XmlReader oXmlReader = null;
if (oSchema != null)
{
oXmlReader = XmlReader.Create(filepath, oXmlReaderSettings);
}
else
{
oXmlReader = XmlReader.Create(filepath);
}
object obj = null;
try
{
XmlSerializer oXmlSerializer = new XmlSerializer(type);
obj = oXmlSerializer.Deserialize(oXmlReader);
}
finally
{
oXmlReader.Close();
}
return obj;
}
private static XmlSchema GetSchemaFromType(Type type)
{
var oSoapReflectionImporter = new SoapReflectionImporter();
var oXmlTypeMapping = oSoapReflectionImporter.ImportTypeMapping(type);
var oXmlSchemas = new XmlSchemas();
var oXmlSchema = new XmlSchema();
oXmlSchemas.Add(oXmlSchema);
var oXMLSchemaExporter = new XmlSchemaExporter(oXmlSchemas);
oXMLSchemaExporter.ExportTypeMapping(oXmlTypeMapping);
return oXmlSchema;
}
private static dynamic GetObjFormfileWithoutValidation(string filepath, Type type)
{
XmlReader oXmlReader = null;
oXmlReader = XmlReader.Create(filepath);
object obj = null;
try
{
XmlSerializer oXmlSerializer = new XmlSerializer(type);
obj = oXmlSerializer.Deserialize(oXmlReader);
}
finally
{
oXmlReader.Close();
}
return obj;
}
private static void Saveobjecttofile(object objectToSave, string filepath)
{
try
{
System.Xml.Serialization.XmlSerializer oXmlSerializer = new System.Xml.Serialization.XmlSerializer(objectToSave.GetType());
using (System.Xml.XmlTextWriter oXmlTextWriter = new System.Xml.XmlTextWriter(filepath, System.Text.Encoding.UTF8))
{
oXmlTextWriter.Indentation = 2;
oXmlTextWriter.Formatting = System.Xml.Formatting.Indented;
oXmlSerializer.Serialize(oXmlTextWriter, objectToSave);
oXmlTextWriter.Flush();
oXmlTextWriter.Close();
}
}
catch (Exception)
{ throw; }
}
}
[XmlType("Items")]
public class Items
{
[XmlAttribute("key")]
public string key { get; set; }
[XmlText()]
public string value { get; set; }
}
[Serializable, XmlRoot("MyObject")]
public class MyObject
{
[XmlElement("MyObjectType", IsNullable = true)]
public string MyObjectType { get; set; }
[XmlElement("Items")]
public List<Items> Items;
public string this[string key]
{
get
{
return null != Items.Find(x => x.key == key) ? Items.Find(x => x.key == key).value : null;
}
set
{
if (Items == null) Items = new List<Items>();
if (null != Items.Find(x => x.key == key))
{
Items.Find(x => x.key == key).value = value;
}
else
{
Items.Add(new Items { key = key, value = value });
}
}
}
}
}
Exception details:
System.Xml.Schema.XmlSchemaException
Message:There is an error in XML document (3, 10).
Inner Exception message:The 'key' attribute is not declared.
StackTrace:
at system.Xml.Schema.XmlSchemaValidator.SendValidationEvent(XmlSchemaValidationException e, XmlSeverityType severity)
at System.Xml.Schema.XmlSchemaValidator.SendValidationEvent(String code, String arg)
at System.Xml.Schema.XmlSchemaValidator.ValidateAttribute(String lName, String ns, XmlValueGetter attributeValueGetter, String attributeStringValue, XmlSchemaInfo schemaInfo)
at System.Xml.Schema.XmlSchemaValidator.ValidateAttribute(String localName, String namespaceUri, XmlValueGetter attributeValue, XmlSchemaInfo schemaInfo)
at System.Xml.XsdValidatingReader.ValidateAttributes()
at System.Xml.XsdValidatingReader.ProcessElementEvent()
at System.Xml.XsdValidatingReader.ProcessReaderEvent()
at System.Xml.XsdValidatingReader.Read()
at System.Xml.XmlReader.MoveToContent()
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderMyObject.Read3_MyObject(Boolean isNullable, Boolean checkType)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderMyObject.Read4_MyObject()
Demo fiddle here.
Your problem is here:
private static XmlSchema GetSchemaFromType(Type type)
{
var oSoapReflectionImporter = new SoapReflectionImporter();
SoapReflectionImporter is designed to generate a schema for a c# type that has been marked with SOAP attributes. Such a schema can be used to generate an XmlSerializer customized to use such attributes, as shown in How to: Serialize an Object as a SOAP-Encoded XML Stream:
XmlTypeMapping myTypeMapping = new SoapReflectionImporter().ImportTypeMapping(type);
XmlSerializer mySerializer = new XmlSerializer(myTypeMapping);
You, however, are not using SOAP attributes. You are using regular XmlSerializer attributes, as can be see e.g. in your Items class:
[XmlType("Items")]
public class Items
{
[XmlAttribute("key")]
public string key { get; set; }
[XmlText()]
public string value { get; set; }
}
Thus you should be using XmlReflectionImporter instead:
private static XmlSchema GetSchemaFromType(Type type)
{
var oReflectionImporter = new XmlReflectionImporter();
var oXmlTypeMapping = oReflectionImporter.ImportTypeMapping(type);
var oXmlSchemas = new XmlSchemas();
var oXmlSchema = new XmlSchema();
oXmlSchemas.Add(oXmlSchema);
var oXMLSchemaExporter = new XmlSchemaExporter(oXmlSchemas);
oXMLSchemaExporter.ExportTypeMapping(oXmlTypeMapping);
return oXmlSchema;
}
Related: How do I programmatically generate an xml schema from a type?
Fixed sample demo here.

How to serialize/de-serialize a Dictionary<string, object> into XML

I am trying to write a save routine for my application where several parts of the application add items to a Dictionary and then the save function writes them to a XML file. The open routine needs to read those files and re-populate the Dictionary and I can then place those objects back into my application. I am struggling with the de-serialization of the routine I have now. My save routine is as follows
XmlDocument xmlDoc = new XmlDocument();
// Write down the XML declaration
XmlDeclaration xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);
// Create the root element
XmlElement rootNode = xmlDoc.CreateElement("TireStudy");
xmlDoc.InsertBefore(xmlDeclaration, xmlDoc.DocumentElement);
xmlDoc.AppendChild(rootNode);
foreach (var saveItem in _SaveItems)
{
XPathNavigator nav = rootNode.CreateNavigator();
using (var writer = nav.AppendChild())
{
var serializer = new XmlSerializer(saveItem.Value.GetType());
writer.WriteWhitespace("");
serializer.Serialize(writer, saveItem.Value);
writer.Close();
}
}
xmlDoc.Save(fileName);
This routine works to create a file, but I would like the key value of the dictionary to be saved in the file as well and I am not sure how to de-serialize the file this creates because I do not know the types of the objects before I read them.
Part 2 (I hate adding new parts to a question, but I don't see a better way to address the problems going forward)
I now have the following code,
var knownTypes = new List<Type>
{
typeof(ObservableCollection<string>),
typeof(ObservableCollection<Segments>),
typeof(Segments),
typeof(List<string>)
};
var serialized = _SaveItems.Serialize(knownTypes);
but I get the following exception
Type 'System.Collections.Generic.List`1[System.String]' cannot be added to list of known types since another type 'System.Collections.ObjectModel.ObservableCollection`1[System.String]' with the same data contract name 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:ArrayOfstring' is already present. If there are different collections of a particular type - for example, List<Test> and Test[], they cannot both be added as known types. Consider specifying only one of these types for addition to the known types list.
If I delete either the typeof(ObservableCollection) or the typeof(List) it exceptions complaining it needs the one I deleted.
You could use DataContractSerializer as explained in this post but you may have to pass the known types as a parameter to the serializer to support nested object typed classes:
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.IO;
using System.Xml;
public static class SerializationExtensions
{
public static string Serialize<T>(this T obj, IEnumerable<Type> knownTypes)
{
var serializer = new DataContractSerializer(obj.GetType(), knownTypes);
using (var writer = new StringWriter())
using (var stm = new XmlTextWriter(writer))
{
serializer.WriteObject(stm, obj);
return writer.ToString();
}
}
public static T Deserialize<T>(this string serialized, IEnumerable<Type> knownTypes)
{
var serializer = new DataContractSerializer(typeof(T), knownTypes);
using (var reader = new StringReader(serialized))
using (var stm = new XmlTextReader(reader))
{
return (T)serializer.ReadObject(stm);
}
}
}
public class Address
{
public string Country { get; set; }
public string City { get; set; }
}
public class CodedAddress
{
public int CountryCode { get; set; }
public int CityCode { get; set; }
}
class Program
{
static void Main(string[] args)
{
var persons1 = new Dictionary<string, Address>();
persons1.Add("John Smith", new Address { Country = "US", City = "New York" });
persons1.Add("Jean Martin", new Address { Country = "France", City = "Paris" });
// no need to provide known types to the serializer
var serializedPersons1 = persons1.Serialize(null);
var deserializedPersons1 = serializedPersons1.Deserialize<Dictionary<string, Address>>(null);
var persons2 = new Dictionary<string, object>();
persons2.Add("John Smith", new Address { Country = "US", City = "New York" });
persons2.Add("Jean Martin", new CodedAddress { CountryCode = 33, CityCode = 75 });
// must provide known types to the serializer
var knownTypes = new List<Type> { typeof(Address), typeof(CodedAddress) };
var serializedPersons2 = persons2.Serialize(knownTypes);
var deserializedPersons2 = serializedPersons2.Deserialize<Dictionary<string, object>>(knownTypes);
}
}

Xml Serialization Error: Unable to cast object of type

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);
}
}
}

How do I deserialize XML namespaces in C# (System.Xml.Serialization)?

I am just putting the finishing touches to my Zthes format deserializer (System.Xml.Serialization) which uses the namespace "dc" in the element "thes". All "term" elements are deserializing fine because they have no namespace but I cannot figure out how to tell the deserializer that the "thes" elements have a namespace.
Here is what I am trying to do (which isn't working) so hopefully someone could give me the proper syntax.
[XmlElement("namespace:someElement")]
public string SomeElement;
Here's a quick sample for you...
[XmlRoot("myObject")]
public class MyObject
{
[XmlElement("myProp", Namespace = "http://www.whited.us")]
public string MyProp { get; set; }
[XmlAttribute("myOther", Namespace = "http://www.whited.us")]
public string MyOther { get; set; }
}
class Program
{
static void Main(string[] args)
{
var xnames = new XmlSerializerNamespaces();
xnames.Add("w", "http://www.whited.us");
var xser = new XmlSerializer(typeof(MyObject));
using (var ms = new MemoryStream())
{
var myObj = new MyObject()
{
MyProp = "Hello",
MyOther = "World"
};
xser.Serialize(ms, myObj, xnames);
var res = Encoding.ASCII.GetString(ms.ToArray());
/*
<?xml version="1.0"?>
<myObject xmlns:w="http://www.whited.us" w:myOther="World">
<w:myProp>Hello</w:myProp>
</myObject>
*/
}
}
}
[XmlElement("someElement", Namespace="namespace")]
public string SomeElement;
Addendum: Make sure "namespace" is the full URI of the namespace, not just the prefix.

Categories