I have the following extension method to serialize my class....
public static string SerializeToXml<T>(this object obj)
{
XDocument doc = new XDocument();
XmlSerializer ser = new XmlSerializer(typeof(T));
using (var writer = doc.CreateWriter())
{
ser.Serialize(writer, obj);
}
return doc.ToString();
}
This seems to work fine and returns the following string for my serialized object:
<AuthenticatedUser xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Username>mark</Username>
<UserID>4</UserID>
<Roles>
<string>AuthenticatedUsers</string>
</Roles>
<IsValid>false</IsValid>
</AuthenticatedUser>
However when I try to deserialize this string using the method below I get this error:
{"The encoding style '<AuthenticatedUser xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\r\n <Username>mark</Username>\r\n <UserID>4</UserID>\r\n <Roles>\r\n <string>AuthenticatedUsers</string>\r\n </Roles>\r\n <IsMale>false</IsMale>\r\n</AuthenticatedUser>' is not valid for this call because this XmlSerializer instance does not support encoding. Use the SoapReflectionImporter to initialize an XmlSerializer that supports encoding."}
Method....
public static T DeserializeFromXml<T>(this string xml)
{
var element = XElement.Parse(xml);
XmlSerializer ser = new XmlSerializer(typeof(T));
using (var reader = element.CreateReader())
{
return (T)ser.Deserialize(reader, xml);
}
}
So after I read the error message I changed the deserialize method to use the SoadReflectionImporter....
public static T DeserializeFromXml<T>(this string xml)
{
var element = XElement.Parse(xml);
SoapReflectionImporter soap = new SoapReflectionImporter();
var mapping = soap.ImportTypeMapping(typeof(T));
XmlSerializer ser = new XmlSerializer(mapping);
using (var reader = element.CreateReader())
{
return (T)ser.Deserialize(reader, xml);
}
}
However I now get this error...
{"The encoding style '<AuthenticatedUser xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\r\n <Username>mark</Username>\r\n <UserID>4</UserID>\r\n <Roles>\r\n <string>AuthenticatedUsers</string>\r\n </Roles>\r\n <IsValid>false</IsValid>\r\n</AuthenticatedUser>' is not valid for this call. Valid values are 'http://schemas.xmlsoap.org/soap/encoding/' for SOAP 1.1 encoding or 'http://www.w3.org/2003/05/soap-encoding' for SOAP 1.2 encoding."}
Does anyone know where I'm going wrong and how I can deserialize this string successfully?
The problem is the overload of the Deserialize method that you are calling:
return (T)ser.Deserialize(reader, xml);
The xml parameter in the call specifies the encoding style, but in this case you are passing the xml from the serialization. Simply delete the second parameter and just call Deserialize with the reader and it should work fine:
return (T)ser.Deserialize(reader);
XElement.CreateReader() doesn't return the XDeclaration.
Instead, try making an XmlReader from a StringReader.
Why are you using XmlSerializer ?
Unless you must control the way the output XML looks, you should be using DataContractSerializer
Here is a nice blog post about the two
Do you need the Parse(xml) call and the reader element? Since you have the string, can't you just deserialize the string? First convert to bytes...
byte [] bytes = Encoding.Unicode.GetBytes(xml);
MemoryStream mem = new MemoryStream(bytes);
returnValue = (T)ser.Deserialize(mem);
Related
I have created the following wrapper method to disable DTD
public class Program
{
public static void Main(string[] args)
{
string s = #"<?xml version =""1.0"" encoding=""utf-16""?>
<ArrayOfSerializingTemplateItem xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">
<SerializingTemplateItem>
</SerializingTemplateItem>
</ArrayOfSerializingTemplateItem >";
try
{
XmlReader reader = XmlWrapper.CreateXmlReaderObject(s);
XmlSerializer sr = new XmlSerializer(typeof(List<SerializingTemplateItem>));
Object ob = sr.Deserialize(reader);
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
Console.ReadLine();
}
}
public class XmlWrapper
{
public static XmlReader CreateXmlReaderObject(string sr)
{
byte[] byteArray = Encoding.UTF8.GetBytes(sr);
MemoryStream stream = new MemoryStream(byteArray);
stream.Position = 0;
XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.None;
settings.DtdProcessing = DtdProcessing.Ignore;
return XmlReader.Create(stream, settings);
}
}
public class SerializingTemplateItem
{
}
The above throws exception "There is no Unicode byte order mark. Cannot switch to Unicode." (Demo fiddle here: https://dotnetfiddle.net/pGxOE9).
But if I use the following code to create the XmlReader instead of calling the XmlWrapper method. It works fine.
StringReader stringReader = new StringReader( xml );
XmlReader reader = new XmlTextReader( stringReader );
But I need to use the wrapper method as a security requirement to disable DTD. I don't know why I am unable to deserialize after calling my wrapper method. Any help will be highly appreciated.
Your problem is that you have encoded the XML into a MemoryStream using Encoding.UTF8, but the XML string itself claims to be encoded in UTF-16 in the encoding declaration in its XML text declaration:
<?xml version ="1.0" encoding="utf-16"?>
<ArrayOfSerializingTemplateItem>
<!-- Content omitted -->
</ArrayOfSerializingTemplateItem >
Apparently when the XmlReader encounters this declaration, it tries honor the declaration and switch from UTF-8 to UTF-16 but fails for some reason - possibly because the stream really is encoded in UTF-8. Conversely when the deprecated XmlTextReader encounters the declaration, it apparently just ignores it as not implemented, which happens to cause things to work successfully in this situation.
The simplest way to resolve this is to read directly from the string using a StringReader using XmlReader.Create(TextReader, XmlReaderSettings):
public class XmlWrapper
{
public static XmlReader CreateXmlReaderObject(string sr)
{
var settings = new XmlReaderSettings
{
ValidationType = ValidationType.None,
DtdProcessing = DtdProcessing.Ignore,
};
return XmlReader.Create(new StringReader(sr), settings);
}
}
Since a c# string is always encoded internally in UTF-16 the encoding statement in the XML will be ignored as irrelevant. This will also be more performant as the conversion to an intermediate byte array is completely skipped.
Incidentally, you should dispose of your XmlReader via a using statement:
Object ob;
using (var reader = XmlWrapper.CreateXmlReaderObject(s))
{
XmlSerializer sr = new XmlSerializer(typeof(List<SerializingTemplateItem>));
ob = sr.Deserialize(reader);
}
Working sample fiddle here.
Related questions:
Meaning of - <?xml version="1.0" encoding="utf-8"?>
Ignoring specified encoding when deserializing XML
I'm using serialization to a string as follows.
public static string Stringify(this Process self)
{
XmlSerializer serializer = new XmlSerializer(typeof(Process));
using (StringWriter writer = new StringWriter())
{
serializer.Serialize(writer, self,);
return writer.ToString();
}
}
Then, I deserialize using this code. Please note that it's not an actual stringification from above that's used. In our business logic, it makes more sense to serialize a path, hence reading in from said path and creating an object based on the read data.
public static Process Processify(this string self)
{
XmlSerializer serializer = new XmlSerializer(typeof(Process));
using (XmlReader reader = XmlReader.Create(self))
return serializer.Deserialize(reader) as Process;
}
}
This works as supposed to except for a small issue with encoding. The string XML that's produced, contains the addition encoding="utf-16" as an attribute on the base tag (the one that's about XML version, not the actual data).
When I read in, I get an exception because of mismatching encodings. As far I could see, there's no way to specify the encoding for serialization nor deserialization in any of the objects I'm using.
How can I do that?
For now, I'm using a very brute work-around by simply cutting of the excessive junk like so. It's Q&D and I want to remove it.
public static string Stringify(this Process self)
{
XmlSerializer serializer = new XmlSerializer(typeof(Process));
using (StringWriter writer = new StringWriter())
{
serializer.Serialize(writer, self,);
return writer.ToString().Replace(" encoding=\"utf-16\"", "");
}
}
Why does XmlSerializer populate my object property with an XmlNode array when deserializing an empty typed element using XmlNodeReader instead of an empty string like it does when using StringReader (or XmlTextReader)?
The second assertion in the following code sample fails:
var doc = new XmlDocument();
doc.Load(new StringReader(#"
<Test xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""
xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
<Value xsi:type=""xsd:string"" />
</Test>"));
var ser = new XmlSerializer(typeof (Test));
var reader1 = new StringReader(doc.InnerXml);
var obj1 = (Test) ser.Deserialize(reader1);
Debug.Assert(obj1.Value is string);
var reader2 = new XmlNodeReader(doc.FirstChild);
var obj2 = (Test) ser.Deserialize(reader2);
Debug.Assert(obj2.Value is string);
public class Test
{
public object Value { get; set; }
}
I'm guessing it has something to do with the null internal NamespaceManager property but I'm not sure how to work around this mysterious limitation. How can I reliably deserialize a subset of my parsed XML document without converting it back into a string and re-parsing?
It looks like this is a very old XmlNodeReader bug that Microsoft have no intention of fixing. (Archived Microsoft Connect link here). I found a workaround on Lev Gimelfarb's blog here that adds namespaces to the reader's NameTable as prefixes are looked up.
public class ProperXmlNodeReader : XmlNodeReader
{
public ProperXmlNodeReader(XmlNode node) : base(node)
{
}
public override string LookupNamespace(string prefix)
{
return NameTable.Add(base.LookupNamespace(prefix));
}
}
I have been working with XML in database LINQ and find that it is very difficult to work with the serializer.
The database LINQ required a field that store XElement.
I have a complex object with many customized structure class, so I would like to use the XmlSerializer to serialize the object.
However, the serializer can only serialize to file ("C:\xxx\xxx.xml") or a memory stream.
However to convert or serialize it to be a XElement so that I can store in the database using LINQ?
And How to do the reverse? i.e. Deserialize an XElement...
Try to use this
using (var stream = new MemoryStream())
{
serializer.Serialize(stream, value);
stream.Position = 0;
using (XmlReader reader = XmlReader.Create(stream))
{
XElement element = XElement.Load(reader);
}
}
deserialize :
XmlSerializer xs = new XmlSerializer(typeof(XElement));
using (MemoryStream ms = new MemoryStream())
{
xs.Serialize(ms, xml);
ms.Position = 0;
xs = new XmlSerializer(typeof(YourType));
object obj = xs.Deserialize(ms);
}
To make what John Saunders was describing more explicit, deserialization is very straightforward:
public static object DeserializeFromXElement(XElement element, Type t)
{
using (XmlReader reader = element.CreateReader())
{
XmlSerializer serializer = new XmlSerializer(t);
return serializer.Deserialize(reader);
}
}
Serialization is a little messier because calling CreateWriter() from an XElement or XDocument creates child elements. (In addition, the XmlWriter created from an XElement has ConformanceLevel.Fragment, which causes XmlSerialize to fail unless you use the workaround here.) As a result, I use an XDocument, since this requires a single element, and gets us around the XmlWriter issue:
public static XElement SerializeToXElement(object o)
{
var doc = new XDocument();
using (XmlWriter writer = doc.CreateWriter())
{
XmlSerializer serializer = new XmlSerializer(o.GetType());
serializer.Serialize(writer, o);
}
return doc.Root;
}
First of all, see Serialize Method to see that the serializer can handle alot more than just memory streams or files.
Second, try using XElement.CreateWriter and then passing the resulting XmlWriter to the serializer.
The SQL has XML data type may be this can help you look at msdn
I would like to use JsonFx to convert XML to/from custom types and LINQ queries. Can anyone please provide an example to de-serialisation and serialisation back again?
Here's an example of the XML I'm working with.
XML pasted here: http://pastebin.com/wURiaJM2
JsonFx Supports several strategies of binding json to .net objects including dynamic objects. https://github.com/jsonfx/jsonfx
Kind regards
Si
PS I did try pasting the xml document into StackOverflow but it removed a lot of the documents quotes and XML declaration.
Here's a method that I have used. It may require some tweaking:
public static string SerializeObject<T>(T item, string rootName, Encoding encoding)
{
XmlWriterSettings writerSettings = new XmlWriterSettings();
writerSettings.OmitXmlDeclaration = true;
writerSettings.Indent = true;
writerSettings.NewLineHandling = NewLineHandling.Entitize;
writerSettings.IndentChars = " ";
writerSettings.Encoding = encoding;
StringWriter stringWriter = new StringWriter();
using (XmlWriter xml = XmlWriter.Create(stringWriter, writerSettings))
{
XmlAttributeOverrides aor = null;
if (rootName != null)
{
XmlAttributes att = new XmlAttributes();
att.XmlRoot = new XmlRootAttribute(rootName);
aor = new XmlAttributeOverrides();
aor.Add(typeof(T), att);
}
XmlSerializer xs = new XmlSerializer(typeof(T), aor);
XmlSerializerNamespaces xNs = new XmlSerializerNamespaces();
xNs.Add("", "");
xs.Serialize(xml, item, xNs);
}
return stringWriter.ToString();
}
And for Deserialization:
public static T DeserializeObject<T>(string xml)
{
using (StringReader rdr = new StringReader(xml))
{
return (T)new XmlSerializer(typeof(T)).Deserialize(rdr);
}
}
And call it like this:
string xmlString = Serialization.SerializeObject(instance, "Root", Encoding.UTF8);
ObjectType obj = Serialization.DeserializeObject<ObjectType>(xmlString);
Hope this helps. The rootName parameter in the Serialize method lets you customize the value of the root node in the resulting xml string. Also, your classes must be decorated with the proper Xml attributes which will control how an entity is serialized.
This post explains how to create an XSD and a Classes from an XML file and then covers serialisation and de-serialisation.
http://geekswithblogs.net/CWeeks/archive/2008/03/11/120465.aspx
Using this technique with the XSD.exe to create an XSD and then classes in a CS file I was able to serialisation and then de-serialisation back again.
However the serialisation process does not create an exact representation of the source XML, so there's still some post work to be done there.