I was using this Xml Deserialization with complex elements in c# as a reference. I've noticed there are a lot of threads about Deserializing xml, but they don't mention when a tag has multiple values within it.
I am trying to deserialize my xml for lvl3 into an array of objects.
I'm getting a "there is an error in the xml document (1, 2)" error.
I have an xml string that I'm retrieving via an HTTP GET request that is formatted like this:
<xml ...>
<lvl1 id="xxx" name="yyy">
<lvl2 id="aaa" name="bbb">
<lvl3 id="mmm" name="nnn" val1="ppp" val2="qqq">
<lvl4a x="000" y="000" z="000" />
<lvl4b a="000" b="000" c="000" />
<lvl4c l="000" w="000" h="000" />
...
</lvl3>
</lvl2>
</lvl1>
</xml>
I have the following code that keeps throwing an exception:
"An unhandled exception of type 'System.InvalidOperationException' occurred in System.Xml.dll"
The exception is thrown by this line:
temp = (Test)new XmlSerializer(typeof(Test)).Deserialize(rdr);
But I'm not sure how to go about debugging it to find the error. Here is the complete code:
XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(xmlstring);
XmlNodeList list = xmldoc.GetElementsByTagName("lvl2");
for (int i = 0; i < list.Count; i++)
{
Test temp = new Test();
using (XmlReader rdr = XmlReader.Create(new StringReader(list[i].InnerXml)))
{
temp = (Test)new XmlSerializer(typeof(Test)).Deserialize(rdr); // exception thrown here
}
Console.WriteLine(temp.id);
Console.WriteLine(temp.overall.x);
}
[XmlRoot("lvl3")]
public class Test{
[XmlAttribute("id")]
public string id { get; set; }
[XmlAttribute("name")]
public string name { get; set; }
[XmlElement(ElementName = "lvl4a")]
public Overall overall { get;set; }
}
public class Overall
{
[XmlAttribute("x")]
public string x { get; set; }
[XmlAttribute("y")]
public string y { get;set; }
[XmlAttribute("z")]
public string z { get;set; }
}
Either fix your Test class to include public properties for val1 and val2 attributes or remove them from the xml. The schema of xml should match the class structure.
Related
I'm using C# (WPF).
I have the following XML file:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationToRun IsFromXML="true">
<Apps>
<FulllPath>c:\file1.txt</FullPath>
<FulllPath>c:\file2.txt</FullPath>
<FulllPath>c:\file3.txt</FullPath>
</Apps>
</ApplicationToRun>
And i try to deseriazlier the xml file.My Code:
mlSerializer xs = new XmlSerializer(typfof(MyXMLClass));
StringReader st = new StringReader(xmlPath);
MyXMLClass apps = (MyXMLClass)xs.Deserialize(st); //Exception - System.Windows.Markup.XamlParseException
Inside the inner exception: System.InvalidOperationException :There is an error in XML document (1.1) ...
My Classes:
[XmlRootAttriute("ApplicationToRun:)
public class MyXMLClass
{
[XmlAttribute]
public bool IsFromXML {get;set;}
[XmlElement("Apps")]
public FullPath [] fullPath {get;set;}
}
public class FullPath
{
public stirng fullPathApp {set;get;}
}
Where is my mistake?
Thanks!
The XML is badly formed, but all it takes is a bit of cleaning up:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationToRun IsFromXML="true">
<Apps>
<FullPath>foo</FullPath>
<FullPath>bar</FullPath>
</Apps>
</ApplicationToRun>
Then assign the correct XML attributes to your class:
[XmlRootAttribute("ApplicationToRun")]
public class MyXMLClass
{
[XmlAttribute]
public bool IsFromXML { get; set; }
[XmlArray("Apps")]
[XmlArrayItem("FullPath")]
public string[] fullPath { get; set; }
}
There's a typo in the elements. The opening elements have 3 Ls (FulllPath) but the closing ones have 2 Ls (as they should) that's causing the XML error
<?xml version="1.0" encoding="utf-8"?>
<m:error xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<m:code />
<m:message xml:lang="en-US">An error occurred while processing this request.</m:message>
<m:innererror>
<m:message>Exception has been thrown by the target of an invocation.</m:message>
<m:type>System.Reflection.TargetInvocationException</m:type>
<m:stacktrace></m:stacktrace>
<m:internalexception>
<m:message>An error occurred while executing the command definition. See the inner exception for details.</m:message>
<m:type>System.Data.Entity.Core.EntityCommandExecutionException</m:type>
<m:stacktrace></m:stacktrace>
<m:internalexception>
<m:message>Cannot insert duplicate key row in object 'XXXX' with unique index 'XXXXXX'. The duplicate key value is (XXXXX)</m:message>
<m:type>System.Data.SqlClient.SqlException</m:type>
<m:stacktrace></m:stacktrace>
</m:internalexception>
</m:internalexception>
</m:innererror>
</m:error>" System.Data.Services.Client.DataServiceClientException
I have this XML structure coming back from a WEB API 2 ODATA source. I need to parse this error at the server then pass a new error to the client.I have tried deserializing this to various Exception types, but the fact that Exception Implement IDICTIONARY stops that. How can I deserialize this into a strongly typed object?
If the above isn't easily down, I guess my other question would be what is best practice for handling these errors
My suggestion as to how to handle these error messages that you get, is to create a similar looking class structure instead of trying to serialize the information into an Exception object.
This allows for a bit more flexibility on your end, in case the source of your XML decides to change the structure of their error messages instead of basing it on the structure of an Exception, plus it removes the requirement to try and figure out how to deserialize an Exception object (which according to a few searches is a bit more complexer).
So using the tool in Visual Studio to convert XML text into C# classes (found under Edit => Paste Special => Paste XML as Classes), the below class structure is obtained.
The actual structure is modified from what that tool generates, since it likes to bloat the classes a lot.
[XmlTypeAttribute(AnonymousType = true, Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")]
[XmlRootAttribute(ElementName = "error", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata", IsNullable = false)]
public partial class ODATAException
{
public string code { get; set; }
public ODATAErrorMessage message { get; set; }
public ODATAInternalException innererror { get; set; }
}
[XmlTypeAttribute(AnonymousType = true, Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")]
public partial class ODATAErrorMessage
{
[XmlAttributeAttribute(Form = System.Xml.Schema.XmlSchemaForm.Qualified, Namespace = "http://www.w3.org/XML/1998/namespace")]
public string lang { get; set; }
[XmlTextAttribute()]
public string Value { get; set; }
}
[XmlTypeAttribute(AnonymousType = true, Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")]
public partial class ODATAInternalException
{
public string message { get; set; }
public string type { get; set; }
public string stacktrace { get; set; }
public ODATAInternalException internalexception { get; set; }
}
Utilizing this class structure, you can then easily deserialize the received XML into a strong typed object and use it further in whichever way you like.
using (TextReader sampleTextReader = new StringReader(txtInput.Text)) // Change this whereever you get the XML string from
{
XmlSerializer sampleXmlSeri = new XmlSerializer(typeof(ODATAException));
ODATAException newExc = sampleXmlSeri.Deserialize(sampleTextReader) as ODATAException;
if (newExc != null)
{
// Do something with the error
}
}
You are also now free to give the variables any names that are more descriptive to yourself/your team and use the XmlElement attributes to link your properties to the actual names inside the XML.
There is also a very interesting and good Q/A regarding making an exception serializable, that I would recommend reading as well:
What is the correct way to make a custom .NET Exception serializable?
I have this XML:
<ResultData xmlns="http://schemas.datacontract.org/2004/07/TsmApi.Logic.BusinesEntities"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Information>Schedule added.</Information>
<Success>true</Success>
</ResultData>
Is there a way to get as result only that:
<ResultData>
<Information>Sched added.</Information>
<Success>true</Success>
</ResultData>
Without all the other things from the example below?
Because when I try to get the object of the result string shown below, it doesn't work.
Datacontract XML serialization
The code I try to use is:
var serializer = new XmlSerializer(typeof(ResultData));
var rdr = new StringReader(xmlResultString);
var resultingMessage = (ResultData)serializer.Deserialize(rdr);
And on the last row it shows me error:
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Xml.dll
Additional information: There is an error in XML document (1, 2).
<ResultData xmlns='http://schemas.datacontract.org/2004/07/TsmApi.Logic.BusinesEntities'> was not expected.
ResultData:
[DataContract]
public class ResultData
{
[DataMember]
public bool Success
{
get;
set;
}
[DataMember]
public string Information
{
get;
set;
}
}
You are seeing the exception due to DataContract serialization namespace in the xml. Ideally you want to deserialize this with DataContractSerializer.
If you want to use XmlSerializer, then you will have to clean up the namespace declaration. Following will cleanup all the namespace and allow you to use XmlSerializer. In the foreach loop, we have to remove IsNamespaceDeclaration attribute and then set the element Name property to LocalName.
string xmlResultString = #"<ResultData xmlns=""http://schemas.datacontract.org/2004/07/TsmApi.Logic.BusinesEntities""
xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"">
<Information>Schedule added.</Information>
<Success>true</Success>
</ResultData>";
var doc = XDocument.Parse(xmlResultString);
foreach (var element in doc.Descendants())
{
element.Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
element.Name = element.Name.LocalName;
}
xmlResultString = doc.ToString();
var rdr = new StringReader(xmlResultString);
var serializer = new XmlSerializer(typeof(ResultData));
var resultingMessage = (ResultData)serializer.Deserialize(rdr);
How I read in the specific value of an XML attribute from a node when my XML looks like the following:
<Settings>
<Display_Settings>
<Screen>
<Name Name="gadg" />
<ScreenTag Tag="asfa" />
<LocalPosition X="12" Y="12" Z="12" />
</Screen>
</Display_Settings>
</Settings>
I only know how to read in the inner text value of XML and not the attribute value. For instance, I want the value of X in LocalPosition. This is what I've tried so far;
XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Screen");
foreach (XmlNode nodeInfo in nodeList)
{
XmlNodeList nodeContent = nodeInfo.ChildNodes;
foreach (XmlNode nodeItems in nodeContent)
{
if (nodeItems.Name == "Tag")
{
print("There is a name");
}
if (nodeItems.Name == "LocalPosition")
{
print("TEST");
}
}
}
Though for what I want to do, I think this is the wrong way to go about it. Can someone point in the right direction please?
You can use LINQ to XML:
var xdoc = XDocument.Load(path_to_xml);
int x = (int)xdoc.Descendants("LocalPosition").First().Attribute("X");
Or with XPath
int x = (int)xdoc.XPathSelectElement("//LocalPosition").Attribute("X");
string XValue= nodeItems.Attributes["X"].Value; // will solve your problem.
I was a little confused when I first started parsing XML with C# too! Instead of trying to write it all yourself .NET provides System.XML.Linq and System.XML.Serialization to help us with all of the hard stuff!
This approach is slightly different as we:
Load the XML document into a System.XML.Linq.XDocument,
Deserialize the XDocument into .NET objects that we can use as we please =]
I hope you don't mind, but I tweaked your XML example a little bit. Try to avoid using too many attributes within your elements, as they tend to reduce readability. The first line of the XML sample just highlights the version that is being used.
You should be able to copy and paste the code sample below into a new console application to get a feel for how it works. Just make sure your XML file is in the ..\bin\Debug folder (otherwise you will need to provide a full file path reference to it).
As you can see, in this example your XML elements (Display_Settings, Screen, Name, ScreenTag, and LocalPosition) are all classes that we decorate with XMLElement attributes. These classes just have public properties that are automatically populated when we call Deserialize(). Neat!
The same applies to the X, Y and Z attributes.
There are a bunch of tutorials that can explain this a lot better than me - enjoy! =]
XML Example
<?xml version="1.0"?>
<Settings>
<Display_Settings>
<Screen>
<Name>NameGoesHere</Name>
<ScreenTag>ScreenTagGoesHere</ScreenTag>
<LocalPosition X="12" Y="12" Z="12" />
</Screen>
</Display_Settings>
</Settings>
Complete Code Sample
using System;
using System.Xml.Linq;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// Create XDocument to represent our XML file
XDocument xdoc = XDocument.Load("XmlFile.xml");
// Create a deserializer and break down our XML into c# objects
XmlSerializer deserializer = new XmlSerializer(typeof(Settings));
// Deserialize into a Settings object
Settings settings = (Settings)deserializer.Deserialize(xdoc.CreateReader());
// Check that we have some display settings
if (null != settings.DisplaySettings)
{
Screen screen = settings.DisplaySettings.Screen;
LocalPosition localPosition = screen.LocalPosition;
// Check to make sure we have a screen tag before using it
if (null != screen.ScreenTag)
{
Console.WriteLine("There is a name: " + screen.ScreenTag);
}
// We can just write our integers to the console, as we will get a document error when we
// try to parse the document if they are not provided!
Console.WriteLine("Position: " + localPosition.X + ", " + localPosition.Y + ", " + localPosition.Z);
}
Console.ReadLine();
}
}
[XmlRoot("Settings")]
public class Settings
{
[XmlElement("Display_Settings")]
public DisplaySettings DisplaySettings { get; set; }
}
public class DisplaySettings
{
[XmlElement("Screen")]
public Screen Screen { get; set; }
}
public class Screen
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("ScreenTag")]
public string ScreenTag { get; set; }
[XmlElement("LocalPosition")]
public LocalPosition LocalPosition { get; set; }
}
public class LocalPosition
{
[XmlAttribute("X")]
public int X { get; set; }
[XmlAttribute("Y")]
public int Y { get; set; }
[XmlAttribute("Z")]
public int Z { get; set; }
}
}
Try with nodeItems.Attributes["X"].Value
I am totally stumped on this one. I am getting a "Object reference not set to an instance of an object." error - but I can't figure out why. This is the code that I have:
public class PlayerProfile
{
public List<Profile> PlayerINfo = new List<Profile>();
public void LoadProfiles(string path)
{
XDocument xmlDoc = XDocument.Load(path);
PlayerINfo = new List<Profile>();
// This is where I get the error:
PlayerINfo = (from profiles in xmlDoc.Root.Element("OnlineProfile").Elements("Player")
select new Profile
{
Name = (string)profiles.Element("Name"),
Sex = (string)profiles.Element("Sex"),
Avatar = (string)profiles.Element("Avatar").Attribute("path") ?? "",
Created = (DateTime)profiles.Element("Created")
}).ToList();
}
}
Here is my Profile class:
public class Profile
{
public string Name { get; set; }
public string Sex { get; set; }
public string Avatar { get; set; }
public DateTime Created { get; set; }
}
EDIT - Adding the XML file code:
<?xml version="1.0" encoding="utf-8"?>
<OnlineProfile>
<Player>
<Name>Stacey</Name>
<Sex>Female</Sex>
<Avatar path="/images/Picture.png" />
<Ratio>
<Win>0</Win>
<Loss>0</Loss>
<Abandoned>0</Abandoned>
</Ratio>
<Created>6/19/2011</Created>
</Player>
</OnlineProfile>
from profiles in xmlDoc.Root.Element("OnlineProfile").Elements("Player")
This is the problem - OnlineProfile is your root element, just do
from profiles in xmlDoc.Root.Elements("Player")
Do this: from profiles in xmlDoc.Element("OnlineProfile").Elements("Player") instead of from profiles in xmlDoc.Root.Element("OnlineProfile").Elements("Player")
From the XML you posted "OnlineProfile" is your Root element, so the child elements you expect arent there.
Tried this out + solved this in visual studio. I see the people above beat me to it. Another way to achieve this is:
xmlDoc.Element("OnlineProfile").Elements("Player")
This is what I posted prior to the Xml becomming available...
here's a good candidate for that error
(string)profiles.Element("Avatar").Attribute("...
").Attribute would cause an error. You need to check for null.
e.g.
= profiles.Element("Avatar") != null ? (string)profiles.Element("Avatar").Attribute("... : null;
do you definitely have an element called Avatar in your Xml file. Did the file load in correctly?