validating an xml and finding line and column of error - c#

i have an xml document of this kind
<xml>
<person name="a">
<age>21</age>
<salary>50000></salary>
</person>
<person name="b">
<age>25</age>
<salary>30000></salary>
</person>
<person name="c">
<age>30</age>
<salary>60000></salary>
</person>
<person name="d">
<age>35</age>
<salary>150000></salary>
</person>
</xml>
Now im trying to validate this document by passing this to a method like this
validate(string file)
{
// here i have some logic
// say i am trying to check if the salary is >50000 and age > 30
// if it doesn't satisfy the condition i have to return an error
}
I am able to achieve this . What i really want is to know where exactly the error in xml document is , like which line and column.
How should i do this ? any suggestions?

You are closing your 'age' tag like this
<age>35<age/>
It should be like
<age>35</age>
hope this works for you.

Firstly, you have not specified how you are parsing your XML in c#. It matters.
Now as for validity of your XML document, a valid xml document should have these:
XML documents must have a root element
XML elements must have a closing tag
XML tags are case sensitive
XML elements must be properly nested
-XML attribute values must be quoted
now try adding this to the top of your xml doc
<?xml version="1.0" encoding="ISO-8859-1"?>
Now, to parse XML document, you can use either XmlDocument class or Linq's XDocument class.
Lets take example of XmlDocument.
if you have a xml string, load Xml as below:
XmlDocument doc = new XmlDocument();
doc.LoadXml(stringXML);
foreach(XmlNode node in doc.SelectNodes("xml/person/salary"))
{
var strSalary = node.InnerText;
var intSalary = Convert.ToInt32(strSalary??0);
}
if you have a xml file, load Xml as below:
XmlDocument doc = new XmlDocument();
doc.Load(XMLFilePath);

Full example:
string xml = #"<xml>
<person name=""a"">
<age>21</age>
<salary>50000</salary>
</person>
<person name=""b"">
<age>25</age>
<salary>30000</salary>
</person>
<person name=""c"">
<age>30</age>
<salary>60000</salary>
</person>
<person name=""d"">
<age>35</age>
<salary>150000</salary>
</person>
</xml>";
using (var sr = new StringReader(xml))
{
var xml2 = XDocument.Load(sr, LoadOptions.SetLineInfo);
foreach (var person in xml2.Root.Elements())
{
//string name = (string)person.Attribute("name"); // Unused
int age = (int)person.Element("age");
int salary = (int)person.Element("salary");
// Your check
bool error = salary > 50000 && age > 30;
if (error)
{
// IMPORTANT PART HERE!!!
int lineNumber = -1;
int colNumber = -1;
var lineInfo = (IXmlLineInfo)person;
if (lineInfo.HasLineInfo())
{
lineNumber = lineInfo.LineNumber;
colNumber = lineInfo.LinePosition;
}
return string.Format("Error on line {0}, col {1}", lineNumber, colNumber);
// END IMPORTANT PART!!!
}
}
}
The "trick" is that XElement implement IXmlLineInfo (as explained here), but you have to load the document with LoadOptions.SetLineInfo.

Related

How do you remove blank lines from an XML document or node using C#?

I want all the blank lines to be removed in the XML below before writing it to document. It may help to know that I used the .DeleteSelf() method of the XPathNavigator class to get rid of the unwanted nodes before (and that only leaves empty lines).
<Person xmlns="http://someURI.com/something">
<FirstName>Name1</FirstName>
<MiddleName>Name2</MiddleName>
<LastName>Name3</LastName>
</Person>
I'd suggest to use XDocument class:
1. method:
string xcontent = #" strange xml content here ";
XDocument xdoc = XDocument.Parse(xcontent);
xdoc.Save("FullFileName.xml");
2. method:
XmlReader rdr = XmlReader.Create(new StringReader(xcontent));
XDocument xdoc = XDocument.Load(rdr);
xdoc.Save("FullFileName.xml");
returns:
<Person xmlns="http://someURI.com/something">
<FirstName>Name1</FirstName>
<MiddleName>Name2</MiddleName>
<LastName>Name3</LastName>
</Person>
Msdn documentation: https://msdn.microsoft.com/en-us/library/system.xml.linq.xdocument%28v=vs.110%29.aspx
Can also do line by line reading and writing.
string line = string.Empty;
using (StreamReader file_r = new System.IO.StreamReader("HasBlankLines.xml"))
{
using (StreamWriter file_w = new System.IO.StreamWriter("NoBlankLines.xml"))
{
while ((line = file_r.ReadLine()) != null)
{
if (line.Trim().Length > 0)
file_w.WriteLine(line);
}
}
}
Outputs:
<Person xmlns="http://someURI.com/something">
<FirstName>Name1</FirstName>
<MiddleName>Name2</MiddleName>
<LastName>Name3</LastName>
</Person>

