Finding a specific xml element containt - c#

Ihave a large xml file and I want to get child element value by giving the parent child element value, I am new in xml file please any help here is my xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<masterController> <uuid>XXXXXXXXXXXXXXXXXXXXXXXXX</uuid>
<channels>
<channel>
<nodeGroups>
<nodeGroup>
<analogNode>
<typeCode>8</typeCode>
<id>1</id>
<sdos>
<sdo>
<description>Host ID</description>
<compareLevel>Ignore</compareLevel>
<datafield xmlns:xsi="http://www.XXXXX.XXXX/XXXXX/XMLSchema-instance"
xsi:type="intField">
<description>Host ID</description>
<compareLevel>Ignore</compareLevel>
<offset>2</offset>
<size>1</size>
<readonly>true</readonly>
<isMappedToPdo>false</isMappedToPdo>
<ownerNodeSerial>12102904</ownerNodeSerial>
<ownerSdoIndex>3</ownerSdoIndex>
<data xsi:type="intData">
<value xmlns:xs="http://www.XX.CC/2XXX/XMLSchema" xsi:type="xs:int">2</value>
<unit></unit>
<min>1</min>
<max>15</max>
</data>
<intValue>2</intValue>
</datafield>
<index>3</index>
<totalbytes>3</totalbytes>
</sdo>
<sdo>
<description>Host ID</description>
<compareLevel>Ignore</compareLevel>
<datafield xmlns:xsi="http://www.XXXXX.XXXX/XXXXX/XMLSchema-instance"
xsi:type="intField">
<description>Host ID</description>
<compareLevel>Ignore</compareLevel>
<offset>2</offset>
<size>1</size>
<readonly>true</readonly>
<isMappedToPdo>false</isMappedToPdo>
<ownerNodeSerial>12102905</ownerNodeSerial>
<ownerSdoIndex>4</ownerSdoIndex>
<data xsi:type="intData">
<value xmlns:xs="http://www.XX.CC/2XXX/XMLSchema" xsi:type="xs:int">16</value>
<unit></unit>
<min>1</min>
<max>15</max>
</data>
<intValue>2</intValue>
</datafield>
<index>3</index>
<totalbytes>3</totalbytes>
</sdo>
</sdos>
</analogNode>
</nodeGroup>
</nodeGroups>
</channel> </channels> </masterController>
I' am trying this but am not geting anything:
XElement root = XElement.Load(Server.MapPath("sample.xml"));
IEnumerable<XElement> masterco = from el in root.Elements("sdo") where (from add in el.Elements("datafield")
where
(string)add.Element("ownerNodeSerial") == TextBox1.Text &&
(string)add.Element("ownerSdoIndex") == TextBox1.Text
select add)
.Any()
select el;
foreach (XElement el in masterco)
{
TextBox3.Text = (string)el.Element("value");
}
I want to get this:
<value xmlns:xs="http://www.XX.CC/2XXX/XMLSchema" xsi:type="xs:int">16</value>
and be able to update it.

There is one major error in your query:
You are using Elements on root, but you are looking for the tag sdo which is not a direct child of the root tag. You have to use Descendants instead.
Additionally, I think you want to have an OR instead of an AND regarding the text of TextBox1.
Fix it:
var masterco = from el in root.Descendants("sdo")
where (from add in el.Elements("datafield")
where
(string)add.Element("ownerNodeSerial") == TextBox1.Text ||
(string)add.Element("ownerSdoIndex") == TextBox1.Text
select add).Any()
select el;
To actually get the value you want, you should use a different query. There is really no need to select the sdo tag at all.
var value = root.Descendants("datafield")
.Where(x => (string)x.Element("ownerNodeSerial") == TextBox1.Text ||
(string)x.Element("ownerSdoIndex") == TextBox1.Text)
.Select(x => (string)x.Element("data").Element("value"))
.Single();
TextBox3.Text = value;
You can see that I am assuming that in the whole XML document only one matching datafield/data/value entry exists. I derive that information from the way you update your textbox. This would make no sense if there would be multiple tags - the values would overwrite each other in the text box.

Related

c# Find element/s with a specific child element

