Deserialize object property with StringReader vs XmlNodeReader - c#

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

Related

Deserializing XML Array in unity

Im trying to deserialize an array of objects from a XML Document.
The document built in the following structure:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<element>
.....
</element>
<element>
.....
</element>
</root>
But for some reason Im having lots of problems doing so.
This is my function which I call to deserialize it:
public static CardModel[] Load(string text)
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "root";
xRoot.IsNullable = true;
XmlSerializer serializer = new XmlSerializer(typeof(CardModel[]),xRoot);
StringReader reader = new StringReader(text);
CardModel[] o = serializer.Deserialize(reader) as CardModel[];
reader.Close();
return o;
}
And I am not sure if its done correctly or not. Because I know that in json you are unable to deserialize an array and you have to do some sort of "hack".
In the CardModel class (which is the element of the array) i use above the class the tag [XmlRoot("root")]. I have also tried to use [XmlRoot("element")] but still im getting stuck.
Afaik you can't directly deserialize into an array but would need a wrapper class like
[Serializable]
[XMLRoot("root")]
public class Root
{
// This does the magic of treating all "element" items nested under the root
// As part of this array
[XmlArray("element")]
public CardModel[] models;
}
And rather deserilialize into that like
public static CardModel[] Load(string text)
{
// I don't think that you need the attribute overwrite here
var serializer = new XmlSerializer(typeof(Root));
using(var reader = new StringReader(text))
{
var root = (Root) serializer.Deserialize(reader);
return root.models;
}
}

XML list not enclosed in root element deserialization

