Query to retrieve names of group nodes - c#

If I had some XML such as this loaded into an XDocument object:
<Root>
<GroupA>
<Item attrib1="aaa" attrib2="000" />
</GroupA>
<GroupB>
<Item attrib1="bbb" attrib2="111" />
<Item attrib1="ccc" attrib2="222" />
<Item attrib1="ddd" attrib2="333" />
</GroupB>
<GroupC>
<Item attrib1="eee" attrib2="444" />
<Item attrib1="fff" attrib2="555" />
</GroupC>
</Root>
What would a query look like to retrieve the names of the group nodes?
For example, I'd like a query to return:
GroupA
GroupB
GroupC

Something like this:
XDocument doc; // populate somehow
// this will give the names as XName
var names = from child in doc.Root.Elements()
select child.Name;
// if you want just the local (no-namespaces) name as a string, use this
var simpleNames = from child in doc.Root.Elements()
select child.Name.LocalName;

Related

XDocument Descendant Selector using Wildcard?

I have some XML structured like this:
<form>
<section-1>
<item-1>
<value />
</item-1>
<item-2>
<value />
</item-2>
</section-1>
<section-2>
<item-3>
<value />
</item-3>
<item-4>
<value />
</item-4>
</section-2>
</form>
...and want to turn it into something sane like this:
<form>
<items>
<item id="1">
<value/>
</item>
<item id="2">
<value/>
</item>
<item id="3">
<value/>
</item>
<item id="4">
<value/>
</item>
</items>
</form>
I am struggling to turn the old XML into an array or object of values. Once in the new format I'd be able to do the following:
XDocument foo = XDocument.Load(form.xml);
var items = foo.Descendants("item")
.Select(i => new Item
{
value = i.Element("value").Value
});
...but in the current mess the xml is in can I wildcard the descendants selector?
var items = foo.Descendants("item"*)
...or something? I tried to follow this question's answer but failed to adapt it to my purpose.
Ah-ha! It did click in the end. If I leave the descendants selector blank and add in a where statement along the lines of what's in this question's answer
.Where(d => d.Name.ToString().StartsWith("item-"))
Then we get:
XDocument foo = XDocument.Load(form.xml);
var items = foo.Descendants()
.Where(d => d.Name.ToString().StartsWith("item-"))
.Select(i => new Item
{
value = i.Element("value").Value
});
...and I'm now able to iterate through those values while outputting the new XML format. Happiness.

Xpath path not working

I'm trying to get an element and its siblings via XpathNavigator.
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE document SYSTEM 'xmlschemas/domino_6_5_5.dtd'>
<document xmlns='http://www.lotus.com/dxl' version='6.5' maintenanceversion='5.0'>
<item name='Keywords'><text/></item>
<item name='Version'><number>1</number></item>
<item name='UPDATEDISTRIBUTION'><text>1</text></item>
<item name='$FILE' summary='true' sign='true' seal='true'>
<object>
<file hosttype='cdstorage' compression='none' flags='storedindoc' name='STG08828'>
<created><datetime>20110119T230442,22+01</datetime></created>
</file>
</object>
</item>
</document>
I want to navigate to the file-element, with the following XPath:
//item/name/object/file[#name='STG08828']
Why this path is wrong?
EDIT: Thanks for the hint with my "name"-mistake.
When I try to run it, I get nothing.
XmlElement rootNode = xmlDoc.DocumentElement;
// select the file Element
String query = "//file[#name='" + name + "']";
XmlNodeList fileElement = rootNode.SelectNodes(query);
I think you want:
//item/object/file[#name='STG08828']
Or maybe just:
//file[#name='STG08828']
I think you are missing your names space for xmlns='http://www.lotus.com/dxl'
Example:
XNamespace myMs = "http://www.lotus.com/dxl";

How to query xml of this structure?

<root>
<level1>
<item id="1" date="" name="" >
<item id="2" date="" name="" >
<item id="3" date="" name="" >
<item id="4" date="" name="" >
<item id="5" date="" name="" >
</level1>
</root>
I have an xml structure like the one above.
I used
XmlNodeList xnList = xmlDoc.SelectNodes("/level1");
If I used xmlnodelist as above, how can I specifically only get the element with id="3"?
or more useful if I could store all elements inside as elements in xnlist?
XmlNodeList xnList = xmlDoc.SelectNodes("//level1/item[#id='3']");
and if you want to use Linq To Xml
var xDoc = XDocument.Parse(xmlstring); // XDocument.Load(filename)
var items = xDoc.Descendants("level1")
.First()
.Elements("item")
.Select(item => new {
ID = item.Attribute("id").Value,
Name = item.Attribute("name").Value
})
.ToList();
You can even combine XPath and Linq2Xml
var item2 = xDoc.XPathSelectElements("//level1/item")
.Select(item => new {
ID = item.Attribute("id").Value,
Name = item.Attribute("name").Value
})
.ToList();
besides the great answer from #L.B I also use Linq, personally I think is a lot more readable:
xdoc.Element("level1")
.Descendants("item")
.Where(x => x.Attribute("id").Value == "3").First();
but it all depends on your style ;)