How to store inner text of nodes with same names to a List?

Using C#, I need to store all the data of the inner text of Email nodes into a list, and all the data of the inner text of Related nodes into a list for each person separately. For now I was only able to store only first "email" node and first "related" node into lists. I get this:
,when I should get this
.
How to get the right answer? This is my xml file:
<? xml version="1.0" encoding="utf-8"?>
<People>
<Person>
<Name>Toni</Name>
<Email>a#g.c</Email>
<Email>b#g.c</Email>
<Email>c#g.c</Email>
<Related>Friend1</Related>
<Related>Friend2</Related>
</Osoba>
<Osoba>
<Name>Deni</Name>
<Email>d#g.c</Email>
<Email>e#g.c</Email>
<Email>f#g.c</Email>
<Related>Friend3</Related>
<Related>Friend4</Related>
</Osoba>
</People>
I am assuming the following xml Instead of the one you posted since it is wrong.
<?xml version="1.0" encoding="utf-8"?>
<People>
<Person>
<Name>Toni</Name>
<Email>a#g.c</Email>
<Email>b#g.c</Email>
<Email>c#g.c</Email>
<Related>Friend1</Related>
<Related>Friend2</Related>
</Person>
<Person>
<Name>Deni</Name>
<Email>d#g.c</Email>
<Email>e#g.c</Email>
<Email>f#g.c</Email>
<Related>Friend3</Related>
<Related>Friend4</Related>
</Person>
</People>
I am using LINQ to XML.(Another way is to use XmlDocument)
String path = "Path of your xml file";
XDocument doc = XDocument.Load(path);
var nodes = doc.Descendants("Person");
foreach (XElement node in nodes)
{
var name = node.Element("Name").Value;
var emails = node.Elements("Email").Select(x => x.Value);
}

C# XML Read All Child Nodes Of A Specific Node

So I have a program that reads all the name nodes in a XML file and adds these to a Combo Box. On a button click, it then takes this response and needs to get all the other data from the child nodes of the node the name is in.
The XML document:
<People>
<Person>
<Name>Greg</Name>
<Age>23</Age>
<Height>200</Height>
</Person>
<Person>
<Name>John</Name>
<Age>34</Age>
<Height>230</Height>
</Person>
</People>
What I've got so far:
XmlDocument Doc = new XmlDocument();
Doc.Load(FilePath);
foreach (XmlNode Node in Doc.SelectNodes("People/Person"))
{
comboBox1.Items.Add(Node.SelectSingleNode("Name").InnerText);
}
string RegPicked = comboBox1.SelectedItem.ToString();
foreach (XmlNode xNode in Doc.SelectNodes("People/Person"))
if (xNode.SelectSingleNode("Name").InnerText == RegPicked)
{
textBox1.Text = xNode.ParentNode.ChildNodes.ToString();
}
Doc.Save(FilePath);
When I run the code I just get "System.Xml.XmlChildNodes" in the text box.
I know I've done something wrong but I'm not sure what.
you have to distinguished child node:
textBox1.Text = xNode.ParentNode.ChildNodes.SelectSingleNode("age").InnerText;
Do this, you will get all the elements inside the parent of the node you are searching the value of.
string str = #"<People>
<Person>
<Name>Greg</Name>
<Age>23</Age>
<Height>200</Height>
</Person>
<Person>
<Name>John</Name>
<Age>34</Age>
<Height>230</Height>
</Person>
</People>";
XDocument xdoc = XDocument.Parse(str);
var xmlURL = (from el in xdoc.Descendants("Name")
where el.Value == "John"
select el.Parent).First().ToString();