My XML has structure similar to:
<rootNode>
<node/>
<otherNode/>
<specificNode>
nested nodes
</specificNode>
<specificNode>
nested nodes
</specificNode>
</rootNode>
then I have corresponding class:
class rootNode
{
string node;
string otherNode;
List<specificNodesClass> specificNodes;
}
class specificNodesClass
{
//all the specific Node's nested nodes
}
How can I deserialize the XML to fullfill rootNode type object?
I've tried :
XmlSerializer serializer = new XmlSerializer(typeof(rootNode));
result = (rootNode)serializer.Deserialize(xdocument.CreateReader(ReaderOptions.None));
with [XmlElement("specificNode")] above the List<specificNodesClass> specificNodes;, but I get "Input string was not in a correct format." exception.
Could it be an exception caused by specificNode's nested elements incorrect deserialization?
Problem was at one of the specificNode's nested nodes, as I suspected. Problem solved! Thank you everybody!
You can use following code to deserialize the XML:
XmlSerializer deserializer = new XmlSerializer(typeof(rootNode));
TextReader reader = new StreamReader(#"D:\myXml.xml");//path of xml file
object obj = deserializer.Deserialize(reader);
rootNode XmlData = (rootNode)obj;
reader.Close();
For more freferences, Click here
Decorate class rootNode with [XmlRoot("rootNode")] and decorate the properties of your class appropriately with [XmlElement()]
You could also use
public static T DeSerializeObject<T>(string xml)
{
if (string.IsNullOrEmpty(xml))
{
return default(T);
}
XmlSerializer _xs = new XmlSerializer(typeof(T));
using (StringReader _tr = new StringReader(xml))
{
using (XmlReader _xr = XmlReader.Create(_tr, new XmlReaderSettings()))
{
return (T)_xs.Deserialize(_xr);
}
}
}

XmlNodeReader returns {None}

I'm currently having trouble deserializing an XmlDocument from a web service call, here is my code : -
public void getTest(XmlDocument requestDoc)
{
XmlDocument results = new XmlDocument();
XmlSerializer serial = new XmlSerializer(typeof(DataRequest));
DataRequest req;
XmlNodeReader reader = new XmlNodeReader(requestDoc.DocumentElement);
req = (DataRequest)serial.Deserialize(reader);
response.write(req.toString());
}
now, the trouble I am having is that the XmlNodeReader just contains "{None}" when I step through in debug, the requestDoc definatly has the expected XML structure, any ideas?
Kind regards
Gib
The "none" probably just means it hasn't started iterating yet, and is at BOF (for want of a better term). It should still work. Usually, if it doesn't it means the namespaces are incorrect - double-check for xmlns in the source.
This works fine, for example:
public class Test
{
static void Main()
{
var doc = new XmlDocument();
doc.LoadXml(#"<Test foo=""bar""></Test>");
var ser = new XmlSerializer(typeof(Test));
using (var reader = new XmlNodeReader(doc.DocumentElement))
{
var test = (Test)ser.Deserialize(reader);
Console.WriteLine(test.Foo);
}
}
[XmlAttribute("foo")]
public string Foo { get; set; }
}

XmlSerializer - Encoding not supported

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

How can I read information from an XML file and set them to attributes in a class?

I have this code:
public class Hero
{
XmlReader Reader = new XmlTextReader("InformationRepositories/HeroRepository/HeroInformation.xml");
XmlReaderSettings XMLSettings = new XmlReaderSettings();
public ImageSource GetHeroIcon(string Name)
{
XMLSettings.IgnoreWhitespace = true;
XMLSettings.IgnoreComments = true;
Reader.MoveToAttribute(" //I'm pretty much stuck here.
}
}
And this is the XML file I want to read from:
<?xml version="1.0" encoding="utf-8" ?>
<Hero>
<Legion>
<Andromeda>
<HeroType>Agility</HeroType>
<Damage>39-53</Damage>
<Armor>3.1</Armor>
<MoveSpeed>295</MoveSpeed>
<AttackType>Ranged(400)</AttackType>
<AttackRate>.75</AttackRate>
<Strength>16</Strength>
<Agility>27</Agility>
<Intelligence>15</Intelligence>
<Icon>Images/Hero/Andromeda.gif</Icon>
</Andromeda>
</Legion>
<Hellbourne>
</Hellbourne>
</Hero>
I'm tring to get the ,/Icon> element.
MoveToAttribute() won't help you, because everything in your XML is elements. The Icon element is a subelement of the Andromeda element.
One of the easiest ways of navigating an XML document if you're using the pre-3.5 xml handling is by using an XPathNavigator. See this example for getting started, but basically you just need to create it and call MoveToChild() or MoveToFollowing() and it'll get you to where you want to be in the document.
XmlDocument doc = new XmlDocument();
doc.Load("InformationRepositories/HeroRepository/HeroInformation.xml");
XPathNavigator nav = doc.CreateNavigator();
if (nav.MoveToFollowing("Icon",""))
Response.Write(nav.ValueAsInt);
Note that an XPathNavigator is a forward only mechanism, so it can be problematic if you need to do looping or seeking through the document.
If you're just reading XML to put the values into objects, you should seriously consider doing this automatically via object serialization to XML. This would give you a painless and automatic way to load your xml files back into objects.
Mark your attributes in your object according to the element you want to load them to:
See: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlattributeattribute.aspx
If, for some reason, you can't do this to your current object, consider making a bridge object which mirrors your original object and add a AsOriginal() method which returns the Original Object.
Working off the msdn example:
public class GroupBridge
{
[XmlAttribute (Namespace = "http://www.cpandl.com")]
public string GroupName;
[XmlAttribute(DataType = "base64Binary")]
public Byte [] GroupNumber;
[XmlAttribute(DataType = "date", AttributeName = "CreationDate")]
public DateTime Today;
public Group AsOriginal()
{
Group g = new Group();
g.GroupName = this.GroupName;
g.GroupNumber = this.GroupNumber;
g.Today = this.Today;
return g;
}
}
public class Group
{
public string GroupName;
public Byte [] GroupNumber;
public DateTime Today;
}
To Serialize and DeSerialize from LINQ objects, you can use:
public static string SerializeLINQtoXML<T>(T linqObject)
{
// see http://msdn.microsoft.com/en-us/library/bb546184.aspx
DataContractSerializer dcs = new DataContractSerializer(linqObject.GetType());
StringBuilder sb = new StringBuilder();
XmlWriter writer = XmlWriter.Create(sb);
dcs.WriteObject(writer, linqObject);
writer.Close();
return sb.ToString();
}
public static T DeserializeLINQfromXML<T>(string input)
{
DataContractSerializer dcs = new DataContractSerializer(typeof(T));
TextReader treader = new StringReader(input);
XmlReader reader = XmlReader.Create(treader);
T linqObject = (T)dcs.ReadObject(reader, true);
reader.Close();
return linqObject;
}
I don't have any example code of Serialization from non-LINQ objects, but the MSDN link should point you in the right direction.
You can use linq to xml:
public class XmlTest
{
private XDocument _doc;
public XmlTest(string xml)
{
_doc = XDocument.Load(new StringReader(xml);
}
public string Icon { get { return GetValue("Icon"); } }
private string GetValue(string elementName)
{
return _doc.Descendants(elementName).FirstOrDefault().Value;
}
}
you can use this regular exspression "<Icon>.*</Icon>" to find all the icons
then just remove the remove the tag, and use it....
would be a lot shorter
Regex rgx = new Regex("<Icon>.*</Icon>");
MatchCollection matches = rgx.Matches(xml);
foreach (Match match in matches)
{
string s= match.Value;
s= s.Remove(0,6)
s= s.Remove(s.LastIndexOf("</Icon>"),7);
console.Writeline(s);
}

Categories