Using dictionary notation to get xml values - c#

I've got XML, and would like to get the value of a node using array/data table like notation
<Response>
<Outcome>
<KeyValueOfstringOutcomepQnxSKQu>
<Key>Icon</Key>
<Value>
<DataType>System.String</DataType>
<Field>Icon</Field>
<Value>O</Value>
</Value>
</KeyValueOfstringOutcomepQnxSKQu>
<KeyValueOfstringOutcomepQnxSKQu>
<Key>IconDescription</Key>
<Value>
<DataType>System.String</DataType>
<Field>IconDescription</Field>
<Value>Old</Value>
</Value>
</KeyValueOfstringOutcomepQnxSKQu>
<KeyValueOfstringOutcomepQnxSKQu>
<Key>IconLongDescription</Key>
<Value>
<DataType>System.String</DataType>
<Field>IconLongDescription</Field>
<Value>Older</Value>
</Value>
</KeyValueOfstringOutcomepQnxSKQu>
</Outcome>
</Response>
I want to do something like outcome.Key[IconDescription].Value to give me a value of "Old"

Linq provides access to XML through the XDocumentclass which allows you to run XPath against XML. Whilst it is not exactly in the style of your ideal XPath does provide a handy query language for accessing XML nodes, and your tag suggests you might be interested in an XPath solution!
Here is an example of using an XDocument to query an XML file. For this example I've simply read a file from a stream.
using System.Xml.Linq;
using System.Xml.XPath;
...
static void Main(string[] args)
{
XDocument doc = XDocument.Load(new FileStream(#"C:\path_to\input.xml", FileMode.Open));
string desrcription = doc.XPathSelectElement("/Response/Outcome/KeyValueOfstringOutcomepQnxSKQu[Key='IconDescription']/Value/Value").Value;
Console.WriteLine(desrcription);
Console.ReadLine();
}

Related

How to parse GENERIC XML envelope and get all nodes values(inner text) separated (not concatenated)

I want to parse ANY SOAP response XML, not knowing ahead the structure, usually if I have a string xml with response, strXML, and I would do this:
XmlDocument doc = new XmlDocument();
doc.LoadXml(strXML);
String str = doc.InnerText;
return str;
I would get the values of all nodes, the text, but concatenated.
I want to parse SOAP responses in a generic way.
For example if I have this response envelope coming:
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:hy="http://www.herongyang.com/Service/">
<soapenv:Header/>
<soapenv:Body>
<hy:GetExchangeRateResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<ratePart xsi:type="xsd:decimal">123.14</ratePart>
</hy:GetExchangeRateResponse>
</soapenv:Body>
</soapenv:Envelope>
The c# code above would return me for this envelope the good value, 123.14, but if there would be an extra child node to hy:GetExchangeRateResponse let say :
123.14
and
234.14
then I would get: 123.14234.14 concatenated, I want to have something like 123.14, 234.14 ...
PS: usually the services I worked with were returning one value so yeah although is a simple way was working, but not when there are multiple nodes/text.
You can fetch all text nodes from the document and then process each node separately.
For example:
var texts = doc.SelectNodes("//text()").Cast<XmlNode>().Select(x => x.Value).ToArray();
will fetch all text node values into texts (array of string).
using System;
using System.Linq;
using System.Xml.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
string xxx = "the xml response with multiple final nodes";
XDocument doc = XDocument.Parse(xxx);
List<string> texts = (from node in doc.DescendantNodes().OfType<XText>() select node.Value).ToList();
string asLongString = string.Join(",", texts);
Console.WriteLine("Values {0}", asLongString);
}
}
this guy gave me the answer first sorry :) https://csharpforums.net/members/johnh.4/

Search for strings between same named XML tags in C#