Sample
The sample code finds the attribute names of the elements whose children elements'values match the value entered in a texbox
Now look at my code and XMLfile and you'll see that they're identical to the above link. The problem is that my code only prints the first group that matches the text box value.
XML
<?xml version="1.0" encoding="utf-8"?>
<groups>
<group name="a">
<ip>10.3.4</ip>
<ip>10.1.4</ip>
</group>
<group name="b">
<ip>10.2.1</ip>
<ip>10.3.4</ip>
<ip>10.55.55</ip>
</group>
</groups>
Code
XElement root = XElement.Load("c:\etc);
IEnumerable<XElement> tests =
from el in root.Elements("group")
where (string)el.Element("ip") == textBox1.Text
select el;
foreach (XElement el in tests)
Console.WriteLine((string)el.Attribute("name"));
The problem is in the where clause. Because if i comment it, the system will print both group names but when the where clause is active, it's always only returning 1 group. might as well be using FirstOrDefault() -_-
var tests =
from el in root.Elements("group")
where el.Elements("ip").Any(o => o.Value == textBox1.Text)
select el;
Try this code:
XElement root = XElement.Load(#"your path to a file");
//set text box to some default value to test if function will work
textBox1.Text = "10.1.4";
//here I used etension method, commented is alternative version, for better understanding
IEnumerable<XElement> tests = root.Elements("group").Where(gr => gr.Elements("ip").Any(ip => ip.Value == textBox1.Text));
//IEnumerable<XElement> tests = root.Elements("group").Where(gr => gr.Elements("ip").Where(ip => ip.Value == textBox1.Text).Count() > 0);
foreach (XElement el in tests)
//Console.WriteLine((string)el.Attribute("name"));
MessageBox.Show((string)el.Attribute("name"));
Your code didn't work, as you compared single element against the text of a text box. What you want is to check if any among the ip elements is equal to specified text.
In both groups you have the same ip
<ip> 10.3.4 </ ip>
In this case it will even bring both groups together.
I'd advise doing one more condition by checking the 'name'.
var xml = XDocument.Parse(xmlString);
// a, b
string[] matchingNames = xml.Root.Elements("group")
.Where(g => g.Elements("ip").Any(e => e.Value == textBoxText))
.Select(g => g.Attribute("name").Value).ToArray();
Select the attribute name from groups where any of the child ip elements contain the text box text.

Formatting an elements text using descendants of XElement

Im wondering how to select a specific element from the heirarchy so that I can format its text.
In the example below I would like to format the specific element to remove the time portion of the date, but I am also looking for a way to format any of the elements for example to add a currency symbol to the text between each price tag.
My example
<orders>
<order>
<type> tools </type> //I would like the ability to select this element
<text> screwdriver </text>
<id> 100981 </id>
<price> 5.00 </price>
<date> 01/01/15 12:51:36 </date>
</order>
<order>
<type> uniform </type>
<text> boots </text>
<id> 100546 </id>
<price> 25.00 </price>
<date> 12/01/15 15:30:41 </date>
</order>
</orders>
What I have so far
foreach (XElement element in doc.Descendants())
{
var nodes = element.Nodes().Where(p => p.NodeType == XmlNodeType.Text);
foreach (XText node in nodes)
{
node.Value = FirstLetterToUpper( node.Value );// set the first letter of each output to be uppercase
}
}
What I have tried
foreach (XElement element in doc.Descendants())
{
var nodes = element.Nodes().Where(p => p.NodeType == XmlNodeType.Text);
if( element.Descendants() == element.Element("date"))
{
element.Value = Convert.ToDateTime(element.Value).ToShortDateString();
}
foreach (XText node in nodes)
{
node.Value = FirstLetterToUpper( node.Value );
}
}
I have some XML experience but have never worked with XElement before.
I've been searching SO for a while now but cant find what Im looking for. The answers below are some of the suggested answers from typing this question but they dont provide a solution as the XML elements are generated dynamically in a loop.
XElement node with text node has its formatting ignored
string.Format in XElement not formatting
Any help with this would be great as I have not attempted this before. Thanks.
You can get the text node's parent using Parent property and check it's name:
foreach (XText node in doc.DescendantNodes()
.Where(x => NodeType == XmlNodeType.Text))
{
if(node.Parent.Name == "date") { ... }
if(node.Parent.Name == "price") { ... }
}
BTW, don't forget the save the document using XDocument.Save method after you made the changes.
I usually create one class to represent the xml and send yo the instance of object with correct type. for Example convert.ToDecimal(node.innerText)

Before inserting new element in xml check if exists the value

I have below a xml file with the below format:
<?xml version="1.0" encoding="utf-8" ?>
<Root>
<Countries>
<country>India</country>
<country>USA</country>
<country>UK</country>
</Countries>
</Root>
string newCountry="UAE"
I want to insert this "UAE" country to the above xml file, before that I want to check whether "UAE" is already exists in the xml. If not exists then only want to insert otherwise no operation. How can I do this?
Like this:
XDocument xml = XDocument.Load("path_to_file");
string newCountry = "UAE";
XElement countries = xml.Descendants("Countries").First();
XElement el = countries.Elements().FirstOrDefault(x => x.Value == newCountry);
if (el == null)
{
el = new XElement("country");
el.Value = newCountry;
countries.Add(el);
}
//Console.WriteLine(countries.ToString());
The easiest way would probably be to read the xml into C# objects, check for the existance of UAE, potentially add it, and write the objects back to XML.

C# Linq XML, check for specific value and parse to array

I have the following XML:
<?xml version="1.0" ?>
<NewDataSet>
<Data>
<ElementDefinition>
<ID>1</ID>
<QUANTITY>0</QUANTITY>
</ElementDefinition>
<ElementDefinition>
<ID>2</ID>
<QUANTITY>1</QUANTITY>
</ElementDefinition>
</Data>
</NewDataSet>
I need to create an array which contains all ElementDefinitions which contain a QUANTITY element with a value other then 0.
I tried:
var f = XDocument.Load(path);
var xe = f.Root.Elements("QUANTITY").Where(x => x.Value != "0").ToArray();
But that doesn't seem to work. With the above XML the array should contain 1 item, but it stays 0.
After that I need to create a string for each ElementDefinition in the array, the string must contain the value of the corresponding ID element.
For that I tried:
foreach (string x in xe)
{
string ID = //not sure what to do here
}
You want something like this:
var ids = f.Root.Descendants("ElementDefinition")
.Where(x => x.Element("QUANTITY").Value != "0")
.Select(x => x.Element("ID").Value);
As you want the ID, it is not very helpful to select all QUANTITY nodes. Instead, select exactly what you specified in your question:
All ElementDefinitions (Descendants("ElementDefinition")), that have a QUANTITY with a Value other than 0 (Where(x => x.Element("QUANTITY").Value != "0"). From the resulting nodes, select the ID (Select(x => x.Element("ID").Value)).
Yo can replace with
var xe = f.Root.Elements("Data/ElementDefinition/QUANTITY").Where(x => x.Value != "0").ToArray();

Modify Node Value C# with ID

Here is my XML :
<?xml version="1.0" encoding="utf-8" ?>
<Selection>
<ID>1</ID>
<Nom>Name 1</Nom>
<DateReference>0</DateReference>
<PrefixeMedia>Department</PrefixeMedia>
<FormatExport>1630</FormatExport>
<TraceAuto>Oui</TraceAuto>
<SubID></SubID>
</Selection>
<Selection>
<ID>2</ID>
<Nom>Name 1</Nom>
<DateReference>0</DateReference>
<PrefixeMedia>Department</PrefixeMedia>
<FormatExport>1630</FormatExport>
<TraceAuto>1</TraceAuto>
<SubID>1</SubID>
</Selection>
My problem is I would like to modify for example the node content of <Nom>Name 1</Nom> which is located in <Selection></Selection> which have <ID>1</ID> (Search by ID)
I'm using XElement and XDocument to do simple search but I need some help to solve this problem above. (Developpment on SilverLight
Best Regards.
Another way to do this is using XmlDocument:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(#"\path\to\file.xml");
// Select the <nom> node under the <Selection> node which has <ID> of '1'
XmlNode name = xmlDoc.SelectSingleNode("/Selection[ID='1']/Nom");
// Modify the value of the node
name.InnerText = "New Name 1";
// Save the XML document
xmlDoc.Save(#"\path\to\file.xml");
If you don't know how to get at the correct <Nom> node to update, the trick is to first select a <Selection> node that contains the correct <ID> node, then you can get that <Nom> node.
Something like:
XElement tree = <your XML>;
XElement selection = tree.Descendants("Selection")
.Where(n => n.Descendants("ID").First().Value == "1") // search for <ID>1</ID>
.FirstOrDefault();
if (selection != null)
{
XElement nom = selection.Descendants("Nom").First();
nom.Value = "Name one";
}
Note 1: By using Descendants("ID").First() I expect every Selection node to contain an ID node.
Note 2: And every Selection node contains a Nom node
Note 3: Now you still have to store the whole XML, if that's what you need.

Categories

Resources