Load selective XML data into Dataset using C#

Appreciate some pointers on how can I use XPath on XML file to extract only some data and load it into a dataset.
ds.ReadXml(fsReadXml);
will load the entire xml into dataset but my requirement is to load only particular node and values to dataset.
Sample xml data:
<data cobdate="5 Jul 2011" DBStatus="">
<view>BOTH</view>
<show_acctnbr>true</show_acctnbr>
<summary>
<headings sum="Summary" real_per="Realized this period" real_trd="Profit/loss in trading currency" real_select="Profit/loss in selected currency" short_term="Short Term Profit/Loss" long_term="Long Term Profit/Loss" />
<account number="A123456" curr_code="USD" curr_desc="US Dollars" tradecurrvalue="123,123.00" selectcurrvalue="123,123.00" managed="NO" />
<account number="P123456" curr_code="USD" curr_desc="US Dollars" tradecurrvalue="0.00" selectcurrvalue="0.00" managed="NO" />
</summary>
<detail>
<headings dateaq="Date acquired" datesld="Date sold" desc="Description" sec_nbr="Security number " qty="Quantity" cost="Cost basis" />
<account number="A123456" currency="US Dollars">
<item datesold="29 Apr 11" sec_nbr="1234" description="SOME VALUE(USD)" quantity="8,000" proceeds="123,123.0" />
<item datesold="29 Apr 11" sec_nbr="4567" description="SOME VALUE(USD)" quantity="9,000" proceeds="123,123.0" />
</account>
<account number="P123456" currency="US Dollars">
<item datesold="29 Apr 11" sec_nbr="1234" description="SOME VALUE(USD)" quantity="8,000" proceeds="123,123.0" />
<item datesold="29 Apr 11" sec_nbr="4567" description="SOME VALUE(USD)" quantity="9,000" proceeds="123,123.00" />
</account>
</detail>
</data>
In this example data, I just need to load accounts from <summary> node and if possible only number, tradecurrvalue and selectcurrvalue attributes. I using C# and 3.5.
With XDocument:
var doc = XDocument.Load(fileName);
var lst = doc
.Descendants("summary") // don't care where summary is
.Elements("account ") // direct child of <summary>
.Select(x => new
{
number = x.Attribute("number").Value,
...
});
foreach(var account in lst)
{
.... // add to DataSet
}
can also use XmlNodeList and XmlNode, light weight compared to others
foreach (XmlNode node in nodes)
{
dt.Rows.Add(node["Name"].InnerText.ToString(),
node["NoOfDay"].InnerText.ToString(),
node["dateColumn"].InnerText.ToString()
);
}
Reference Link
Evaluate this XPath expression in your code:
/*/summary/account/#*
[contains('|number|tradecurrvalue|selectcurrvalue|',
concat('|',name(),'|')
)
]
This selects any attribute (of any account element that is a child of any summary element that is a child of the top element in the XML document), named "number", "tradecurrvalue" or "selectcurrvalue"
It is very easy to enlarge the list of possible attribute names you want selected -- just include them in the pipe-delimited name list.

XML Namespaces are confounding me

I have an XML document which is confounding me. I'd like to (to start) pull all of the document nodes (/database/document), but it only works if I remove all of the attributes on the database element. Specifically the xmlns tag causes an xpath query for /database/document to return nothing - remove it, and it works.
xmlns="http://www.lotus.com/dxl"
I take it this has to do with XML namespaces. What is it doing, and more to the point, how do I make it stop? I just want to parse the document for data.
<?xml version="1.0" encoding="utf-8"?>
<database xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.lotus.com/dxl xmlschemas/domino_7_0_3.xsd"
xmlns="http://www.lotus.com/dxl"
version="7.0"
maintenanceversion="3.0"
path="C:\LotusXML\test1.nsf"
title="test1">
<databaseinfo numberofdocuments="3">
<datamodified>
<datetime dst="true">20090812T142141,48-04</datetime>
</datamodified>
<designmodified>
<datetime dst="true">20090812T154850,91-04</datetime>
</designmodified>
</databaseinfo>
<document form="NameAddress">
<noteinfo noteid="8fa" unid="x" sequence="2">
<created>
<datetime dst="true">20090812T130308,71-04</datetime>
</created>
<modified>
<datetime dst="true">20090812T142049,36-04</datetime>
</modified>
<revised>
<datetime dst="true">20090812T142049,35-04</datetime>
</revised>
<lastaccessed>
<datetime dst="true">20090812T142049,35-04</datetime>
</lastaccessed>
<addedtofile>
<datetime dst="true">20090812T130321,57-04</datetime>
</addedtofile>
</noteinfo>
<updatedby>
<name>MOOSE</name>
</updatedby>
<revisions>
<datetime dst="true">20090812T130321,57-04</datetime>
</revisions>
<item name="Name">
<text>joe</text>
</item>
<item name="OtherName">
<text>dave</text>
</item>
<item name="Address">
<text>here at home</text>
</item>
<item name="PictureHere">
<richtext>
<pardef id="1" />
<par def="1">
</par>
<par def="1" />
</richtext>
</item>
</document>
<document form="NameAddress">
<noteinfo noteid="8fe" unid="x" sequence="2">
<created>
<datetime dst="true">20090812T130324,59-04</datetime>
</created>
<modified>
<datetime dst="true">20090812T142116,95-04</datetime>
</modified>
<revised>
<datetime dst="true">20090812T142116,94-04</datetime>
</revised>
<lastaccessed>
<datetime dst="true">20090812T142116,94-04</datetime>
</lastaccessed>
<addedtofile>
<datetime dst="true">20090812T130333,90-04</datetime>
</addedtofile>
</noteinfo>
<updatedby>
<name>MOOSE</name>
</updatedby>
<revisions>
<datetime dst="true">20090812T130333,90-04</datetime>
</revisions>
<item name="Name">
<text>fred</text>
</item>
<item name="OtherName">
<text>wilma</text>
</item>
<item name="Address">
<text>bedrock</text>
</item>
<item name="PictureHere">
<richtext>
<pardef id="1" />
<par def="1">
</par>
<par def="1" />
</richtext>
</item>
</document>
<document form="NameAddress">
<noteinfo noteid="902" unid="x" sequence="2">
<created>
<datetime dst="true">20090812T130337,09-04</datetime>
</created>
<modified>
<datetime dst="true">20090812T142141,48-04</datetime>
</modified>
<revised>
<datetime dst="true">20090812T142141,47-04</datetime>
</revised>
<lastaccessed>
<datetime dst="true">20090812T142141,47-04</datetime>
</lastaccessed>
<addedtofile>
<datetime dst="true">20090812T130350,20-04</datetime>
</addedtofile>
</noteinfo>
<updatedby>
<name>MOOSE</name>
</updatedby>
<revisions>
<datetime dst="true">20090812T130350,20-04</datetime>
</revisions>
<item name="Name">
<text>julie</text>
</item>
<item name="OtherName">
<text>mccarthy</text>
</item>
<item name="Address">
<text>the pen</text>
</item>
<item name="PictureHere">
<richtext>
<pardef id="1" />
<par def="1">
</par>
<par def="1" />
</richtext>
</item>
</document>
</database>
The xmlns="http://www.lotus.com/dxl" sets a default namespace for contained nodes. It means that /database/document is really /{http://www.lotus.com/dxl}:database/{http://www.lotus.com/dxl}:document. Your XPath query will need to include the namespace:
XmlDocument doc = new XmlDocument();
doc.Load(fileName);
XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("tns", "http://www.lotus.com/dxl");
var documents = doc.SelectNodes("/tns:database/tns:document", ns);
When there is an XML namespace defined, each element needs to preceded by it for it to be correctly recognized.
If you were to use LINQ to XML to read in this data it would look something like this:
XDocument xdoc = XDocument.Load("file.xml");
XNamespace ns = "http://www.lotus.com/dxl";
var documents = xdoc.Descendants(ns + "document");
XML namespaces are similar in concept to C# namespaces (or any other language that supports it). If you define a class inside a namespace, you wouldn't be able to access it without first specifying the namespace (this is what using statements do for you).
You need to specify the element by their full name, including the namespace. The easy way to do this is to define the appropriate XNamespace and prepend it to the element name.
XDocument myDoc;
XNamespace ns = "http://www.lotus.com/dxl";
XElement myElem = myDoc.Element(ns + "ElementName");
See MSDN for more information.

Categories