I get a XML Code out of an HTML Page in my WinForm in C#.
Example XML:
<param>
<value><array><data>
<value><string>Number1</string></value>
<value><string>11194652</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Error</string></value>
<value><string>200</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Number2</string></value>
<value><string>2155847</string></value>
</data></array></value>
</param>
<param>
<value><array><data>
<value><string>Print</string></value>
<value><string>no</string></value>
</data></array></value>
</param>
Now i need the data for Number1 (11194652), Number2 (2155847) and Error (200). Sadly i cannot search for tags, because they are all named the same. What is the best way to separate the data 11194652, 200 and 2155847 out of this XML?
Thanks in advance
You could convert the required information to a Dictionary as following.
var xDoc = XDocument.Parse($"<root>{xml}</root>");
var dataValues = xDoc.Descendants("param").Select(x=>x.Descendants("string").Select((c=>c.Value)))
.ToDictionary(x=> x.First(),y=>y.Last());
This would produce an output as the following.
With the Dictionary in place, you could search for the required Values using Keys
dataValues["Number1"]
dataValues["Number2"]
dataValues["Error"]
using XDocument
var xdoc = XDocument.Load(xmlFile);
var results = xdoc.Descendants("string")
.Select(x => x.Value);
Provided that you add a root node to your XML data, you could located the "Number1", "Error" or "Number2" node using an XPath and then select the following sibling node.
Try the following method:
private static string GetValue(string key)
{
XDocument xDocument = XDocument.Parse(yourXmlString);
var elem = xDocument.XPathSelectElement("/root/param/value/array/data/value/string[text() = 'Error']/../following-sibling::value/string");
return elem.Value;
}
GetValue("Number1") should return "11194652" and GetValue("Error") should return "200".

Reading a XML File using XPath Expression in C#

I am currently having a problem with reading a XML file using XPath expression. I have used the XmlDocument class. When I try reading a particular node from the XML, I get an empty list. The node which I am trying to read is the ID below ProductionRequest.
Here is the XML file which I tried to read:
<?xml version="1.0" encoding="iso-8859-1"?>
<ProductionSchedule xmlns="http://www.wbf.org/xml/b2mml-v02">
<ID>00000020000000</ID>
<Location>
<EquipmentID>8283</EquipmentID>
<EquipmentElementLevel>Site</EquipmentElementLevel>
<Location>
<EquipmentID>0</EquipmentID>
<EquipmentElementLevel>Area</EquipmentElementLevel>
</Location>
</Location>
<ProductionRequest>
<ID>0009300000000</ID>
<ProductProductionRuleID>W001</ProductProductionRuleID>
<StartTime>2017-04-20T23:57:20</StartTime>
<EndTime>2017-04-20T24:00:00</EndTime>
</ProductionRequest>
</ProductionSchedule>
This is the code which I used to read the above XML
using System;
using System.Xml.Linq;
using System.Xml;
using System.Xml.XPath;
namespace XML
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
string fullName = "F:\\Programming\\XML\\Example XML.xml";
XmlDocument xreader = new XmlDocument();
xreader.Load(fullName);
XmlNode root = xreader.DocumentElement;
XmlNodeList xnList1 =
xreader.SelectNodes("/ProductionSchedule/ProductionRequest/ID");
}
}
}
I could not find the cause for this problem. Could anyone help me in this regard. Looking forward for valuable inputs.
Your xml contains namespace http://www.wbf.org/xml/b2mml-v02 at root level node <ProductionSchedule>
And you are using the XPath expression /ProductionSchedule/ProductionRequest/ID but this XPath expression is not suitable for this xml document and that's why you can't get any desired value.
You need to use the below XPath expression to get the id's of all <ProductionRequest> node.
XmlNodeList xnList1 = xreader.SelectNodes("//*[name()='ProductionSchedule']/*[name()='ProductionRequest']/*[name()='ID']");
OR you can add namespace manually like
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xreader.NameTable);
nsmgr.AddNamespace("x", "http://www.wbf.org/xml/b2mml-v02");
XmlNodeList xnList1 = xreader.SelectNodes("//x:ProductionSchedule/x:ProductionRequest/x:ID", nsmgr);
And finally, you can read id from any of the parent nodes in variable xnList1 like
foreach (XmlNode id in xnList1)
{
Console.WriteLine(id.InnerText);
}
Output:

Make my XML to string output look presentable

