How would I parse this XML String? - c#

I have an XML string like this:
<Summary>Foo</Summary><Description>Bar</Description>
I want to read this into a class object:
class Foobar()
{
string Summary {get;set;}
string Description {get;set;}
}
I tried using XmlTextReader, but it throws an exception that no root element was found. This is what I tried:
using (XmlTextReader reader = new XmlTextReader(new StringReader(comment)))
{
while (reader.Read())
//do something here
}
I also tried deserializing it directly into an object like this:
[XmlTypeAttribute]
public class Foobar
{
[XmlElementAttribute("Summary")]
public string Summary { get; set; }
[XmlElementAttribute("Description")]
public string Description { get; set; }
}
This fails as well because I cannot define a [XmlRootElement] for the class Foobar, since there is no root element.

Define a root element
<root>
<Summary>Foo</Summary>
<Description>Bar</Desciption>
</root>

You need to set root element for that your xaml would be
<root>
<Summary>Foo</Summary><Description>Bar</Description>
</root>
For Rootelement in XMLSeralizatoion : http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlrootattribute.aspx

The easiest way is probably to manually add a root element.
string xml = "<root>" + comment + "</root>";
Then you can parse it with whichever method you want.

Use a constructor form that allows for XMLFragments (chunks of XML that could be valid if put into a single element, but which are not so-rooted):
using (XmlTextReader reader = new XmlTextReader(comment, XmlNodeType.Element, null))
{
while (reader.Read())
//do something here
}
Better yet, use Create(), which gives more flexibility still.

Related

Direct read a xml with more of 4 levels of child nodes and get the values in different levels of deep at the same time

I try to read an xml file with almost 8 leves of child nodes but without succsses.
I try using LINQ, SelectNode, SelectSingleNode, try to use a dumb foreach over XMLNodeList.
In my last intent I use this dumb foreach over the XMLNodeList to try to catch the text or value of some nodes.
These nodes are in diferents levels of deep but I can get onle the first element in sequesnse but the rest only repeat the firs value.
This is part of my code.
XmlDocument xDocXML = new XmlDocument();
xDocXML.Load(file_name);//file_name is a string with the full path of the file
XmlNodeList Article = xDocXML.SelectNodes("/ArticleSet/Article/Document"); //We get the Document of the article
foreach(XmlNode n in Article)
{
spmid = n["ID"].InnerText;
liga = string.Concat(TestString1, spmid);
//Test 1
//stitle = n.SelectSingleNode("//Article/ArticleTitle").InnerText;
//Test 2
//stitle = n["//Article/ArticleTitle"].InnerText;
XmlNode titles = n.SelectSingleNode("//Article/ArticleTitle");
stitle = titles.InnerText;//This line only work once and it repeat in all xmlnodes read
camposcuenta = camposcuenta + 1;
dt_abstractdb.Rows.Add(new Object[] { camposcuenta.ToString(), spmid, stitle, sresum, liga, ligaPDF, ligadoi });
}
Any suggestion over this
Without knowing what your XML looks like, I would recomend creating a class to represent your XML file and then use serialization. With this solution you can have multiple levels and let the Framework handle them.
Check this for example: How to Deserialize XML document
You can also use an external tool to generate your POCO classes, for exemple: https://xmltocsharp.azurewebsites.net/
Example code from the link's solution:
Classes to represent your XML:
[Serializable()]
public class Car
{
[System.Xml.Serialization.XmlElement("StockNumber")]
public string StockNumber { get; set; }
[System.Xml.Serialization.XmlElement("Make")]
public string Make { get; set; }
[System.Xml.Serialization.XmlElement("Model")]
public string Model { get; set; }
}
[Serializable()]
[System.Xml.Serialization.XmlRoot("CarCollection")]
public class CarCollection
{
[XmlArray("Cars")]
[XmlArrayItem("Car", typeof(Car))]
public Car[] Car { get; set; }
}
Reading code:
CarCollection cars = null;
string path = "cars.xml";
XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
StreamReader reader = new StreamReader(path);
cars = (CarCollection)serializer.Deserialize(reader);
reader.Close();

