So i have next xml document:
<Items>
<Item>
<ID>123</ID>
<Name>Super Item</Name>
<Count>1</Count>
<Price>45</Price>
</Item>
<Item>
<ID>456</ID>
<Name>not super Item</Name>
<Count>10</Count>
<Price>5</Price>
</Item>
<Item>
<ID>789</ID>
<Name>Simple Item</Name>
<Count>6</Count>
<Price>10</Price>
</Item>
</Items>
So how can i find needed item by ID and read next values? Thanks in advance.
code:
XDocument doc = XDocument.Load (filePath);
foreach (var item in doc.Descendants("ID"))
{
if ((string)item.Element("ID") == "789")
{
How to read Name "Simple Item"?
How to read Count "6"?
How to read Price "10"?
}
}
By what you are asking you could formatt your xml like this:
<Items>
<Item id="123">
<Name>Super Item</Name>
<Count>1</Count>
<Price>45</Price>
</Item>
<Item id="456">
<Name>not super Item</Name>
<Count>10</Count>
<Price>5</Price>
</Item>
<Item id="789">
<Name>Simple Item</Name>
<Count>6</Count>
<Price>10</Price>
</Item>
</Items>
Then in code:
int yourId = 456;
XDocument doc = XDocument.Load("test.xml");
var result = from el in doc.Root.Elements("Item")
where el.Attribute("id").Value == yourId.ToString()
select el;
The id here is an attribute.And for reading its values 2 ways:
//1º
foreach (var item in result.Elements())
{
Console.WriteLine(item.Name + " = " + item.Value);
}
//2º - will print the element
Console.WriteLine(result);
It depends on what you want to do when you find those values. Here is a general method using a foreach loop to find the item with the specified ID and returning it's name:
private string GetItemName(string _id)
{
XmlDocument xDoc = new XmlDocument();
xDoc.Load("myXmlFile.xml");
foreach (XmlNode item in xDoc.SelectNodes("/Items/Item"))
{
if (item.SelectSingleNode("ID").InnerText == _id)
{
// we found the item! Now what do we do?
return item.SelectSingleNode("Name").InnerText;
}
}
return null;
}
Related
I would like to parse the below xml using XDocument in Linq.
<?xml version="1.0" encoding="UTF-8"?>
<string xmlns="http://tempuri.org/">
<Sources>
<Item>
<Id>1</Id>
<Name>John</Name>
</Item>
<Item>
<Id>2</Id>
<Name>Max</Name>
</Item>
<Item>
<Id>3</Id>
<Name>Ricky</Name>
</Item>
</Sources>
</string>
My parsing code is :
var xDoc = XDocument.Parse(xmlString);
var xElements = xDoc.Element("Sources")?.Elements("Item");
if (xElements != null)
foreach (var source in xElements)
{
Console.Write(source);
}
xElements is always null. I tried using namespace as well, it did not work. How can I resolve this issue?
Try below code:
string stringXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><string xmlns=\"http://tempuri.org/\"><Sources><Item><Id>1</Id><Name>John</Name></Item><Item><Id>2</Id><Name>Max</Name></Item><Item><Id>3</Id><Name>Ricky</Name></Item></Sources></string>";
XDocument xDoc = XDocument.Parse(stringXml);
var items = xDoc.Descendants("{http://tempuri.org/}Sources")?.Descendants("{http://tempuri.org/}Item").ToList();
I tested it and it correctly shows that items has 3 lements :) Maybe you used namespaces differently (it's enough to inspect xDoc objct in object browser and see its namespace).
You need to concatenate the namespace and can directly use Descendants method to fetch all Item nodes like:
XNamespace ns ="http://tempuri.org/";
var xDoc = XDocument.Parse(xmlString);
var xElements = xDoc.Descendants(ns + "Item");
foreach (var source in xElements)
{
Console.Write(source);
}
This prints on Console:
<Item xmlns="http://tempuri.org/">
<Id>1</Id>
<Name>John</Name>
</Item><Item xmlns="http://tempuri.org/">
<Id>2</Id>
<Name>Max</Name>
</Item><Item xmlns="http://tempuri.org/">
<Id>3</Id>
<Name>Ricky</Name>
</Item>
See the working DEMO Fiddle
I am trying to edit a specific node in an XML document. I have the following XML data:
<Items xmlns="http://foo.com/blah/blah">
<Item>
<Format>1</Format>
<Name>Edit this one</Name>
<MetaDataDictionary>
<MetaData>
<Name>Do not edit this one</Name>
<Value>0</Value>
</MetaData>
</MetaDataDictionary>
</Item>
<Item>
<Format>1</Format>
<Name>Edit this one</Name>
<MetaDataDictionary>
<MetaData>
<Name>Do not edit this one</Name>
<Value>0</Value>
</MetaData>
</MetaDataDictionary>
</Item>
</Items>
I want to append a number to each Item/Name node content but not to the Metadata/Name nodes, save the file off as test_n.xml and repeat n number of times.
The code I'm using seems to get me what I want for the Item/Name node and saved the file(s) correctly but it also updates the Metadata/Name nodes and I do not want that value to be updated. I understand the problem is in the navigator.Select call but I just don't know how to update one and skip the other.
XmlDocument doc = new XmlDocument();
doc.Load("test.xml");
XPathNavigator navigator = doc.CreateNavigator();
XmlNamespaceManager manager = new XmlNamespaceManager(navigator.NameTable);
manager.AddNamespace("at", "http://foo.com/blah/blah");
for (int i = 0; i < 10; i++)
{
foreach (XPathNavigator nav in navigator.Select("//at:Name", manager))
{
var currValue = nav.Value;
nav.SetValue(currValue + " " + i);
}
doc.Save("test_" + i + ".xml");
}
In the end I'd like it to be:
<Items xmlns="http://foo.com/blah/blah">
<Item>
<Format>1</Format>
<Name>Edit this one 0</Name>
<MetaDataDictionary>
<MetaData>
<Name>Do not edit this one</Name>
<Value>0</Value>
</MetaData>
</MetaDataDictionary>
</Item>
<Item>
<Format>1</Format>
<Name>Edit this one 0</Name>
<MetaDataDictionary>
<MetaData>
<Name>Do not edit this one</Name>
<Value>0</Value>
</MetaData>
</MetaDataDictionary>
</Item>
</Items>
Don't select all name tags, instead select only name tags which are children of /Items/Item tag.
The XPath query would look like /Items/Item/Name.
You can test it here. See the documentation for details.
I'm trying to select a specific node and fetch the values in it's childnodes. This would normally be pretty easy, but the complication is that the nodes have the same name. My xml looks something like this;
<Settings>
<Config>
</Config>
<Items>
<Item>
<ID>Hello</ID>
<Pth>Somevalue</Pth>
<Zvb>True</Zvb>
<Ico>True</Ico>
</Item>
<Item>
<ID>Stack</ID>
<Pth>Somevalue</Pth>
<Zvb>False</Zvb>
<Ico>True</Ico>
</Item>
<Item>
<ID>Overflow</ID>
<Pth>Somevalue</Pth>
<Zvb>False</Zvb>
<Ico>True</Ico>
</Item>
</Items>
</Settings>
Each <ID>'s innertext is always unique. I now want to select the <Item> ,where it's <ID>'s innertext is "Stack". (I need the other childnode-values as well, like Pth, Zvb and Ico. So everything under <Item> basically)
I did this is powershell, and it looks something like this;
$script:specificItem = $dgvItems.rows[$_.RowIndex].Cells[1].Value
$script:fetch = #($xml.SelectNodes('//Item')) | Select-Object * | Where { $_.ID -like $specificItem }
So far I've got this (I'm in a RowEnter event of a datagridview):
XmlDocument xml = new XmlDocument();
xml.Load(GlobalVars.configfile);
int rowindex = dgvItemlist.CurrentCell.RowIndex;
dgvItemlist.Rows[rowindex].Cells[2].Value.ToString(); //This will contain for example "Stack"
XmlNodeList Items = xml.SelectNodes("//Items/Item"); //probably other ways to start as well
... but from here I struggle with filtering or selecting the one I want. I know this is a fairly common question, but I can't find a good solution for this exact issue.
You could also use XDocument (Linq to XML):
string xml =#"<Settings>
<Config>
</Config>
<Items>
<Item>
<ID>Hello</ID>
<Pth>Somevalue</Pth>
<Zvb>True</Zvb>
<Ico>True</Ico>
</Item>
<Item>
<ID>Stack</ID>
<Pth>Somevalue</Pth>
<Zvb>False</Zvb>
<Ico>True</Ico>
</Item>
<Item>
<ID>Overflow</ID>
<Pth>Somevalue</Pth>
<Zvb>False</Zvb>
<Ico>True</Ico>
</Item>
</Items>
</Settings>";
XDocument xdoc = XDocument.Parse(xml);
XElement desired = xdoc.Descendants("Item").FirstOrDefault(x=>(string)x.Element("ID")=="Stack");
if(desired!=null)
{
string Pth = (string)desired.Element("Pth");
string Zvb = (string)desired.Element("Zvb");
string Ico = (string)desired.Element("Ico");
}
desired will be the wanted element.
Try to change the last line of your code into:
XmlNodeList Items = xml.SelectNodes("//Items/Item[ID='Stack']");
This should return:
<Item>
<ID>Stack</ID>
<Pth>Somevalue</Pth>
<Zvb>False</Zvb>
<Ico>True</Ico>
</Item>
Try the following. It will return the specific node you are looking for.
XmlNode itemNode = doc.SelectSingleNode("//ID[text()='Stack']").ParentNode;
I'm trying to extract data from provided xml and add that to object using XmlReader, but I noticed on nodes without value, I get "\n " instead.
Example xml:
<Items>
<Item>
<NodeA>Some Value</NodeA>
<NodeB>N</NodeB>
<NodeC />
</Item>
<Item>
...
</Item>
</Items>
Part of my modified C#:
while (sub_reader.ReadToFollowing("Item"))
{
var item = new Item();
sub_reader.ReadToFollowing("NodeA");
sub_reader.Read();
item.NodeA = sub_reader.Value;
sub_reader.ReadToFollowing("NodeB");
sub_reader.Read();
item.NodeB = sub_reader.Value;
sub_reader.ReadToFollowing("NodeC");
sub_reader.Read();
item.NodeC = sub_reader.Value; //This return "\n "
this.Items.Add(item);
}
Is there any function/convenient way that work the above but return null or empty string when <NodeC /> happens? The real xml is much larger and I don't want to do if else on each of them.
Any suggestion is appreciated. Thanks!
Rather than calling Read followed by taking Value property, use ReadElementContentAsString method:
sub_reader.ReadToFollowing("NodeA");
item.NodeA = sub_reader.ReadElementContentAsString();
sub_reader.ReadToFollowing("NodeB");
item.NodeB = sub_reader.ReadElementContentAsString();
sub_reader.ReadToFollowing("NodeC");
item.NodeC = sub_reader.ReadElementContentAsString();
Using XDocument <NodeC/> return string.Empty. Here dotNetFiddle
string xml = #"<Items>
<Item>
<NodeA>Some Value</NodeA>
<NodeB>N</NodeB>
<NodeC />
</Item>
<Item>
<NodeA>Some 2223Value</NodeA>
<NodeB>2223N</NodeB>
<NodeC>12344</NodeC>
</Item>
</Items>";
XDocument doc = XDocument.Parse(xml);
var result = doc.Root.Descendants("NodeC");
foreach(var item in result)
{
Console.WriteLine(item.Value);
}
If you want to deserialize the XDocument to some object you can check this answer: How do I deserialize XML into an object using a constructor that takes an XDocument?
public static MyClass FromXml (XDocument xd)
{
XmlSerializer s = new XmlSerializer(typeof(MyClass));
return (MyClass)s.Deserialize(xd.CreateReader());
}
I have a xml like this :
<?xml version="1.0" encoding="iso-8859-1" ?>
<Menu>
<Group Flow="Horizontal">
<Item Text="Basket" >
<Item Text="Favorites">
<Item Text="Add" Roles="SuperAdmin">
<Item Text="Evaluation" Roles="Admin">
</Item>
<Item Text="Titularisation" Roles="users,Admin">
</Item>
</Item>
</Group>
</Menu>
and I want to retrieve all attributes "Roles".
I tried this :
XElement rootElement = XElement.Load(#"C:\Users\TTM\Desktop\GeneralMenu.xml");
XElement menuElt = rootElement.Element("Group");
var itemsAdd = from el in menuElt.Descendants("Item")
where (string)el.Attribute("Text") == "Add"
select el;
foreach (var item in itemsAdd)
{
Console.WriteLine(item.Attribute("Roles").Value);
}
Console.Read();
but I get only the "Roles" of this balise:
<Item Text="Add" Roles="SuperAdmin">
Try this:-
XDocument xdoc = XDocument.Load(XMLFile);
var itemAdd = xdoc.Descendants("Item")
.Where(x => (string)x.Attribute("Text") == "Add")
.Select(x => new
{
Roles = (string)x.Attribute("Roles"),
ChildRoles = x.Descendants("Item")
.Select(z => (string)z.Attribute("Roles")).ToList()
});
Here, Roles will contain the Roles from your parent node, and ChildRoles will hold all the Roles present in the descendants of parent where Text is Add.
You can then access them using foreach loop:-
foreach (var item in itemAdd)
{
Console.WriteLine(item.Roles);
foreach (var childRoles in item.ChildRoles)
{
Console.WriteLine(childRoles);
}
}
If you want all the roles to be fetched in a single property then you can fetch it like this:-
Roles = x.Descendants("Item").Select(z => (string)z.Attribute("Roles"))
.Concat(new List<string> { (string)x.Attribute("Roles") }).ToList()