xml error:System.Xml.XPath.XPathException: Expression must evaluate to a node-set

I am trying to find a node in my xml file but getting the error ( see title)?
// instantiate XmlDocument and load XML from file
XmlDocument doc = new XmlDocument();
doc.Load(#"C:\temp\test2.xml");
var node = doc.SelectSingleNode("/Offers/Offer/ID=[text()='1']");
var test = node;
xml
<?xml version="1.0" encoding="utf-8"?>
<Offers>
<Offer>
<Model>AAAA</Model>
<ID>1</ID>
<Name>First offer</Name>
</Offer>
<Offer>
<Model>BBBB</Model>
<ID>2</ID>
<Name>Second offer</Name>
</Offer>
</Offers>
Remove the = after ID:
var node = doc.SelectSingleNode("/Offers/Offer/ID=[text()='1']");
becomes:
var node = doc.SelectSingleNode("/Offers/Offer/ID[text()='1']");

Parsing Optional Tags in C#

I'm trying to find an easy and slick way to do the following requirement.
I have a XML message with this arrangement:
<persons>
<person>
<firstName>Mike</firstName>
<middleName>K.</middleName>
<lastName>Kelly</lastName>
</person>
<person>
<firstName>Steve</firstName>
<lastName>David</lastName>
</person>
<person>
<firstName>Laura</firstName>
<middleName>X.</middleName>
<lastName>Xavier</lastName>
</person>
</persons>
I want to parse this XML using xPath expressions.
persons/person/firstName
persons/person/middleName
persons/person/lastName
My objective is store firstName, middleName and lastName tag values like this into a list of string objects like this:
firstNameList[0] = "Mike";
firstNameList[1] = "Steve";
firstNameList[2] = "Laura";
middleNameList[0] = "K.";
middleNameList[1] = null;
middleNameList[2] = "X.";
lastNameList[0] = "Kelly";
lastNameList[1] = "David";
lastNameList[2] = "Xavier";
In my C# code, I do this:
XmlNodeList firstNameNodeList = xmlDoc.SelectNodes("persons/person/firstName", nsmgr);
XmlNodeList middleNameNodeList = xmlDoc.SelectNodes("persons/person/middleName", nsmgr);
XmlNodeList lastNameNodeList = xmlDoc.SelectNodes("persons/person/lastName", nsmgr);
The problem with this code is that for middle name, I don't have it for 2nd person in my XML list. So the middleNameNodeList returns 2 values (K. and X.) but I wouldn't know whether the 1st or 2nd or 3rd person's middle name is missing.
I was hoping that SelectNodes() API would provide an iteration or index ID as which repeating element has a given value.
Please suggest me an easiest way to achieve what I needed? Thanks so much for your help, JK
How about this?
foreach (Node person in xmlDoc.SelectNodes("persons/person", nsmgr))
{
firstNameNodeList.Add(person.SelectSingleNode("firstName", nsmgr));
middleNameNodeList.Add(person.SelectSingleNode("middleName", nsmgr));
lastNameNodeList.Add(person.SelectSingleNode("lastName", nsmgr));
}
Intead of getting a list of names, try getting a list of persons, then iterate the list and get their names.
You just have to iterate over persons/person and handle each individually - this would work:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(#"test.xml");
var persons = xmlDoc.SelectNodes("persons/person");
foreach (XmlNode person in persons)
{
string firstName = person.SelectSingleNode("firstName").InnerText;
string middleName = (person.SelectSingleNode("middleName") != null)
? person.SelectSingleNode("middleName").InnerText
: null;
string lastName = person.SelectSingleNode("lastName").InnerText;
}
Try
<persons>
<person>
<firstName>Mike</firstName>
<middleName>K.</middleName>
<lastName>Kelly</lastName>
</person>
<person>
<firstName>Steve</firstName>
<middleName />
<lastName>David</lastName>
</person>
<person>
<firstName>Laura</firstName>
<middleName>X.</middleName>
<lastName>Xavier</lastName>
</person>
</persons>
<person>
<firstName>Steve</firstName>
<middleName />
<lastName>David</lastName>
</person>
this should return "K","","X" for InnnerText

Categories