I'm working with a 3rd party application that takes input in via XML, and then returns the input back out in XML, I'm looking for a way to format the information to display it nicely in a richtextbox.
<Solution>
<ID>1</ID>
<Property>
<Name>DriverSheave</Name>
<Value>1VP34</Value>
</Property>
<Property>
<Name>DriverBushing</Name>
<Value>
</Value>
</Property>
<Property>
<Name>DrivenSheave</Name>
<Value>AK49</Value>
this is some sample xml that i would receive as an output from the 3rd party app, What I'm currently doing is this.
richTextBox1.Text = Configurator.Results.InnerText.ToString();
which gives me results like this.
1DriverSheave3MVP55B69DriverBushingDrivenSheave3MVB200RDrivenBushingR1BeltB225BeltQty3
essentially id like to know the best way to move these around, and make the output formatted nicely. so im not asking that you format this for me, but rather let me know the proper way to go about formatting this.
id like it to look similar to
Refer to the below code:
using System.XML.Linq;
class XMLParseProgram
{
public DataTable ReadXML(string strXML)
{
XDocument xdoc = XDocument.Load(strXML);
var property= from props in xdoc.Element("Solution").Elements("Property").ToList().ToList();
if (property!= null)
{
DataTable dtItem = new DataTable();
dtItem.Columns.Add("Name");
dtItem.Columns.Add("Value");
foreach (var itemDetail in property.ElementAt(0))
{
dtItem.Rows.Add();
if (itemDetail.Descendants("Name").Any())
dtItem.Rows[count]["Name"] = itemDetail.Element("Name").Value.ToString();
if (itemDetail.Descendants("Value").Any())
dtItem.Rows[count]["Value"] = itemDetail.Element("Value").Value.ToString();
}
}
}
}
You can use the DataTable to generate the string in your program the way you want.
If one of Configurator or Configurator.Results is an XmlDocument or XmlElement you can use one of these converters to an XElement:
richTextBox1.Text = Configurator.Results.ToXElement().ToString();

Retrieving specific data from XML file

Using LINQ to XML.
I have an XML file which looks like this:
<?xml version="1.0" encoding="utf-8"?>
<TileMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Title>title</Title>
<Abstract>Some clever text about this.</Abstract>
<SRS>OSGEO:41001</SRS>
<Profile>global-mercator or something</Profile>
</TileMap>
I can retrieve the <Title> from this with no problems by using this little piece of code:
string xmlString = AppDomain.CurrentDomain.BaseDirectory + #"Capabilities\" + name + ".xml";
string xmlText = File.ReadAllText(xmlString);
byte[] buffer = Encoding.UTF8.GetBytes(xmlText);
XElement element = XElement.Load(xmlString);
IEnumerable<XElement> title =
from el in element.Elements("Title")
select el;
foreach (XElement el in title)
{
var elementValue = el.Value;
}
However, this isn't very flexible because say I have an XML file that looks like this:
<?xml version="1.0" encoding="utf-8"?>
<RootObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Services>
<TileMapService>
<Title>title</Title>
<href>http://localhost/root</href>
</TileMapService>
</Services>
</RootObject>
It can't find <Title> but it finds <Services> (I presume) but since it's not called "Title" it just ignores it. I'm not very strong in working with XML. How would I go about making a method that looks through the XML and fetches me "Title" or however you'd implement this?
You're currently just looking at the child elements of the root element.
Instead, if you want to find all descendants, use Descendants.
Additionally, there's no point in using a query expression of from x in y select x (or rather, there's a very limited point in some cases, but not here). So just use:
var titles = element.Descendants("Title");
Personally I would actually use XDocument here rather than XElement - you have after all got a whole document, complete with XML declaration, not just an element.
Change your LINQ query to:
IEnumerable<XElement> title =
from el in element.Descendants("Title")
select el;
Elements returns only the immediate children, Descendants returns all descendant nodes instead.
Descendants will select all the "Title" elements irrespective of the level. Please use xpath to correctly locate the element
using System;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;
using System.IO;
public class Program
{
public static void Main()
{
string xmlFile = AppDomain.CurrentDomain.BaseDirectory + #"Capabilities\" + name + ".xml";
XElement xml=XElement.Load(xmlFile);
IEnumerable<XElement> titleElements = xml.XPathSelectElements("//Services/TileMapService/Title");
}
}

Categories