Get certain xml node and save the value - c#

Considering the following XML:
<Stations>
<Station>
<Code>HT</Code>
<Type>123</Type>
<Names>
<Short>H'bosch</Short>
<Middle>Den Bosch</Middle>
<Long>'s-Hertogenbosch</Long>
</Names>
<Country>NL</Country>
</Station>
</Stations>
There are multiple nodes. I need the value of each node.
I've got the XML from a webpage (http://webservices.ns.nl/ns-api-stations-v2)
Login (--) Pass (--)
Currently i take the XML as a string and parse it to a XDocument.
var xml = XDocument.Parse(xmlString);
foreach (var e in xml.Elements("Long"))
{
var stationName = e.ToString();
}

You can retrieve "Station" nodes using XPath, then get each subsequent child node using more XPath. This example isn't using Linq, which it looks like you possibly are trying to do from your question, but here it is:
XmlDocument xml = new XmlDocument();
xml.Load(xmlStream);
XmlNodeList stations = xml.SelectNodes("//Station");
foreach (XmlNode station in stations)
{
var code = station.SelectSingleNode("Code").InnerXml;
var type = station.SelectSingleNode("Type").InnerXml;
var longName = station.SelectSingleNode("Names/Long").InnerXml;
var blah = "you should get the point by now";
}
NOTE: If your xmlStream variable is a String, rather than a Stream, use xml.LoadXml(xmlStream); for line 2, instead of xml.Load(xmlStream). If this is the case, I would also encourage you to name your variable to be more accurately descriptive of the object you're working with (aka. xmlString).

This will give you all the values of "Long" for every Station element.
var xml = XDocument.Parse(xmlStream);
var longStationNames = xml.Elements("Long").Select(e => e.Value);

Related

XML - where node has same name but different values

I am trying to read an xml file (and later import the data in to a sql data base) which contains employees names address' etc.
The issue I am having is that in the xml the information for the address for the employee the node names are all the same.
<Employee>
<EmployeeDetails>
<Name>
<Ttl>Mr</Ttl>
<Fore>Baxter</Fore>
<Fore>Loki</Fore>
<Sur>Kelly</Sur>
</Name>
<Address>
<Line>Woof Road</Line>
<Line>Woof Lane</Line>
<Line>Woofington</Line>
<Line>London</Line>
</Address>
<BirthDate>1985-09-08</BirthDate>
<Gender>M</Gender>
<PassportNumber>123756rt</PassportNumber>
</EmployeeDetails>
</Employee>
I all other items are fine to extract and I have tried to use Linq to iterate through each "Line" node but it always just gives be the first Line and not the others.
var xAddreesLines = xEmployeeDetails.Descendants("Address").Select(x => new
{
address = (string)x.Element("Line").Value
});
foreach (var item in xAddreesLines)
{
Console.WriteLine(item.address);
}
I need to able to when I'm importing to my sql db that address line is separate variable
eg
var addressline1 = first <line> node
var addressline2 = second <line> node etc etc.
Any advice would be most welcome.
This should give you the expected output:-
var xAddreesLines = xdoc.Descendants("Address")
.Elements("Line")
.Select(x => new { address = (string)x });
You need to simply fetch the Line elements present inside Address node and you can project them. Also note there is no need to call the Value property on node when you use explicit conversion.
You can do it like this:
using System.Xml;
.
.
.
XmlDocument doc = new XmlDocument();
doc.Load("source.xml");
// if you have the xml in a string use doc.LoadXml(stringvar)
XmlNamespaceManager nsmngr = new XmlNamespaceManager(doc.NameTable);
XmlNodeList results = doc.DocumentElement.SelectNodes("child::Employee", nsmngr);
foreach (XmlNode result in results)
{
XmlNode namenode = result.SelectSingleNode("Address");
XmlNodeList types = result.SelectNodes("line");
foreach (XmlNode type in types)
{
Console.WriteLine(type.InnerText);
}
XmlNode fmtaddress = result.SelectSingleNode("formatted_address");
}
Refer to this question for the original source.

Create loops and parsing XML nodes value for Selenium tests

