Serialize Modelstate to XML asp.net mvc - c#

Im trying the serialize the modelstate to an xml string. First I create a modelstate dictionary and this dictionary I try to serialize. This is the code I use:
ModelStateDictionary dict = new ModelStateDictionary();
dict.Merge(ModelState);
XmlSerializer serializer = new XmlSerializer(dict.GetType());
using (StringWriter writer = new StringWriter()){
serializer.Serialize(writer, dict);
var r2 = writer.ToString();
}
r2 is created, but all tags are filled with \r\n. What am I doing wrong?

You could use a XmlWriter and specify in the settings that you don't want to keep whitespaces and indentation:
XmlSerializer serializer = new XmlSerializer(dict.GetType());
var settings = new XmlWriterSettings { Indent = false };
using (var stream = new MemoryStream())
using (var writer = XmlWriter.Create(stream, settings))
{
serializer.Serialize(writer, dict);
string r2 = Encoding.UTF8.GetString(stream.ToArray());
}

Related

XML Deserialization encoding issue

I already searched a lot and unable to find a solution and unable to determine the correct approach
I am serializing an object to xml string and deserializing it back to an object using c#. XML string after serialization adds a leading ?. When I dezerialize it back to the object I am getting an error There is an error in XML document (1, 1)
?<?xml version="1.0" encoding="utf-16"?>
Serialization code:
string xmlString = null;
MemoryStream memoryStream = new MemoryStream();
XmlSerializer xs = new XmlSerializer(typeof(T));
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("abc", "http://example.com/abc/");
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream,Encoding.Unicode);
xs.Serialize(xmlTextWriter, obj, ns);
memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
xmlString = ConvertByteArrayToString(memoryStream.ToArray());
ConvertByteArrayToString:
UnicodeEncoding encoding = new UnicodeEncoding();
string constructedString = encoding.GetString(characters);
Deserialization Code:
XmlSerializer ser = new XmlSerializer(typeof(T));
StringReader stringReader = new StringReader(xml);
XmlTextReader xmlReader = new XmlTextReader(stringReader);
object obj = ser.Deserialize(xmlReader);
xmlReader.Close();
stringReader.Close();
return (T)obj;
I would like to know what I am doing wrong with encoding and I need a solution that works for most cases. Thanks
Use following function for serialization and Deserialization
public static string Serialize<T>(T dataToSerialize)
{
try
{
var stringwriter = new System.IO.StringWriter();
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(stringwriter, dataToSerialize);
return stringwriter.ToString();
}
catch
{
throw;
}
}
public static T Deserialize<T>(string xmlText)
{
try
{
var stringReader = new System.IO.StringReader(xmlText);
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
catch
{
throw;
}
}
Your serialized XML contains a Unicode byte-order mark in the beginning, and this is where the deserializer fails.
To remove the BOM you need to create a different version of encoding suppressing BOM instead of using default Encoding.Unicode:
new XmlTextWriter(memoryStream, new UnicodeEncoding(false, false))
Here the second false prevents BOM being prepended to the string.

Error reading XML file

Person person = GetPerson();
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add(string.Empty, string.Empty);
XmlSerializer serializer = new XmlSerializer(typeof(Person));
string personText = string.Empty;
using (MemoryStream memoryStream = new MemoryStream())
{
using (XmlWriter xmlWriter = XmlWriter.Create(memoryStream, new XmlWriterSettings() { Encoding = Encoding.UTF8 }))
{
serializer.Serialize(xmlWriter, person, ns);
xmlWriter.Flush();
personText = Encoding.UTF8.GetString(memoryStream.ToArray());
}
}
string path = #"D:\person.xml";
// Write method 1:
File.WriteAllText(path, personText);
// Write method 2:
using (StreamWriter streamWriter = new StreamWriter(path, false , Encoding.UTF8))
{
streamWriter.Write(personText);
}
// Read the xml
using (FileStream fileStream = new FileStream(path, FileMode.Open))
{
return XDocument.Load(XmlReader.Create(fileStream));
}
When I read the xml after writing using method 2, I get this Data at the root level is invalid. Line 1, position 1. But it works fine using method 1.
What is causing this? Any pointers appreciated.
The problem is that both the StreamWriter and the XmlWriter are adding a byte-order-mark.
Options:
String the BOM from personText to start with
Pass new UTF8Encoding(false) instead of Encoding.UTF8 for the StreamWriter
Pass new UTF8Encoding(false) instead of Encoding.UTF8 for the XmlWriter
Avoid converting to text and back again in the first place: you've got the binary data in the MemoryStream, why not just dump that to disk?

XmllSerializer.Serialize and returning raw xml

I am using an XmlSerializer to serialize an object to xml. After the object gets serialized, I end up with something like...
<?xml version="1.0" encoding="utf-8"?>
<xml xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
</xml>
I need to have it return simply...
<xml></xml>
Is there a way to serialize xml without the extra information? I realize strictly proper xml requires these additional elements, but I need the simpler form as I am appending these xml strings together to form a larger xml blob.
UPDATE
I was able to get the following code to work...
public static string Serialize(object o)
{
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
xws.Indent = true;
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add(String.Empty, String.Empty);
StringBuilder sb = new StringBuilder();
XmlWriter xmlw = XmlWriter.Create(sb, xws);
XmlSerializer serializer = new XmlSerializer(o.GetType());
serializer.Serialize(xmlw, o, ns);
xmlw.Flush();
return sb.ToString();
}
In C#, you can do something like this:
XmlSerializer serializer = new XmlSerializer(typeof(object));
StringWriter stringWriter = new StringWriter();
using (XmlWriter writer = XmlWriter.Create(stringWriter, new XmlWriterSettings() { OmitXmlDeclaration = true }))
{
serializer.Serialize(writer, this, new XmlSerializerNamespaces() { "",""});
}
string xmlText = stringWriter.ToString();
Explanation:
OmitXmlDeclaration = true makes it remove the declaration.
new XmlSerializerNamespaces() { "",""} removes the namespaces.

remove encoding from xmlserializer

I am using the following code to create an xml document -
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
new XmlSerializer(typeof(docket)).Serialize(Console.Out, i, ns);
this works great in creating the xml file with no namespace attributes. i would like to also have no encoding attribute in the root element, but I cannot find a way to do it. Does anyone have any idea if this can be done?
Thanks
Old answer removed and update with new solution:
Assuming that it's ok to remove the xml declaration completly, because it makes not much sense without the encoding attribute:
XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", "");
using (XmlWriter writer = XmlWriter.Create(Console.Out, new XmlWriterSettings { OmitXmlDeclaration = true}))
{
new XmlSerializer(typeof (SomeType)).Serialize(writer, new SomeType(), ns);
}
To remove encoding from XML header pass TextWriter with null encoding to XmlSerializer:
MemoryStream ms = new MemoryStream();
XmlTextWriter w = new XmlTextWriter(ms, null);
s.Serialize(w, vs);
Explanation
XmlTextWriter uses encoding from TextWriter passed in constructor:
// XmlTextWriter constructor
public XmlTextWriter(TextWriter w) : this()
{
this.textWriter = w;
this.encoding = w.Encoding;
..
It uses this encoding when generating XML:
// Snippet from XmlTextWriter.StartDocument
if (this.encoding != null)
{
builder.Append(" encoding=");
...
string withEncoding;
using (System.IO.MemoryStream memory = new System.IO.MemoryStream()) {
using (System.IO.StreamWriter writer = new System.IO.StreamWriter(memory)) {
serializer.Serialize(writer, obj, null);
using (System.IO.StreamReader reader = new System.IO.StreamReader(memory)) {
memory.Position = 0;
withEncoding= reader.ReadToEnd();
}
}
}
string withOutEncoding= withEncoding.Replace("<?xml version=\"1.0\" encoding=\"utf-8\"?>", "");
Credit to this blog for helping me with my code
http://blog.dotnetclr.com/archive/2008/01/29/removing-declaration-and-namespaces-from-xml-serialization.aspx
here's my solution, same idea, but in VB.NET and a little clearer in my opinion.
Dim sw As StreamWriter = New, StreamWriter(req.GetRequestStream,System.Text.Encoding.ASCII)
Dim xSerializer As XmlSerializer = New XmlSerializer(GetType(T))
Dim nmsp As XmlSerializerNamespaces = New XmlSerializerNamespaces()
nmsp.Add("", "")
Dim xWriterSettings As XmlWriterSettings = New XmlWriterSettings()
xWriterSettings.OmitXmlDeclaration = True
Dim xmlWriter As XmlWriter = xmlWriter.Create(sw, xWriterSettings)
xSerializer.Serialize(xmlWriter, someObjectT, nmsp)

Serializing WITHOUT xmlns

I have a couple extension methods that handle serialization of my classes, and since it can be a time consuming process, they are created once per class, and handed out by this method.
public static XmlSerializer GetSerializerFor(Type typeOfT)
{
if (!serializers.ContainsKey(typeOfT))
{
var xmlAttributes = new XmlAttributes();
var xmlAttributeOverrides = new XmlAttributeOverrides();
System.Diagnostics.Debug.WriteLine(string.Format("XmlSerializerFactory.GetSerializerFor(typeof({0}));", typeOfT));
xmlAttributes.Xmlns = false;
xmlAttributeOverrides.Add(typeOfT, xmlAttributes);
var newSerializer = new XmlSerializer(typeOfT, xmlAttributeOverrides);
serializers.Add(typeOfT, newSerializer);
}
return serializers[typeOfT];
}
This is called by the extension method .Serialize()
public static XElement Serialize(this object source)
{
try
{
var serializer = XmlSerializerFactory.GetSerializerFor(source.GetType());
var xdoc = new XDocument();
using (var writer = xdoc.CreateWriter())
{
serializer.Serialize(writer, source, new XmlSerializerNamespaces(new[] { new XmlQualifiedName("", "") }));
}
return (xdoc.Document != null) ? xdoc.Document.Root : new XElement("Error", "Document Missing");
}
catch (Exception x)
{
return new XElement("Error", x.ToString());
}
}
Unfortunately, when Serializing classes that are auto-generated, they have the attribute XmlTypeAttribute(Namespace="http://tempuri.org/") applied to them.
This causes the deserialization by the the non-auto-generated counterparts to fail.
I need the serializer to completely ignore and not apply the namespace, but what I have written in the first block of code doesn't seem to remove it, I still end up with xml like this
<Note>
<ID xmlns="http://tempuri.org/">12</ID>
<Author xmlns="http://tempuri.org/">
<ID>1234</ID>
<Type>Associate</Type>
<IsAvailable>false</IsAvailable>
</Author>
<Created xmlns="http://tempuri.org/">2010-06-22T09:38:01.5024351-05:00</Created>
<Text xmlns="http://tempuri.org/">This is an update</Text>
</Note>
Instead of the same, minus the xmlns="http://tempuri.org/" attribute.
Please help, thanks, this is driving me crazy!
EDIT:
I know the problem, just not how to fix it.
My class, isn't just full of simple types.
It contains properties with types of other classes. Which are also auto-generated with the XmlTypeAttribute(Namespace = "http://tempuri.org/") attribute. So what is happening, is that when serialization occurs, and it serializes the properties of my class, they aren't going through my custom serialization, and thus, are having the attribute applied and not overridden.
Now I just need to figure out how to jump that hoop. Any thoughts as to how?
EDIT 2:
The following works to serialize WITHOUT xmlns... but I'm having a problem on the deserialization end, just not yet sure if it's related or not
public static XmlSerializer GetSerializerFor(Type typeOfT)
{
if (!serializers.ContainsKey(typeOfT))
{
var xmlAttributes = new XmlAttributes();
var xmlAttributeOverrides = new XmlAttributeOverrides();
System.Diagnostics.Debug.WriteLine(string.Format("XmlSerializerFactory.GetSerializerFor(typeof({0}));", typeOfT));
xmlAttributes.XmlType = new XmlTypeAttribute
{
Namespace = ""
};
xmlAttributes.Xmlns = false;
var types = new List<Type> {typeOfT, typeOfT.BaseType};
foreach (var property in typeOfT.GetProperties())
{
types.Add(property.PropertyType);
}
types.RemoveAll(t => t.ToString().StartsWith("System."));
foreach (var type in types)
{
xmlAttributeOverrides.Add(type, xmlAttributes);
}
var newSerializer = new XmlSerializer(typeOfT, xmlAttributeOverrides);
//var newSerializer = new XmlSerializer(typeOfT, xmlAttributeOverrides, extraTypes.ToArray(), new XmlRootAttribute(), string.Empty);
//var newSerializer = new XmlSerializer(typeOfT, string.Empty);
serializers.Add(typeOfT, newSerializer);
}
return serializers[typeOfT];
}
EDIT3:
Ended up using solution from
How to remove all namespaces from XML with C#?
public static XElement RemoveAllNamespaces(this XElement source)
{
return !source.HasElements
? new XElement(source.Name.LocalName)
{
Value = source.Value
}
: new XElement(source.Name.LocalName, source.Elements().Select(el => RemoveAllNamespaces(el)));
}
A working solution, for the record!
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
var serializer = new XmlSerializer(yourType);
serializer.Serialize(xmlTextWriter, someObject, ns);
No problem - just pass an empty string as the default namespace to the XML serializer:
XmlSerializer newSerializer =
new XmlSerializer(typeOfT, "");
Unfortunately, there's no easy constructor overload if you really need to define the XmlAttributeOverrides and the default namespace - so either you can skip the XmlAttributeOverrides and use that constructor I mentioned, or you need to use the one that defines all possible parameters (including XmlAttributeOverrides and default XML namespaces - and a few more).
public static byte[] SerializeByteByType(object objectToSerialize, Type type)
{
XmlWriterSettings xmlSetting = new XmlWriterSettings()
{
NewLineOnAttributes = false,
OmitXmlDeclaration = true,
Indent = false,
NewLineHandling = NewLineHandling.None,
Encoding = Encoding.UTF8,
NamespaceHandling = NamespaceHandling.OmitDuplicates
};
using (MemoryStream stm = new MemoryStream())
{
using (XmlWriter writer = XmlWriter.Create(stm, xmlSetting))
{
var xmlAttributes = new XmlAttributes();
var xmlAttributeOverrides = new XmlAttributeOverrides();
xmlAttributes.Xmlns = false;
xmlAttributes.XmlType = new XmlTypeAttribute() { Namespace = "" };
xmlAttributeOverrides.Add(type, xmlAttributes);
XmlSerializer serializer = new XmlSerializer(type, xmlAttributeOverrides);
//Use the following to serialize without namespaces
XmlSerializerNamespaces xmlSrzNamespace = new XmlSerializerNamespaces();
xmlSrzNamespace.Add("", "");
serializer.Serialize(writer, objectToSerialize, xmlSrzNamespace);
stm.Flush();
stm.Position = 0;
}
return stm.ToArray();
}
}
public static string SerializeToXml(object obj)
{
UTF8Encoding encoding = new UTF8Encoding(false);
var xmlAttributes = new XmlAttributes();
xmlAttributes.Xmlns = false;
xmlAttributes.XmlType = new XmlTypeAttribute() { Namespace = "" };
var xmlAttributeOverrides = new XmlAttributeOverrides();
var types = obj.GetType().Assembly.GetTypes().Where(t => string.Equals(t.Namespace, obj.GetType().Namespace, StringComparison.Ordinal));
foreach (var t in types) xmlAttributeOverrides.Add(t, xmlAttributes);
XmlSerializer sr = new XmlSerializer(obj.GetType(), xmlAttributeOverrides);
MemoryStream memoryStream = new MemoryStream();
StreamWriter writer = new StreamWriter(memoryStream, encoding);
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
// get the stream from the writer
memoryStream = writer.BaseStream as MemoryStream;
sr.Serialize(writer, obj, namespaces);
// apply encoding to the stream
return (encoding.GetString(memoryStream.ToArray()).Trim());
}
It will even work for complex object containing nested object
Credits to user3444796 and Mentor
Working solution (without empty xmlns attribute):
var ns = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
XmlSerializer serializer = new XmlSerializer(typeof(XmlInfo));
XmlDocument doc = new XmlDocument();
using (var ms = new MemoryStream()) {
serializer.Serialize(ms, this, ns);
ms.Position = 0;
doc.Load(ms);
}

Categories