Can I avoid cluttering my class with repetitive XmlElement attributes even though the root node is in a different namespace than its children?

I am retrieving XML documents from a web service I have no control over. The XML is formatted similarly to the following:
<?xml version="1.0"?>
<ns:obj xmlns:ns="somenamespace">
<address>1313 Mockingbird Lane</address>
<residents>5</residents>
</ns:obj>
where the root node is in the "ns" namespace, but none of its child elements are.
After some trial and error, I found that I could deserialize the document to a C# object by doing the following:
[XmlRoot(Namespace="somenamespace", ElementName="obj")]
public class xmlObject
{
[XmlElement(Namespace = "")]
public string address { get; set; }
[XmlElement(Namespace = "")]
public int residents { get; set; }
}
class Program
{
static void Main(string[] args)
{
string xml =
"<?xml version=\"1.0\"?>" +
"<ns:obj xmlns:ns=\"somenamespace\">" +
" <address>1313 Mockingbird Lane</address>" +
" <residents>5</residents>" +
"</ns:obj>";
var serializer = new XmlSerializer(typeof(xmlObject));
using (var reader = new StringReader(xml))
{
var result = serializer.Deserialize(reader) as xmlObject;
Console.WriteLine("{0} people live at {1}", result.residents, result.address);
// Output: "5 people live at 1313 Mockingbird lane"
}
}
}
If I omit the XmlElementAttribute on the individual members, I instead get an empty object. I.e. The output reads
0 people live at
(result.address is equal to null.)
I understand the rationale behind why the deserialization process works like this, but I'm wondering if there is a less verbose way to tell XmlSerializer that the child elements of the object are not in the same namespace as the root node.
The objects I'm working with in production have dozens of members and, for cleanliness sake, I'd like to avoid tagging all of them with [XmlElement(Namespace = "")] if it's easily avoidable.
You can combine XmlRootAttribute with XmlTypeAttribute to make it so the root element, and the root element's elements, have different namespaces:
[XmlRoot(Namespace="somenamespace", ElementName="obj")]
[XmlType(Namespace="")]
public class xmlObject
{
public string address { get; set; }
public int residents { get; set; }
}
Using the type above, if I deserialize and re-serialize your XML I get:
<q1:obj xmlns:q1="somenamespace">
<address>1313 Mockingbird Lane</address>
<residents>5</residents>
</q1:obj>
Sample fiddle.
If you know the contract with the web service, why not use a DataContractSerializer to deserialize the XML into the objects?

Deserialize XML element from InnerText and Value attribute