I am trying to write a test function in C# that read data from an XML file and parse into Selenium testing methods , the XML code is like:
<home>
<ask_frame>
<button>
<id>Object ID<id>
<xpath>Object XPath<xpath>
<textbox>
<id>Object ID<id>
<xpath>Object XPath<xpath>
</ask_frame>
<search_frame>
<id>Object ID<id>
<xpath>Object XPath<xpath>
</search_frame>
<home>
I am trying to create a loop that read the id and xpath value from these nodes and parse them into an method for searching a webpage element by id and xpath. My initial attempt was:
Code updated
public void CheckIdTest()
{
driver.Navigate().GoToUrl(baseURL + "FlightSearch");
XmlDocument xd = new XmlDocument();
xd.Load(#"C:\XMLFile1.xml");
XmlNodeList mainlist = xd.SelectNodes("//home/*");
XmlNode mainroot = mainlist[0];
foreach (XmlNode xnode in mainroot)
{
string objID = xnode.SelectSingleNode("id").InnerText;
string objXPath = xnode.SelectSingleNode("XPath").InnerText;
objID = objID.Trim();
objXPath = objXPath.Trim();
String checkValue = "ObjID value is: " + objID + Environment.NewLine+ "ObjXPath value is: " + objXPath;
System.IO.File.WriteAllText(#"C:\checkvalue.txt", checkValue);
objectCheck(objXPath, objID);
}
}
I have put a String and checked that correct values for ObjID and ObjXPath have been achieved, but this loop also went only twice (checked 2 nodes in first branch). How could I make it runs through every node in my XML?
Any suggestions and explanations to the code will be highly appreciated.
Basically these two lines are using incorrect XPath :
XmlNodeList idlist = xd.SelectNodes("id");
XmlNodeList xpathlist = xd.SelectNodes("XPath");
<id> and <xpath> nodes aren't located directly at the root level, so you can't access it just like above. Besides, xpath is case-sensitive so you should've used "xpath" instead of "XPath". Try to fix it like this :
XmlNodeList idlist = xd.SelectNodes("//id");
XmlNodeList xpathlist = xd.SelectNodes("//xpath");
or more verbose :
XmlNodeList idlist = xd.SelectNodes("home/*/id");
XmlNodeList xpathlist = xd.SelectNodes("home/*/xpath");
UPDATE :
Responding to your comment about looping problem, I think you want to change it like this :
foreach (XmlNode xnode in mainroot.ChildNodes)
{
string objID = xnode.SelectSingleNode("id").InnerText;
string objXPath = pathroot.SelectSingleNode("xpath").InnerText;
objectCheck(objID, objXPath);
}
You are getting this error because you are trying to use an object that is null i.e not instantiated.
Put in a breakpoint at the line
XmlDocument xd = new XmlDocument();
and step through line by line till you find where the nothing.null reference is.
It should not take long to find out what the problem is.

Keeping Redundant Namespace Prefixes On XML Elements in C#

I'm trying to write an XML file that will be picked up and parsed by another service. In order for this to happen the XML must be formatted in a very specific way, namely:
<?xml version="1.0"?>
<Feedbacks:Feedbacks xmlns:Feedbacks="Feedbacks">
<Feedbacks:Elements>
<Feedback:XMLFeedback xmlns:Feedback="Feedback">
<Feedback:MfgUnitID></Feedback:MfgUnitID>
<Feedback:MachineId></Feedback:MachineId>
<Feedback:OperationCode></Feedback:OperationCode>
<Feedback:ItemSeqNum></Feedback:ItemSeqNum>
<Feedback:OperDispositionCd></Feedback:OperDispositionCd>
<Feedback:ItemId></Feedback:ItemId>
<Feedback:ParentItemId></Feedback:ParentItemId>
<Feedback:ItemEndSize>1821</Feedback:ItemEndSize>
<Feedback:ItemDispositionCd></Feedback:ItemDispositionCd>
<Feedback:OperStartDate></Feedback:OperStartDate>
<Feedback:OperEndDate></Feedback:OperEndDate>
</Feedback:XMLFeedback>
</Feedbacks:Elements>
</Feedbacks:Feedbacks>
with data of course between the innermost elements. Here's the issue though, no matter what I do, I can't get any of the C# classes to keep the semicolons on the innermost nodes. As far as I know these need to stay, so is there a way in C# to force it to format the nodes this way? I've tried all of the create methods that I could find in the XMLDocument class. I can get the outer nodes formatted fine, but the inner ones just keep creating problems.
Edit, sorry here's the code that makes the inner nodes.
private void AppendFile(string filename, string[] headers, Dictionary<string, string> values)
{
XmlDocument doc = new XmlDocument();
doc.Load(filename);
XmlNode node = doc.GetElementsByTagName(headers[headers.Length - 2]).Item(0);
string[] hPieces = headers[headers.Length - 1].Split(':');
XmlElement appendee = doc.CreateElement(hPieces[0].Trim(), hPieces[1].Trim(), hPieces[0].Trim());
node.AppendChild(appendee);
foreach (KeyValuePair<string, string> pair in values)
{
string[] ePieces = pair.Key.Split(':');
//XmlElement element = doc.CreateElement(ePieces[0].Trim(), string.Empty, ePieces[1].Trim());
//XmlText text = doc.CreateTextNode(pair.Value);
XmlNode innerNode = doc.CreateNode(XmlNodeType.Element, ePieces[1].Trim(), ePieces[0].Trim());
node.InnerText = pair.Value;
// element.AppendChild(text);
appendee.AppendChild(innerNode);
}
doc.Save(filename);
}
The data for the inner nodes comes in as key value pairs in the dictionary. Where the keys contain the intended name.
Edit2: This is what the file output looks like
<?xml version="1.0" encoding="utf-8"?>
<Feedbacks:Feedbacks xmlns:Feedbacks="Feedbacks">
<Feedbacks:Elements>
<Feedback:XMLFeedback xmlns:Feedback="Feedback">
<MfgUnitID></MfgUnitID>
<MachineId></MachineId>
<OperationCode</OperationCode>
<ItemSeqNum></ItemSeqNum>
<OperDispositionCd></OperDispositionCd>
<ItemId></ItemId>
<ParentItemId></ParentItemId>
<ItemEndSize></ItemEndSize>
<ItemDispositionCd></ItemDispositionCd>
<OperStartDate></OperStartDate>
<OperEndDate></OperEndDate>
</Feedback:XMLFeedback>
</Feedbacks:Elements>
</Feedbacks:Feedbacks>
You can accompish this easily with XLinq:
using System.Xml.Linq;
XNamespace ns1 = "Feedbacks";
XNamespace ns2 = "Feedback";
var doc = new XElement("Feedbacks",
new XAttribute(XNamespace.Xmlns+"Feedbacks", ns1));
doc.Add(new XElement(ns1 + "Elements",
new XElement(ns2 + "Feedback",
new XAttribute(XNamespace.Xmlns+"Feedback", ns2),
new XElement(ns2 + "Unit"))));
Gives
<Feedbacks xmlns:Feedbacks="Feedbacks">
<Feedbacks:Elements>
<Feedback:Feedback xmlns:Feedback="Feedback">
<Feedback:Unit />
</Feedback:Feedback>
</Feedbacks:Elements>
</Feedbacks>
Although I believe that your own output should be valid XML, relying on the parent namespcae.

NodeList.SelectSingleNode() syntax

Having problems getting NodeList.SelectSingleNode() to work properly.
My XML looks like this:
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<inm:Results xmlns:inm="http://www.namespace.com/1.0">
<inm:Recordset setCount="18254">
<inm:Record setEntry="0">
<!-- snip -->
<inm:Image>fileName.jpg</inm:Image>
</inm:Record>
</inm:Recordset>
</inm:Results>
The data is a long series of <inm:Record> entries.
I open the doc and get create a NodeList object based on "inm:Record". This works great.
XmlDocument xdoc = new XmlDocument();
xdoc.Load(openFileDialog1.FileName);
XmlNodeList xRecord = xdoc.GetElementsByTagName("inm:Record");
I start looping through the NodeList using a for loop. Before I process a given entry, I want to check and see if the <inm:Image> is set. I thought it would be super easy just to do
string strImage = xRecord[i].SelectSingleNode("inm:Image").InnerText;
My thinking being, "For the XRecord that I'm on, go find the <inm:Image> value ...But this doesn't work as I get the exception saying that I need a XmlNameSpaceManager. So, I tried to set that up but could never get the syntax right.
Can someone show me how to use the correct XmlNameSpaceManager syntax in this case.
I've worked around the issue for now by looping through all of the childNodes for a given xRecord, and checking the tag once I loop around to it. I would like to check that value first to see if I need to loop over that <inm:Record> entry at all.
No need to loop through all the Record elements, just use XPath to specify the subset that you want:
XmlDocument xdoc = new XmlDocument();
xdoc.Load(openFileDialog1.FileName);
XmlNamespaceManager manager = new XmlNamespaceManager(xdoc.NameTable);
manager.AddNamespace("inm", "http://www.inmagic.com/webpublisher/query");
XmlNodeList nodes = xdoc.SelectNodes("/inm:Results/inm:Recordset/inm:Record[inm:Image != '']", manager);
Using the LINQ to XML libraries, here's an example for retrieving that said node's value:
XDocument doc = XDocument.Load(openFileDialog1.FileName);
List<XElement> docElements = doc.Elements().ToList();
XElement results = docElements.Elements().Where(
ele => ele.Name.LocalName == "Results").First();
XElement firstRecord = results.Elements().Where(
ele => ele.Name.LocalName == "Record").First();
XElement recordImage = firstRecord .Elements().Where(
ele => ele.Name.LocalName == "Image").First();
string imageName = recordImage.Value;
Also, by the way, using Hungarian notation for a type-checked language is overkill. You don't need to prepend string variables with str when it will always be a string.
XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable);
string strImage = xRecord[i].SelectSingleNode("inm:Image",nsMgr).InnerText;
Should do it.
Using this Xml library, you can get all the records that have an Image child element with this:
XElement root = XElement.Load(openFileDialog1.FileName);
XElement[] records = root.XPath("//Record[Image]").ToArray();
If you want to be sure that the Image child contains a value, it can be expressed like this:
XElement[] records = root.XPath("//Record[Image != '']").ToArray();

XPathSelectElements returns null

Load function is already defined in xmlData class
public class XmlData
{
public void Load(XElement xDoc)
{
var id = xDoc.XPathSelectElements("//ID");
var listIds = xDoc.XPathSelectElements("/Lists//List/ListIDS/ListIDS");
}
}
I'm just calling the Load function from my end.
XmlData aXmlData = new XmlData();
string input, stringXML = "";
TextReader aTextReader = new StreamReader("D:\\test.xml");
while ((input = aTextReader.ReadLine()) != null)
{
stringXML += input;
}
XElement Content = XElement.Parse(stringXML);
aXmlData.Load(Content);
in load function,im getting both id and and listIds as null.
My test.xml contains
<SEARCH>
<ID>11242</ID>
<Lists>
<List CURRENT="true" AGGREGATEDCHANGED="false">
<ListIDS>
<ListID>100567</ListID>
<ListID>100564</ListID>
<ListID>100025</ListID>
<ListID>2</ListID>
<ListID>1</ListID>
</ListIDS>
</List>
</Lists>
</SEARCH>
EDIT: Your sample XML doesn't have an id element in the namespace with the nss alias. It would be <nss:id> in that case, or there'd be a default namespace set up. I've assumed for this answer that in reality the element you're looking for is in the namespace.
Your query is trying to find an element called id at the root level. To find all id elements, you need:
var tempId = xDoc.XPathSelectElements("//nss:id", ns);
... although personally I'd use:
XDocument doc = XDocument.Parse(...);
XNamespace nss = "http://schemas.microsoft.com/SQLServer/reporting/reportdesigner";
// Or use FirstOrDefault(), or whatever...
XElement idElement = doc.Descendants(nss + "id").Single();
(I prefer using the query methods on LINQ to XML types instead of XPath... I find it easier to avoid silly syntax errors etc.)
Your sample code is also unclear as you're using xDoc which hasn't been declared... it helps to write complete examples, ideally including everything required to compile and run as a console app.
I am looking at the question 3 hours after it was submitted and 41 minutes after it was (last) edited.
There are no namespaces defined in the provided XML document.
var listIds = xDoc.XPathSelectElements("/Lists//List/ListIDS/ListIDS");
This XPath expression obviously doesn't select any node from the provided XML document, because the XML document doesn't have a top element named Lists (the name of the actual top element is SEARCH)
var id = xDoc.XPathSelectElements("//ID");
in load function,im getting both id and and listIds as null.
This statement is false, because //ID selects the only element named ID in the provided XML document, thus the value of the C# variable id is non-null. Probably you didn't test thoroughly after editing the XML document.
Most probably the original ID element belonged to some namespace. But now it is in "no namespace" and the XPath expression above does select it.
string xmldocument = "<response xmlns:nss=\"http://schemas.microsoft.com/SQLServer/reporting/reportdesigner\"><action>test</action><id>1</id></response>";
XElement Content = XElement.Parse(xmldocument);
XPathNavigator navigator = Content.CreateNavigator();
XmlNamespaceManager ns = new XmlNamespaceManager(navigator.NameTable);
ns.AddNamespace("nss", "http://schemas.microsoft.com/SQLServer/reporting/reportdesigner");
var tempId = navigator.SelectSingleNode("/id");
The reason for the null value or system returned value is due to the following
var id = xDoc.XPathSelectElements("//ID");
XpathSElectElements is System.xml.linq.XElment which is linq queried date. It cannot be directly outputed as such.
To Get individual first match element
use XPathSelectElement("//ID");
You can check the number of occurrences using XPathSelectElements as
var count=xDoc.XPathSelectElements("//ID").count();
you can also query the linq statement as order by using specific conditions
Inorder to get node value from a list u can use this
foreach (XmlNode xNode in xDoc.SelectNodes("//ListIDS/ListID"))
{
Console.WriteLine(xNode.InnerText);
}
For Second list you havnt got the value since, the XPath for list items is not correct

Categories

Resources