How to get XML Elements from XmlDocument object? - c#

Assume that an XmlDocument is successfully loaded with this code:
var doc = new XmlDocument();
doc.Load(stream);
This is a sample portion of the XML stream (the complete XML stream has about 10000 of ProductTable's):
<ProductTable>
<ProductName>Chair</ProductName>
<Price>29.5</Price>
</ProductTable>
Using Linq, how do I access the ProductName and Price elements? Thanks.

I suggest using an XDocument instead of an XmlDocument (the latter is not suited for LINQ to XML). Use the XDocument.Load(...) method to load your "real" XML.
string xml = #"<ProductTable>
<ProductName>Chair</ProductName>
<Price>29.5</Price>
</ProductTable>";
XDocument x = XDocument.Parse(xml);
var tables = x.Descendants("ProductTable");
Dictionary<string,string> products = new Dictionary<string, string>();
foreach (var productTable in tables)
{
string name = productTable.Element("ProductName").Value;
string price = productTable.Element("Price").Value;
products.Add(name, price);
}
If you'd prefer to use sugar-coated SQL like syntax or would like to read up on the topic, this MSDN article is a great place to start.
The following is a more concise version if you feel like using an anonymous type:
XDocument document = XDocument.Parse(xml)
var products = /* products is an IEnumerable<AnonymousType> */
from item in document.Descendants("ProductTable")
select new
{
Name = item.Element("ProductName").Value,
Price = item.Element("Price").Value
};
You could then use this expressive syntax to print the matches in the console:
foreach (var product in products) /* var because product is an anonymous type */
{
Console.WriteLine("{0}: {1}", product.Name, product.Price);
}
Console.ReadLine();

Related

how can i get node Element value From LINQ?

i have a collection of service users, i want to iterate over ServiceUsers and extract value from ServiceUser, (ID, USER_NAME, UN_ID, IP, NAME)
<ServiceUsers xmlns="">
<ServiceUser>
<ID>280334</ID>
<USER_NAME>YVELAMGAMOIYENET12:206322102</USER_NAME>
<UN_ID>731937</UN_ID>
<IP>91.151.136.178</IP>
<NAME>?????????????????????: 123456</NAME>
</ServiceUser>
<ServiceUser>
<ID>266070</ID>
<USER_NAME>ACHIBALANCE:206322102</USER_NAME>
<UN_ID>731937</UN_ID>
<IP>185.139.56.37</IP>
<NAME>123456</NAME>
</ServiceUser>
</ServiceUsers>
my Code looks like this, but i am getting null point exception.
XDocument doc = XDocument.Parse(xml)
List<XElement> xElementList = doc.Element("ServiceUsers").Descendants().ToList();
foreach (XElement element in xElementList)
{
string TEST= element.Element("Name").Value;
comboBoxServiceUser.Items.Add(element.Element("Name").Value);
}
I used the example from XmlSerializer.Deserialize Method as the base for the following snippet that reads the provided xml.
var serializer = new XmlSerializer(typeof(ServiceUsers));
ServiceUsers i;
using (TextReader reader = new StringReader(xml))
{
i = (ServiceUsers)serializer.Deserialize(reader);
}
[XmlRoot(ElementName = "ServiceUsers")]
public class ServiceUsers : List<ServiceUser>
{
}
public class ServiceUser
{
[XmlElement(ElementName = "ID")]
public string Id {get; set;}
}
I think the basis of the problem is your trailing 's' to put it short, You iterate ServiceUser not ServiceUsers
Anyway this runs through fine:
[Fact]
public void CheckIteratorTest()
{
var a = Assembly.GetExecutingAssembly();
string[] resourceNames = a.GetManifestResourceNames();
string nameOf = resourceNames.FirstOrDefault(x => x.Contains("SomeXml"));
Assert.NotNull(nameOf);
using var stream = a.GetManifestResourceStream(nameOf);
Assert.NotNull(stream);
var reader = new StreamReader(stream, Encoding.UTF8);
var serialized = reader.ReadToEnd();
var doc = XDocument.Parse(serialized);
var elemList = doc.Root.Elements("ServiceUser").ToList();
Assert.NotEqual(0, elemList.Count);
foreach(var serviceUser in elemList)
{
System.Diagnostics.Debug.WriteLine($"Name : {serviceUser.Name ?? "n.a."}");
}
}
As being said: XML is case-sensitive. Next issue is .Descendants() returns all the descendant nodes, nested ones, etc, 12 nodes in this case. So NullPointerException will happen even if you fix a "typo".
Here is your fixed code:
XDocument doc = XDocument.Parse(xml);
var xElementList = doc
.Element("ServiceUsers") // picking needed root node from document
.Elements("ServiceUser") // picking exact ServiceUser nodes
.Elements("NAME") // picking actual NAME nodes
.ToList();
foreach (XElement element in xElementList)
{
var TEST = element.Value;
Console.WriteLine(TEST); // do what you were doing instead of console
}
Use doc.Element("ServiceUsers").Elements() to get the<ServiceUser> elements. Then you can loop over the child values of those in a nested loop.
var doc = XDocument.Parse(xml);
foreach (XElement serviceUser in doc.Element("ServiceUsers").Elements()) {
foreach (XElement element in serviceUser.Elements()) {
Console.WriteLine($"{element.Name} = {element.Value}");
}
Console.WriteLine("---");
}
Prints:
ID = 280334
USER_NAME = YVELAMGAMOIYENET12:206322102
UN_ID = 731937
IP = 91.151.136.178
NAME = ?????????????????????: 123456
---
ID = 266070
USER_NAME = ACHIBALANCE:206322102
UN_ID = 731937
IP = 185.139.56.37
NAME = 123456
---
Note: Elements() gets the (immediate) child elements where as Descendants() returns all descendants. Using Elements() gives you a better control and allows you to get the properties grouped by user.
You can also get a specific property like this serviceUser.Element("USER_NAME").Value. Note that the tag names are case sensitive!

How do I get a specific value from xml in c#

My problem is a bit typical and I am very new to working with xml. Please view the following code:
XmlDocument xDoc = new XmlDocument();
xDoc.Load(myxml);
Response.Write("<p><strong>First Result</strong><br/>");
for (int nodeCount = 0; nodeCount < xDoc.ChildNodes.Count; nodeCount++)
{
Response.Write(xDoc.ChildNodes[nodeCount].Name + ":");
Response.Write(xDoc.ChildNodes[nodeCount].InnerXml + "<br/>");
}
Response.Write("</p>");
.. and the output I get in the aspx page is as follows:
First Result
xml:
Response:OK122.160.37.198ININDIAWEST BENGALKOLKATA70015522.569788.3697+05:30
I want the values 'WEST BENGAL' and 'KOLKATA' from it. I am not being able to read/write this in xml format so that I can grab the required nodes and their values. How to do that?
You can use XPath to search the XML. use your "xDoc" variable
XPathDocument doc = new XPathDocument(XmlReader(xDoc));
XPathNavigator nav = doc.CreateNavigator();
XPathExpression exprName = nav.Compile(xPathName);
XPathNodeIterator iteratorName = nav.Select(exprName)
You could use the Descendants() function on the XDocument itself, consider this example:
(Note - have made assumptions about how your data looks)
using System.Xml.Linq;
static void Main()
{
var xml = "<Data><ININDIA>WEST BENGAL</ININDIA><ININDIA>KOLKATA</ININDIA></Data>";
var xd = XDocument.Parse(xml);
//get all `XElement` objects with the name "ININDIA"
foreach (var e in xd.Root.Descendants()
.Where(e=>e.Name.LocalName=="ININDIA"))
{
Console.WriteLine(e.Value);
}
}
will produce
WEST BENGAL
KOLKATA

Retrieve child node value

here is the XML i am using
there are multiple instances of LineItemDetails i posted only one of it ...
<LineItemDetail>
<DetailNumber>1</DetailNumber>
<LineItemNumber>1</LineItemNumber>
<BatchSequenceNumber>1</BatchSequenceNumber>
<RecordSequenceWithinBatch>1</RecordSequenceWithinBatch>
<ChargeAmount Name="GrossBilled">365.380</ChargeAmount>
<Tax>
<TaxType>Tax</TaxType>
<TaxAmount Name="Billed">15.630</TaxAmount>
<TaxBreakdown>
<TaxCode>MN</TaxCode>
<TaxAmount Name="Billed">15.630</TaxAmount>
</TaxBreakdown>
</Tax>
<AddOnCharges>
<AddOnChargeName>ISCAllowed</AddOnChargeName>
<AddOnChargePercentage>-9</AddOnChargePercentage>
<AddOnChargeAmount>-32.880</AddOnChargeAmount>
</AddOnCharges>
<TotalNetAmount>348.130</TotalNetAmount>
<CouponDetails>
<TicketOrFIMIssuingAirline>160</TicketOrFIMIssuingAirline>
<TicketOrFIMCouponNumber>1</TicketOrFIMCouponNumber>
<TicketDocOrFIMNumber>2649488544</TicketDocOrFIMNumber>
<CheckDigit>6</CheckDigit>
<CurrAdjustmentIndicator>USD</CurrAdjustmentIndicator>
<ElectronicTicketIndicator>E</ElectronicTicketIndicator>
<AirlineFlightDesignator>MR</AirlineFlightDesignator>
<FlightNo>885</FlightNo>
<FlightDate>2013-04-03</FlightDate>
<FromAirportCode>ULN</FromAirportCode>
<ToAirportCode>HKG</ToAirportCode>
<SettlementAuthorizationCode>861FBKOPVEZZ4</SettlementAuthorizationCode>
<Attachment>
<AttachmentIndicatorOriginal>N</AttachmentIndicatorOriginal>
</Attachment>
</CouponDetails>
</LineItemDetail>
The above is the XML doc.
i need to retrieve the value of the tag <TaxCode>.
Till now i got this far
var tax = from d in doc.Root.Descendants("Tax") select d;
foreach (var p in tax)
{
taxcode= p.Element("TaxCOde").Value;
}
p.Element("TaxCOde") checks only direct children of p (<Tax> element), so it finds nothing.
Try that:
foreach (var p in tax)
{
taxcode= (string)p.Element("TaxBreakdown").Element("TaxCode");
}
I used (string)XElement conversion instead of XElement.Value property, because it will not throw NullReferenceException when element is not found.
Edit
Test code for your sample XML:
var doc = XDocument.Load("source.txt");
var lineItemDetails = doc.Descendants("LineItemDetail");
foreach (var lineItemDetail in lineItemDetails)
{
var tax = lineItemDetail.Element("Tax");
var taxCode = (string)tax.Element("TaxBreakdown").Element("TaxCode");
}
taxCode is MN.
XDocument DocumentObject = XDocument.Load(yourxml);
IEnumerable<XElement> Tax= from TaxInfo in DocumentObject.Descendants("Tax") select TaxInfo;
foreach (var t in Tax)
{
TaxType = (string)t.Element("TaxType");
}
Try this
foreach (var p in tax)
{
taxcode= p.Element("TaxBreakdown").Element("TaxCode").Value;
}
Why don't you try to use an XmlDocument? one you've loaded the xml in the document you could do this (I've declared the XmlDocument as doc):
foreach(XmlElement elem in doc.GetElementsByTagName("TaxCOde"))
taxcode = elem.InnerText;
I've written this using only my memory, so I'm sorry if I've made some grammar or syntax mistakes

A better way to handle XML updation

I have a DataGridView control where some values are popluted.
And also I have an xml file. The user can change the value in the Warning Column of DataGridView.And that needs to be saved in the xml file.
The below program just does the job
XDocument xdoc = XDocument.Load(filePath);
//match the record
foreach (var rule in xdoc.Descendants("Rule"))
{
foreach (var row in dgRulesMaster.Rows.Cast<DataGridViewRow>())
{
if (rule.Attribute("id").Value == row.Cells[0].Value.ToString())
{
rule.Attribute("action").Value = row.Cells[3].Value.ToString();
}
}
}
//save the record
xdoc.Save(filePath);
Matching the grid values with the XML document and for the matched values, updating the needed XML attribute.
Is there a better way to code this?
Thanks
You could do something like this:
var rules = dgRulesMaster.Rows.Cast<DataGridViewRow>()
.Select(x => new {
RuleId = x.Cells[0].Value.ToString(),
IsWarning = x.Cells[3].Value.ToString() });
var tuples = from n in xdoc.Descendants("Rule")
from r in rules
where n.Attribute("id").Value == r.RuleId
select new { Node = n, Rule = r };
foreach(var tuple in tuples)
tuple.Node.Attribute("action").Value = tuple.Rule.IsWarning;
This is basically the same, just a bit more LINQ-y. Whether or not this is "better" is debatable. One thing I removed is the conversion of IsWarning first to string, then to int and finally back to string. It now is converted to string once and left that way.
XPath allows you to target nodes in the xml with alot of power. Microsoft's example of using the XPathNavigator to modify an XML file is as follows:
XmlDocument document = new XmlDocument();
document.Load("contosoBooks.xml");
XPathNavigator navigator = document.CreateNavigator();
XmlNamespaceManager manager = new XmlNamespaceManager(navigator.NameTable);
manager.AddNamespace("bk", "http://www.contoso.com/books");
foreach (XPathNavigator nav in navigator.Select("//bk:price", manager))
{
if (nav.Value == "11.99")
{
nav.SetValue("12.99");
}
}
Console.WriteLine(navigator.OuterXml);
Source: http://msdn.microsoft.com/en-us/library/zx28tfx1(v=vs.80).aspx

Get certain xml node and save the value

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);

Categories

Resources