I have XML Serializable class with property Name
[Serializable]
public class Item
{
[XmlElement("name")]
public string Name { get; set; }
}
and I want it to be able to deserialize XML file that I have in two ways:
<item>
<name>Name</name>
</item>
and
<item>
<name value="Name" />
</item>
The first works fine but what should I do to be able to deserialize the second also with the same class?
XML Serialization attributes work both with serialization and deserialization. If we'll assume that it might be possible to use attributes for deserializing instance of Item from two different xml structures, then how serialization should work - should it serialize instance name to element value, or to attribute? Or to both? That's why you cannot deserialize two different xml structures into single class. Use two different classes or deserialize it manually without usage of XML Serialization attributes.
I found another way to solve my problem using only one class maybe someone will find this useful
[Serializable]
public class Item
{
[XmlElement("name")]
public NameElement NameElement { get; set; }
}
public class NameElement
{
[XmlAttribute("value")]
public string Value { get; set; }
[XmlText]
public string Text { get; set; }
[XmlIgnore]
public string Name
{
get { return String.IsNullOrEmpty(this.Value) ? this.Text : this.Value; }
set { this.Value = value; }
}
}
Maybe it's not super elegant but it works in both cases and uses the same class.
Since you have mentioned that XML data is coming from external sources, so obviously you don't have control over that.
Therefore you can follow any of the option as below:
Create separate class per XML data structure, because as far I know there is no way to control XML Deserialization when using XmlSerializer
You can use XDocument to read the XML by self, to overcome this limitation.
If going by second idea, I have created small Console Application to demonstrate that.
Main piece of code is as below:
MemoryStream xmlStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlData));
XDocument doc = XDocument.Load(xmlStream);
var records = from record in doc.Descendants("item").Descendants()
select new Item(!record.IsEmpty ? record.Value : record.Attribute("value").Value);
Here I'm reading the element using LinqToXml and checking if the element is not empty, i.e. Value is not blank, then use Value otherwise read the value from element's Attribute.
Console application (Complete code):
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.Serialization;
namespace Console.TestApp
{
class Program
{
static string xmltypeFirst = #"<item>
<name>John</name>
</item>";
static string xmltypeSecond = #"<item>
<name value='Smith' />
</item>";
static void Main(string[] args)
{
var data = xmltypeFirst;
var result = Deserialize(data).ToList();
Console.WriteLine("Name: " + result[0].Name);
data = xmltypeSecond;
result = Deserialize(data).ToList();
Console.WriteLine("Name: " + result[0].Name);
Console.WriteLine("Press any to key to exit..");
Console.ReadLine();
}
private static IEnumerable<Item> Deserialize(string xmlData)
{
MemoryStream xmlStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlData));
XDocument doc = XDocument.Load(xmlStream);
var records = from record in doc.Descendants("item").Descendants()
select new Item(!record.IsEmpty ? record.Value : record.Attribute("value").Value);
return records;
}
}
[Serializable]
public class Item
{
public Item(string name)
{
this.Name = name;
}
[XmlElement("name")]
public string Name { get; set; }
}
}
Note: To run this you will need to add reference to System.Xml.Linq.dll in your project.
Reference: here

There is an error in XML document (2, 2).What does this mean?

