I have a simple XML Document like this
<Pictures>
<Picture>
<Source>1</Source>
<Title>One</Title>
</Picture>
<Picture>
<Source>2</Source>
<Title>Two</Title>
</Picture>
<Pictures>
Im trying to grab the values of 1 and 2. Heres what Im trying.
foreach (XmlNode mynode in doc.ChildNodes)
{
var source = mynode.SelectSingleNode("//Source").InnerText;
var title = mynode.SelectSingleNode("//Title").InnerText;
}
The problem is this returns the value of 1 twice, and the value of One twice, instead of 1 and 2, im assuming because the "//" means the topmose match. I guess my twofold question is...
How would I do this?
How would I do this in Linq?
var xDoc = XDocument.Load("path");
var pictures = xDoc.Root
.Elements("Picture")
.Select(x => new
{
source = (string)x.Element("Source"),
title = (string)x.Element("Title")
}).ToList();
Related
I have an XML file that looks like this -
<SST_SignageCompConfig>
<Items>
<Item>
<Index>0</Index>
<Type>1</Type>
<Duration>7</Duration>
<Name>Branding-Colours-for-business.jpg</Name>
</Item>
<Item>
<Index>1</Index>
<Type>1</Type>
<Duration>7</Duration>
<Name>Flower of Life Meditation - Copy.png</Name>
</Item>
</Items>
</SST_SignageCompConfig>
I need to count how many Item Elements there are within the Items Element.
ie how many images there are.
I'm using XDocument, so my XML file is loaded like this -
string configurationPath = System.IO.Path.Combine("C:\\SST Software\\DSS\\Compilations\\" + compName + #"\\Comp.cfg");
XDocument filedoc = XDocument.Load(configurationPath);
I've tried numerous variations of the following, with all returning a null object reference exception
foreach (var item in filedoc.Element("SST_SignageCompConfig").Element("Items").Element("Item").Nodes())
{
string name = filedoc.Element("SST_SignageCompConfig").Element("Items").Element("Item").Attribute("Name").ToString();
files.Append(name + "|");
}
I've found countless examples of how to count how many different child elements are within an element, but I need to know how many instances of the same element exist.
Can anyone point me in the right direction?
You can select all names like so:
var names = from item in filedoc.Descendants("Item")
select (string)item.Element("Name");
Or without the query syntax:
var names = filedoc.Descendants("Item").Elements("Name").Select(e => e.Value);
You can get only unique names by:
var uniqueNames = names.Distinct();
You're on the right track. Try finding out exactly which invocation is giving you the NullReferenceException. My guess is that it's the attempt to find:
.Element("SST_SignageCompConfig")
Which is your root. Try the following instead:
// note the difference between .Element and .Elements
var count = filedoc.Root.Element("Items").Elements("Item").Count();
You could also use XPath to help you nail down the navigation within your XDocument:
// returns the current top level element
var element = filedoc.Root.XPathSelectElement(".");
// If the returned element is "SST_SignageCompConfig", then:
var nextElement = filedoc.Root.XPathSelectElement("./Items")
// If the "." element is *not* "SST_SignageCompConfig", then try and locate where in your XML document that node is.
// You can navigate up with .Parent and down with .Element(s)
And so on.
How about:
var nav = fileDoc.CreateNavigator();
XPathNodeIterator navShape = nav.Select("/SST_SignageCompConfig/Items");
navShape.MoveNext()
var count = navShape.Count;
If your xml has only one Items element, this should do the trick:
filedoc.Descendants("Item")
.GroupBy(e => e.Element("Name")!=null? e.Element("Name").Value:String.Empty)
.Select(g => new
{
Name = g.Key,
Count = g.Count()
});
Because "Name" is an element and not an attribute of your xml structure.
can you try replacing this?
string name = filedoc.Element("SST_SignageCompConfig").Element("Items").Element("Item").Element("Name").ToString();
This question already has answers here:
Reading value of an XML node
(3 answers)
Closed 8 years ago.
I have created an Xml file with example contents as follows:
<?xml version="1.0" encoding="utf-8"?>
<W-TIBCPTRs>
<W-TIBCPTR>
<TYPTRT>FDR2 R</TYPTRT>
<CLAFCNO VALIDE="NON">5b1</CLAFCNO>
<NUMCLI>0067781</NUMCLI>
<TYPACT>D</TYPACT>
</W-TIBCPTR>
<W-TIBCPTR>
<TYPTRT>FDR2 R</TYPTRT>
<CLAFCNO>511</CLAFCNO>
<NUMCLI>0068078</NUMCLI>
<TYPACT>D</TYPACT>
</W-TIBCPTR>
</W-TIBCPTRs>
i try this
XmlNodeList rowElements = doc.SelectNodes("W-TIBCPTRs/W-TIBCPTR");
foreach (XmlElement rowElement in rowElements)
{
foreach (XmlElement valueElement in rowElement.ChildNodes)
{
strin[] k=valueElement.Name;
}
}
I need to selectNodes automaticly because I can have a xml file with different nodes
Stock childNodes in array, I need these values (TYPTRT,CLAFCNO,NUMCLI,TYPACT)
It's easy to do with Linq to Xml (I suggest to use it instead old XmlDocument API):
var xdoc = XDocument.Load(path_to_xml);
var result = from t in xdoc.Root.Elements("W-TIBCPTR")
select new {
TYPTRT = (string)t.Element("TYPTRT"),
CLAFCNO = (string)t.Element("CLAFCNO"),
NUMCLI = (string)t.Element("NUMCLI"),
TYPACT = (string)t.Element("TYPACT")
};
Result:
[
{
TYPTRT: "FDR2 R",
CLAFCNO: "5b1",
NUMCLI: "0067781",
TYPACT: "D"
},
{
TYPTRT: "FDR2 R",
CLAFCNO: "511",
NUMCLI: "0068078",
TYPACT: "D"
}
]
NOTE: Currently you are selecting element's name. If you want to get inner text of all sub-elements in array, you can use:
string[] items = rowElement.ChildNodes.OfType<XmlElement>()
.Select(e => e.InnerText)
.ToArray();
That will return array of four strings for each W-TIBCPTR element in your sample xml. Same with Linq to Xml will look like:
var result = from t in xdoc.Root.Elements("W-TIBCPTR")
select t.Elements().Select(e => e.Value).ToArray();
Use Linq to XML
var doc= XDocument.Load(<path>);
var values = from c in doc.Descendants("W-TIBCPTR")
select new
{
TYPTRT = c.Element("TYPTRT").Value,
CLAFCNO = c.Element("CLAFCNO").Value
NUMCLI = c.Element("NUMCLI").Value
TYPACT = c.Element("TYPACT").Value
});
i just stuck in an issue when ever i parse my XML like this using Xdocument :
XDocument xmldoc = XDocument.Load(datafromxml);
var data = from query in xmldoc.Descendants("Chapter")
select new MyEntityclass
{
Sampledata = (string)query.Element("SubChapter")
};
i got only one tag inner value from this. i.e from the first tag value only. remaining are skipped.
My XML is like :
<Chapter>
<SubChapter ChapterID="1"><![CDATA["Some data here 1"]]></SubChapter>
<SubChapter ChapterID="2"><![CDATA["Some data here 2"]]></SubChapter>
<SubChapter ChapterID="3"><![CDATA["Some data here 3"]]></SubChapter>
</Chapter>
when i checked in a debug i just got the value of "chapterid : 1". please help me to sort out this. thanks
Your query now only retreives (iterates) the outer node.
You need something like (untested)
var data = from query in xmldoc.Descendants("Chapter")
from chapter in query.Elements("SubChapter") // note the 's'
select new MyEntityclass
{
Sampledata = (string)chapter
};
var data = xmlDoc.Root
.Elements("SubChapter")
.Select(x => new MyEntityclass { Sampledata = (string)x });
I have this XML file:
<MyXml>
<MandatoryElement1>value</MandatoryElement1>
<MandatoryElement2>value</MandatoryElement2>
<MandatoryElement3>value</MandatoryElement3>
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
<MyXml>
All 3 elements that are called 'MandatoryElementX' will always appear in the file. The elements called 'CustomElementX' are unknown. These can be added or removed freely by a user and have any name.
What I need is to fetch all the elements that are not MandatoryElements. So for the file above I would want this result:
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
I don't know what the names of the custom elements may be, only the names of the 3 MandatoryElements, so the query needs to somehow exclude these 3.
Edit:
Even though this was answered, I want to clarify the question. Here is an actual file:
<Partner>
<!--Mandatory elements-->
<Name>ALU FAT</Name>
<InterfaceName>Account Lookup</InterfaceName>
<RequestFolder>C:\Documents and Settings\user1\Desktop\Requests\ALURequests</RequestFolder>
<ResponseFolder>C:\Documents and Settings\user1\Desktop\Responses</ResponseFolder>
<ArchiveMessages>Yes</ArchiveMessages>
<ArchiveFolder>C:\Documents and Settings\user1\Desktop\Archive</ArchiveFolder>
<Priority>1</Priority>
<!--Custom elements - these can be anything-->
<Currency>EUR</Currency>
<AccountingSystem>HHGKOL</AccountingSystem>
</Partner>
The result here would be:
<Currency>EUR</Currency>
<AccountingSystem>HHGKOL</AccountingSystem>
You can define a list of mandatory names and use LINQ to XML to filter:
var mandatoryElements = new List<string>() {
"MandatoryElement1",
"MandatoryElement2",
"MandatoryElement3"
};
var result = xDoc.Root.Descendants()
.Where(x => !mandatoryElements.Contains(x.Name.LocalName));
Do you have created this xml or do you get it by another person/application?
If it's yours I would advise you not to number it. You can do something like
<MyXml>
<MandatoryElement id="1">value<\MandatoryElement>
<MandatoryElement id="2">value<\MandatoryElement>
<MandatoryElement id="3">value<\MandatoryElement>
<CustomElement id="1">value<\CustomElement>
<CustomElement id="2">value<\CustomElement>
<MyXml>
In the LINQ-Statement you don't need the List then.
Your question shows improperly formatted XML but I am assuming that is a typo and the real Xml can be loaded into the XDocument class.
Try this...
string xml = #"<MyXml>
<MandatoryElement1>value</MandatoryElement1>
<MandatoryElement2>value</MandatoryElement2>
<MandatoryElement3>value</MandatoryElement3>
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
</MyXml> ";
System.Xml.Linq.XDocument xDoc = XDocument.Parse(xml);
var result = xDoc.Root.Descendants()
.Where(x => !x.Name.LocalName.StartsWith("MandatoryElement"));
lets say TestXMLFile.xml will contain your xml,
XElement doc2 = XElement.Load(Server.MapPath("TestXMLFile.xml"));
List<XElement> _list = doc2.Elements().ToList();
List<XElement> _list2 = new List<XElement>();
foreach (XElement x in _list)
{
if (!x.Name.LocalName.StartsWith("Mandatory"))
{
_list2.Add(x);
}
}
foreach (XElement y in _list2)
{
_list.Remove(y);
}
I can't figure out why my code just taking the first tag and not the rest.
var xml = XDocument.Load(HttpContext.Current.Server.MapPath("~/App_Data/Themes.xml"));
var q = from f in xml.Descendants("themes")
select new ThemesItem
{
Name = f.Element("theme").Element("name").Value,
Description = f.Element("theme").Element("description").Value,
Author = f.Element("theme").Element("author").Value,
};
return q.ToList();
ThemeItem is just a get set with public string
When i write out this data i use a repeater
Thanks for help :)
That is because the Descendants extension method takes all decendants of the xml node, that is named "themes". Since your themes node is the container for the individual theme tags, there is only one, and when you take .Element on that, you get the first occurence.
This code should work:
var q = from f in xml.Descendants("theme")
select new ThemesItem
{
Name = f.Element("name").Value,
Description = f.Element("description").Value,
Author = f.Element("author").Value,
};
<themes>
<theme>
<name>Standard</name>
<description>standard theme</description>
<author>User 1</author>
<folder>standard</folder>
</theme>
<theme>
<name>Standard</name>
<description>standard theme</description>
<author>User 2</author>
<folder>standard</folder>
</theme>
</themes>
Try using XElement.Load() instead of XDocument.Load()
http://msdn.microsoft.com/en-us/library/bb675196.aspx