How to query xml of this structure? - c#

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

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.

How to remove type in XML while generating?

I have 1000 records in text file. My requirement is to insert data into SQL Server. For that, if I can convert to XML or push to script component also fine.
Source Text file consists in this format...
[{"ID":1,"Name":"test1","State":"AP"},{"ID":2,"Name":"test2","State":"KN"},{"ID":3,"Name":"test3","State":null}]
String json = System.IO.File.ReadAllText(#"C:\t1.txt");
byte[] bytes = Encoding.ASCII.GetBytes();
using (var stream = new MemoryStream(bytes))
{
var quotas = new XmlDictionaryReaderQuotas();
var jasonreader - JsonReaderWriterFactory.CreateJasonReader(stream,quotas);
var xml = XDocument.Load(jsonreader);
xmlDocument xd = new XmlDocument();
using (steamwriter fs = new streamwriter(#"C:\t.xml");
{
fs.write(xml);
}
console.writeLine(xml);
}
Output -
<root type="array"
<item type="object">
<ID type="number">1</ID>
<Name type="string">test1</Name>
<State type="string">AP</State>
</item>
<item type="object">
<root type="array"
<item type="object">
<ID type="number">2</ID>
<Name type="string">test2</Name>
<State type="string">KN</State>
</item>
<item type="object">
<root type="array"
<item type="object">
<ID type="number">3</ID>
<Name type="string">test3</Name>
<State type="string"></State>
</item>
</root>
When am using SSIS XML Source task, am getting columns...
External Columns:
type
text
item_id
Output Columns:
type
text
item_id
Ideally, I should get ID, Name, and State columns. What is the issue? How can I get rid of type in XML?
How about using Linq? (using Json.Net)
var jArr = JArray.Parse(File.ReadAllText(filename));
XElement root = new XElement("root");
foreach(JObject jObj in jArr )
{
root.Add(new XElement("item", jObj.Properties()
.Select(p => new XElement(p.Name,
p.Value.ToString()))
.ToArray()));
}
var xml = root.ToString();
or using more linq
var jArr = JArray.Parse(File.ReadAllText(filename));
XElement root = new XElement("root");
root.Add( jArr.Cast<JObject>()
.Select(jObj => new XElement("item", jObj.Properties()
.Select(p => new XElement(p.Name,
p.Value.ToString()))
.ToArray())));
var xml = root.ToString();

Get entire node XML rather than InnerXml

Given the following XML:
<Root>
<Item id="1">
<name>Foo</name>
<status>Active</status>
</Item>
<Item id="2">
<name>Bar</name>
<status>Inactive</status>
</Item>
</Root>
Let's say I have this XML in an XmlDocument object and then have the following code:
var nodes = xmlDocumentObject.GetElementsByTagName("Item");
foreach (var node in nodes)
{
var nodeXml = ??
}
I can easily get the InnerXml of each node, which for the first node would be:
<name>Foo</name>
<status>Active</status>
But how can I get the XML for the node including the containing tag and its attributes, such as this:
<Item id="1">
<name>Foo</name>
<status>Active</status>
</Item>
Try using XmlNode.OuterXml instead of InnerXml :
foreach (XmlNode node in nodes)
{
var nodeXml = node.OuterXml;
}

Loading duplicate XML attributes using XDocument

I need help loading xml using XDocument. The xml holds the data for a HierarchicalDataTemplate in WPF so each element has the same attributes.
I'm having a newbie problem with how to handle the duplicate attributes Name, image and fileLoc.
I was trying to get something like the code below to work, but as you can see duplicate attributes will not work.
public static List<MenuItem> Load(string MyMenuFile)
{
var mymenu = XDocument.Load(MyMenuFile).Root.Elements("Menu").Select(
x => new MenuItem(
(string)x.Attribute("id"),
(string)x.Attribute("name"),
(string)x.Attribute("image"),
(string)x.Attribute("fileLoc"),
(string)x.Element("itemlist"),
(string)x.Attribute("name"),
(string)x.Attribute("image"),
(string)x.Attribute("fileLoc"),
(string)x.Element("item"),
(string)x.Attribute("name"),
(string)x.Attribute("image"),
(string)x.Attribute("fileLoc")));
return stationfiles.ToList();
}
Here is the xml:
<Menus>
<Menu id="1" Name="Level1" image="C:\lvl1.jpg" fileLoc="C:\lvl1.xml">
</Menu>
<Menu id="2" Name="Level2" image="C:\lvl2.jpg" >
<itemlist Name="Level2" image="C:\lvl2.jpg" fileLoc="C:\lvl2.xml">
</itemlist>
<itemlist Name="Level3" image="C:\lvl3.jpg">
<item Name="First" image="C:\first.jpg" fileLoc="C:\first.xml"></item>
<item Name="Second" image="C:\second.jpg" fileLoc="C:\second.xml"></item>
<item Name="Third" image="C:\third.jpg" fileLoc="C:\third.xml"></item>
</itemlist>
</Menu>
</Menus>
As you can see, different elements but duplicate attributes. Should I have 3 separate classes, but how would I combine them for the XDocument load? Any help would be great.
This assumes those are elements and attributes directly of MenuItem. What I suspect is that you need read attributes of elements itemslist and items. Not sure how to do it with a single loop. You need to loop through the elements and then loop the attribute so THAT element (not the parent element).
You are not being heirarchical in your processing.
I have adjusted your xml, but here is an example of how you should be processing it:
string xml = #"<?xml version=""1.0"" encoding=""UTF-8""?>
<Menus>
<Menu id=""1"" Name=""Level1 - Alpha"" image=""C:\lvl1.jpg"" fileLoc=""C:\lvl1.xml""/>
<Menu id=""2"" Name=""Level1 - Beta"" image=""C:\lvl2.jpg"" fileLoc=""C:\lvl1.xml"" >
<itemlist Name=""Level2-Gamma"" image=""C:\lvl2.jpg"" fileLoc=""C:\lvl2.xml""/>
<itemlist Name=""Level3-Zeta"" image=""C:\lvl3.jpg"" fileLoc=""C:\lvl1.xml"">
<item Name=""First"" image=""C:\first.jpg"" fileLoc=""C:\first.xml""></item>
<item Name=""Second"" image=""C:\second.jpg"" fileLoc=""C:\second.xml""></item>
<item Name=""Third"" image=""C:\third.jpg"" fileLoc=""C:\third.xml""></item>
</itemlist>
</Menu>
</Menus>";
var xd = XDocument.Parse(xml);
var result =
xd.Descendants("Menu")
.Select (l1 => new
{
Name = l1.Attribute("Name").Value,
Image = l1.Attribute("image").Value,
File = l1.Attribute("fileLoc"),
Children = l1.Descendants("itemlist")
.Select (l2 => new {
Name = l2.Attribute("Name").Value,
Image = l2.Attribute("image").Value,
File = l2.Attribute("fileLoc"),
Children = l2.Descendants("item")
.Select (l3 => new {
Name = l3.Attribute("Name").Value,
Image = l3.Attribute("image").Value,
File = l3.Attribute("fileLoc")
})
})
});
Console.WriteLine (result );
Here is the result as found from linqpad:
See how the data parses out, that is how you need to work with it to get it into the menu structure. There are no duplicate attributes. :-)
HTH

LINQ to XML (Dynamic XML)

I have an XML file which has kind of a similar structure that you can see below:
I would like to select title and subitems using LINQ to XML. The difficulties that I have: sometimes a subitem can be just one and sometimes it can be 20 subitems, and I need to add them to List<string>.
<?xml version="1.0"?>
<items>
<item>
<title>Name of the title</title>
<subitem>Test</subitem>
<subitem1>Test</subitem1>
<subitem2>Test</subitem2>
<subitem3>Test</subitem3>
<subitem4>Test</subitem4>
<subitem5>Test</subitem5>
</item>
<item>
<title>Name of the title</title>
<subitem>Test</subitem>
<subitem1>Test</subitem1>
<subitem2>Test</subitem2>
<subitem3>Test</subitem3>
</item>
<item>
<title>Name of the title</title>
<subitem>Test</subitem>
<subitem1>Test</subitem1>
</item>
</items>
The solution, including getting the titles, is:
XDocument yourXDocument = XDocument.Load(yourXmlFilePath);
IEnumerable<Tuple<XElement, IEnumerable<XElement>>> yourSubItems =
yourXDocument.Root.Descendants()
.Where(xelem => xelem.Name == "title")
.Select(xelem => new Tuple<XElement, IEnumerable<XElement>>(xelem, xelem.Parent.Elements().Where(subelem => subelem.Name.LocalName.StartsWith("subitem")));
XDocument xdoc = XDocument.Load(path_to_xml);
var query = from i in xdoc.Descendants("item")
select new
{
Title = (string)i.Element("title"),
Subitems = i.Elements()
.Where(e => e.Name.LocalName.StartsWith("subitem"))
.Select(e => (string)e)
.ToList()
};

Categories