I am trying to read XML document.
My XML:
<?xml version="1.0" encoding="utf-8"?>
<SplashScreen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Path>SplashScreen/Image-King</Path>
</SplashScreen>
My code which is reading XML:
XmlGameScreen = new XmlManager<GameScreen>();
XmlGameScreen.Type = currentscreen.Type;
currentscreen = XmlGameScreen.Load("LoadXML/SplashScreen.xml");
And
public Type Type;
public T Load(string path)
{
T instance;
using (TextReader textreader = new StreamReader(path))
{
XmlSerializer xml = new XmlSerializer(Type);
instance = (T)xml.Deserialize(textreader);
}
return instance;
}
I am getting error on instance = (T)xml.Deserialize(textreader); Is my XML document wrong? I am trying to read <Path>.
Update :
My Internal Exception:
Cannot serialize member 'MyRPGgame.SplashScreen._image' of type 'Microsoft.Xna.Framework.Graphics.Texture2D'
In my case it appears one of the Visual Studio 2017 version 15.5 updates caused this error when trying to open SSRS projects. The solution is to delete the *.rptproj.rsuser file from the project folder and try again.
My experience from it would be that in the 2nd line in the 2nd chararacter, there is an error.
have a look if your class names are different from the XML tags. are you maybe changing the "XML Root name" to a different one.
Have a look at the XML structure and which class are you serializing to which node.
Also, read the
MSDN Documentation about the XmlRootAttribute Class.
That usually means you have whitespace at the start of the file; check for a line-break before the <?xml.... Even better: please show the first few bytes (preferably as far as <SplashScreen) of the file as viewed in a binary editor.
It could also mean you have an invisible unicode or control character somewhere before the <SplashScreen
Just wanted to share what worked for me. I had a similar error
System.InvalidOperationException: There is an error in XML document (1, 40).
---> System.InvalidOperationException: <tsResponse xmlns='http://xxxyyyzzzz.com/api'> was not expected.
I was trying to deserialize a string to an object of type tsResponse.
After adding the following attribute [Serializable, XmlRoot(ElementName = "tsResponse", Namespace = "http://xxxyyyzzzz.com/api")] to the class tsResponse i was able to resolve my issue.
[Serializable, XmlRoot(ElementName = "tsResponse", Namespace = "http://xxxyyyzzzz.com/api")]
public class tsResponse
{
[XmlElement]
public CredentialsXml credentials { get; set; }
}
I.e had to add Namespace attribute (System.Xml.Serialization).
Try this: When you are deserializing XML to List just add an extra
line to the start i.e ArrayOfAddressDirectory as follows and don't put any space at start and end of file.
<?xml version="1.0"?>
<ArrayOfAddressDirectory xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<AddressDirectory>
<Owner>MS. Dhoni</Owner>
<Age>48</Age>
<Company>India</Company>
</AddressDirectory>
</ArrayOfAddressDirectory>
Here is the C# code:
namespace XmlReadProgram
{
public class AddressDirectory
{
public string Owner { get; set; }
public string Age { get; set; }
public string Company { get; set; }
}
public class Program
{
static void Main(string[] args)
{
List<AddressDirectory> adlist = new List<AddressDirectory>();
using (FileStream fileStream = File.OpenRead(#"E:\inputDirectory\address.xml"))
{
XmlSerializer serializer = new XmlSerializer(typeof(List<AddressDirectory>));
adlist = (List<AddressDirectory>)serializer.Deserialize(fileStream);
}
//You can use foreach to print all data
Console.WriteLine(adlist[0].Owner);
Console.WriteLine(adlist[0].Age);
Console.WriteLine(adlist[0].Company);
}
}
}
In my case, a property with [XmlArrayAttribute] had the getter accessing a field with [XmlIgnoreAttribute] that was left uninitialized.
The problem in your case it's definitely the confusion between Type and template T. You are trying to construct Serializer with Type --> new XmlSerializer(Type) and then deserialize with template T ----> (T)xml.Deserialize. So the solution is to replace Type in constructing with typeof(T), and this should eliminate the initial XML document problem of (2, 2).

Xml Deserialization Fails on Empty Element

I have an Xml document that looks similar too
<Reports xmlns="">
<Report>
<ReportID>1</ReportID>
<ParameterTemplate />
</Report>
</Reports>
It fails serializing to this object
[XmlType(TypeName = "Report")]
public class Report
{
[XmlElement("ReportID")]
public int ID { get; set; }
[XmlElement("ParameterTemplate")]
public XElement ParameterTemplate { get; set; }
}
It's failing because the empty ParameterTemplate element, because if it contains child elements it deserializes fine.
How can I get this to work?
This is my Deserialization Code
var serializer = new XmlSerializer(typeof(Report));
return (Report)serializer.Deserialize(source.CreateReader());
The exception is
The XmlReader must be on a node of type Element instead of a node of type EndElement.
How can I get this to deserialize with the existing xml?
Thanks
-c
It looks like the content of XElement - if not null - cannot be an empty XML element. In other words, you would not be able to serialize that XML in your example from an in-memory representation/instance of your Report class.
You can implement IXmlSerializable interface on your report class and overwrite ReadXml and WriteXml methods.
I created the following method to patch the XML text:
Public Function XMLReaderPatch(rawXML As String) As String
If String.IsNullOrEmpty(rawXML) Then Return rawXML
'Pattern for finding items similar to <name*/> where * may represent whitespace followed by text and/or whitespace
Dim pattern As String = "<(\S+)(\s[^<|>]*)?/>"
'Pattern for replacing with items similar to <name*></name> where * may represent whitespace followed by text and/or whitespace
Dim replacement As String = "<$1$2></$1>"
Dim rgx As New Text.RegularExpressions.Regex(pattern)
Return rgx.Replace(rawXML, replacement)
End Function
Use IsNullable=True
[XmlType(TypeName = "Report")]
public class Report
{
[XmlElement("ReportID")]
public int ID { get; set; }
[XmlElement("ParameterTemplate", IsNullable=true)]
public XElement ParameterTemplate { get; set; }
}

Categories

Resources