Can anyone help on how to process the information within Fields only when the Condition is True ?
I've tried For each and if then but I would like something more graceful.
<?xml version="1.0" encoding="UTF-8" ?>
<root>
<Events>
<Event Id="1">
<Condition>True</Condition>
<Fields>
<Parameter Name="thisOne" Value="1234" />
<Parameter Name="notthisOne" Value="xyz" />
<Parameter Name="thisoneagain" Value="5678" />
<Parameter Name="notthisoneAgain" Value="abc" />
</Fields>
</Event>
<Event Id="2">
<Condition>False</Condition>
<Fields>
<Parameter Name="thisOne" Value="1234" />
<Parameter Name="notthisOne" Value="xyz" />
<Parameter Name="thisoneagain" Value="5678" />
<Parameter Name="notthisoneAgain" Value="abc" />
</Fields>
</Event>
</Events>
</root>
This should do it:
var paramSets = e.Descendants("Event")
.Where(ev => (string)ev.Element("Condition") == "True")
.Select(ev => ev.Descendants("Parameter")
.Select(p => new
{
Name = (string)p.Attribute("Name"),
Value = (string)p.Attribute("Value")
}));
This will select a set of parameters for each Event element where Condition is True. In other words, the type of paramSets is IEnumerable<IEnumerable<T>>, where T is an anonymous type with a Name and Value property.
You can loop through it like this:
foreach (var event in paramSets)
{
foreach (var parameter in event)
{
// Do something with the parameter
Console.WriteLine("Name: {0}, Value: {1}", parameter.Name, parameter.Value);
}
}
Use the Where Clause to restrict the set of data in LINQ to XML.
You can get the value of a particular element by drilling down into the element and calling .Value
This will load all the name and value for all the each parameter that is part of an Event that has a condition element with a value of True:
Dim xdoc As XDocument = XDocument.Parse(str)
Dim parameters = From e In xdoc.Root.Elements("Events").Elements("Event")
From p In e.Elements("Fields").Elements("Parameter")
Where e.Element("Condition").Value = "True"
Select New With {
.Name = p.Attribute("Name").Value,
.Value = p.Attribute("Value").Value
}
Related
My source file is a XML file and it looks like this, but a little bit bigger:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Controller Name="PLC1_0">
<LibSolution>CMG</LibSolution>
<LibName>1756-LxxES</LibName>
<LibVersion></LibVersion>
<Parameter Name="ChassisName">Local</Parameter>
<Parameter Name="Slot">0</Parameter>
<Parameter Name="Size">4</Parameter>
<Parameter Name="SoftwareRevision">31</Parameter>
<Parameter Name="ProcessorType">1756-L83ES</Parameter>
<Parameter Name="ConfigureMotion">1</Parameter>
<Parameter Name="MotionGroupName">MotionGroup1</Parameter>
<!--====================================================================================================================-->
<!-- IO CONFIGURATION -->
<!--====================================================================================================================-->
<IOInstances>
<Instance ModuleName="GEN1" UnitName="CAB1" SubUnitName="KF0907_2">
<LibSolution>CMG</LibSolution>
<LibName>1734-IB4/C</LibName>
<LibVersion></LibVersion>
<ParentModule>CLO1_CAB1_KF0901_1</ParentModule>
<Parameter Name="IP">80</Parameter>
<Parameter Name="Slot">6</Parameter>
<Parameter Name="ProgNameIO">CLX1_IO</Parameter>
</Instance>
<Instance ModuleName="GEN1" UnitName="CAB1" SubUnitName="KF0907_4">
<LibSolution>CMG</LibSolution>
<LibName>1734-IB4/C</LibName>
<LibVersion></LibVersion>
<ParentModule>CLO1_CAB1_KF0901_1</ParentModule>
<Parameter Name="IP">80</Parameter>
<Parameter Name="Slot">7</Parameter>
<Parameter Name="ProgNameIO">CLX1_IO</Parameter>
</Instance>
<Instance ModuleName="GEN1" UnitName="CAB1" SubUnitName="KF0907_5">
<LibSolution>CMG</LibSolution>
<LibName>1734-4IOL/A</LibName>
<LibVersion></LibVersion>
<ParentModule>CLO1_CAB1_KF0901_1</ParentModule>
<Parameter Name="Slot">8</Parameter>
</Instance>
<Instance ModuleName="GEN1" UnitName="CAB1" SubUnitName="KF0910_1">
<LibSolution>CMG</LibSolution>
<LibName>1734-OB4/C</LibName>
<LibVersion></LibVersion>
<ParentModule>CLO1_CAB1_KF0901_1</ParentModule>
<Parameter Name="IP">80</Parameter>
<Parameter Name="Slot">9</Parameter>
<Parameter Name="ProgNameIO">CLX1_IO</Parameter>
</Instance>
</IOInstances>
</Controller>
</root>
My query targets the instances with e.g. a parameter with a special name. The problem is it looks like LinQ doesn't work like common SQL.
This query should return all instances which have a ModuleName=GEN1 and a Parameter --> [Attribute] "Name" --> [Value] "Slot"
IEnumerable<XElement> finalObject = from node in xDocConfig.Descendants("IOInstances").Elements("Instance")
where node.Attribute("ModuleName").Value.Equals("GEN1")
where node.Element("Parameter").Attribute("Name").Value.Equals("Slot")
select node;
Unfortunately it returns only the second last instance of my example XML. Coincidentally, at this instance there are some parameters missing, therefore the parameter "slot" is the first one and got found by my query. If it is the second or third parameter it is not found by the query.
I really don't know how to alter my query to get all 4 instances. I appreciate your help.
Where you have:
where node.Element("Parameter")
that seems to only target the first element that matches the name "Parameter".
I believe there is an alternative method called .Elements() which you will have to use because there are multiple Parameter elements within the Instance node.
It will return an IEnumerable I believe, so you will have to change your logic to check through all the Parameter nodes where the attribute Name equals Slot.
https://learn.microsoft.com/en-us/dotnet/api/system.xml.linq.xcontainer.elements?view=netcore-3.1#System_Xml_Linq_XContainer_Elements_System_Xml_Linq_XName_
Update misunderstood the question, to return actual Instances, do this:
IEnumerable<XElement> finalObject = xDocConfig.Descendants("IOInstances")
.SelectMany(x => x.Elements("Instance").Where(i => i.Attribute("ModuleName").Value.Equals("GEN1")
&&
i.Elements("Parameter").Any(p => p.Attribute("Name").Value.Equals("Slot"))
));
Basically, you need to SelectMany to flatten the nested collections, and Any() to find those that contain an element inside.
Please try the following.
c#
void Main()
{
const string fileName = #"e:\temp\prototype.xml";
XDocument xdoc = XDocument.Load(fileName);
IEnumerable<XElement> finalObject = xdoc.Descendants("IOInstances")
.SelectMany(x => x.Elements("Instance")
.Where(i => i.Attribute("ModuleName").Value.Equals("GEN1")
&&
i.Elements("Parameter").Any(p => p.Attribute("Name").Value.Equals("Slot"))
)
);
finalObject.Dump();
}
#Juharr, your hint guided me to a solution, it was the most helpful and very simpel.
IEnumerable<XElement> finalObject = from node in xDocConfig.Descendants("IOInstances").Elements("Instance")
where node.Attribute("ModuleName").Value.Equals("GEN1")
where node.Elements("Parameter").Any(x => x.Attribute("Name").Value.Equals("Slot"))
select node;
The first line returns an enumeration.
The second line filters this
enumeration 'cause there is only one Attribute with that name.
The third line has to include a "mapping", because there is more than one parameter to check. The lambda expression iterates through all parameters and .Any() returns bool values to the WHERE expression.
Thx to all of you, I appreciate your efforts.
I am using c sharp 5.0 and using LINQ to XML I try to programmatically get the differences between 2 xml files
my first files is as simple as :
<?xml version="1.0" encoding="UTF-8" ?>
<table name="MYTABLE">
<columns>
<column name="MY_ID" type="NUMBER"/>
<column name="M_NAME" type="VARCHAR2" />
<column name="MY_PARENT_ID" type="VARCHAR2" />
<column name="MY_CAPTION" type="VARCHAR2" />
</columns>
</table>
and my second xml file is as simple as :
<?xml version="1.0" encoding="UTF-8" ?>
<table name=" MYTABLE ">
<columns>
<column name="MY_ID" type="NUMBER"/>
<column name="M_NAME" type="VARCHAR2" />
<column name="MY_PARENT_ID" type="NUMBER" />
</columns>
</table>
and my code is as simple as :
XDocument doc1 = XDocument.Load(#"Path\table1.xml");
XDocument doc2 = XDocument.Load(#"Path\table2.xml");
var cols1 = doc1.Descendants("column") ;
var cols2 = doc2.Descendants("column") ;
var itemsOnFirstFileOnly = cols1.Except(cols2);
var itemsOnSecondFileOnly = cols2.Except(cols1);
but to my surprise itemsOnFirstFileOnly returned ALL of columns that exist on table1.xml (not only MY_PARENT_ID & MY_CAPTION ) and itemsOnSecondFileOnly returned ALL of the columns that exist in table2.xml (not only MY_PARENT_ID),so why not Except doesn’t work as expected ??
You have to implement Equals on your Column-class so that Except knows when two Columns are equal:
class Column
{
bool Equals(object other)
{
// ...
// skip further checks for simplicity
// ...
Column o = (Column) other;
return o.Name == this.Name && o.Type == this.Type ;
}
}
If you do not implement this method Except will just check for reference-equality which can never be true because your columns come from differenct contexts (files in your case).
EDIT: As you are comparing XNodes the thing is much easier because you can use the XNodeEqualityComparer-class which has its own Equals-implementation that exactly fit your needs as seen here. Thus the following should work:
var itemsOnFirstFileOnly = cols1.Except(cols2, new XNodeEqualityComparer());
You can do this using Linq:
var itemsOnFirstFileOnly = cols1.Where(x => !cols2.Select(y => y.Attribute("name").Value).Contains(x.Attribute("name").Value));
var itemsOnSecondFileOnly = cols2.Where(x => !cols1.Select(y => y.Attribute("name").Value).Contains(x.Attribute("name").Value));
Basically in the above code:
Selects the value of the name attributes
Compares these names
Returns the XElement instances where the name is missing in the other collection.
You can also convert the XElements to strings, which takes attributes into account:
XDocument doc = XDocument.Parse(xml);
XDocument doc2 = XDocument.Parse(xml2);
var diffs = doc.Descendants().Select(e => e.ToString())
.Except(doc2.Descendants().Select(e => e.ToString()));
foreach (var e in diffs)
{
Console.WriteLine(e);
Console.WriteLine();
}
Considering columns, this returns only
<column name="MY_PARENT_ID" type="VARCHAR2" />
<column name="MY_CAPTION" type="VARCHAR2" />
I have an xml data given below. I need to check whether an employee of empName="John" exists in Production department. If exists update the salary otherwise add an employee to the department.
<Company>
<Company Name="ABCDEF" />
<Place="AKR" />
<Production>
<employee empName="John" empId="E11" salary="1000" />
<employee empName="Ivan" empId="E12" salary="3000" />
<employee empName="Paul" empId="E13" salary="1200" />
</Production>
<Marketing>
<employee empName="Keith" empId="EMP11" />
<employee empName="Christina" empId="EMP12" />
</Marketing>
</Company>
I need to check particular node exist in this data using c# linq?
Correct your XML first,
<Company>
<Company Name="ABCDEF" />
<Production>
<employee empName="John" empId="E11" salary="1000" />
<employee empName="Ivan" empId="E12" salary="3000" />
<employee empName="Paul" empId="E13" salary="1200" />
</Production>
<Marketing>
<employee empName="Keith" empId="EMP11" />
<employee empName="Christina" empId="EMP12" />
</Marketing>
</Company>
you can try like this
string filePaths = "XMLFile1.xml";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(filePaths);
XmlNodeList elements = xmlDoc.GetElementsByTagName("employee");
Boolean found = false;
foreach (XmlElement element in elements)
{
if (element.GetAttribute("empName") == "John")
{
found = true;
break;
}
}
Your XML is invalid; you cannot have a node like <Place="AKR" />
But, after changing it into something valid, you could try using this LINQ statement:
XDocument root = XDocument.Parse(File.ReadAllText("xml.xml"));
IEnumerable<XElement> production = root.Root
.Descendants("Production")
.Where(x => x.Elements("employee")
.Where(e => e.Attribute("empName").Value.Equals("John"))
.Any()
);
if (production.Any())
{
Console.WriteLine("John found...");
}
else
{
Console.WriteLine("No John found");
}
try this:
XmlNode node = xmlDoc.SelectSingleNode(NodeName);
n the Below xml,
<?xml version="1.0" encoding="utf-8"?>
<Models>
<Model DESC="HONDA-BLUE-450" MODELID="XX12-01" Status="in" />
<Model DESC="VOLKS--RED-345" MODELID="XY12-01" Status="in" />
<Model DESC="BENZ-BLUE-550" MODELID="XX12-01" Status="in" />
<Model DESC="VOLKS--CYAN-345" MODELID="BG12-01" Status="out" />
<Model DESC="HONDA-GREEN-234" MODELID="AX12-01" Status="in" />
<Model DESC="VOLKS-Yellow-765" MODELID="XY11-01" Status="in" />
<Model DESC="HONDA-GREEN-109" MODELID="AC12-01" Status="in" />
<Model DESC="AUDI-GREEN-984" MODELID="AF42-01" Status="in" />
<Model DESC="VOLKS-Yellow-565" MODELID="XJ11-61" Status="out" />
<Model DESC="HONDA-WHITE-079" MODELID="DC12-38" Status="in" />
<Model DESC="TOYOTA-GREY-109" MODELID="UC12-81" Status="in" />
<Model DESC="AUDI-CYAN-984" MODELID="LF42-41" Status="in" />
<Model DESC="VOLKS-WHITE-865" MODELID="LK11-61" Status="out" />
<Model DESC="HONDA-GREY-039" MODELID="PC12-31" Status="in" />
</Models>
I want to get all the MODELID values whose status is out and DESC starts with VOLKS. Any help would be appreciated.
You can use linq to xml to go through xml nodes and String.StartsWith to check if the atttribute DESC value starts with "VOLKS":
XDocument xdoc = XDocument.Load("data.xml");
var modelIds= from c in xdoc.Descendants("Model")
where c.Attribute("DESC").Value.StartsWith("VOLKS") &&
c.Attribute("Status").Value == "out"
select c.Attribute("MODELID").Value;
Or using a Regex to have SQL LIKE:
XDocument xdoc = XDocument.Load("data.xml"));
var modelIds= from c in xdoc.Descendants("Model")
where c.Attribute("DESC").Value.Like("VOLKS%") &&
c.Attribute("Status").Value == "out"
select c.Attribute("MODELID").Value;
Using a regular expression for LIKE:
public static class MyStringExtensions
{
public static bool Like(this string toSearch, string toFind)
{
return new Regex(#"\A" + new Regex(#"\.|\$|\^|\{|\[|\(|\||\)|\*|\+|\?|\\").Replace(toFind, ch = > #"\" + ch).Replace('_', '.').Replace(" % ", ".*") + #"\
z ", RegexOptions.Singleline).IsMatch(toSearch);
}
}
LIKE source
You didn't mention whether LINQ is ok. Here is a method that uses LINQ, foreach, and XPATH. (to enhance the coverage Mihai already posted).
static void Main(string[] args)
{
XmlDocument xd = new XmlDocument();
xd.Load("Models.xml");
var result = from XmlNode row in xd.DocumentElement.SelectNodes("//Model[starts-with(#DESC, 'VOLKS') and #Status='out']") where row != null select row;
foreach (var x in result)
{
XmlAttributeCollection attributes = x.Attributes;
Console.Write("<Model ");
foreach (XmlAttribute attribute in x.Attributes)
{
Console.Write("{0}=\"{1}\" ", attribute.Name, attribute.Value);
}
Console.WriteLine(" />");
}
}
A Linq to XML solution with lambdas:
var query = XDocument.Load("data.xml").Root.Elements("Model")
.Where(m => m.Attribute("Status").Value == "out" && m.Attribute("DESC").Value.StartsWith("VOLKS"))
.Select(m => m.Attribute("MODELID").Value);
foreach (var v in query)
Console.WriteLine(v);
// output:
// BG12-01
// XJ11-61
// LK11-61
I have an XML which follow the following structure:
<model uir="model1">
<profile>
<profiles>
<timeseries>
<value double="24.7" index="2011-01-01 00:00" />
<value double="24.5" index="2011-01-02 00:00" />
<value index="2011-04-23 00:00" /> //Here for some reason i must have double=null
<value index="2011-04-24 00:00" />
<value double="24.7" index="2011-01-01 00:00" />
<value double="24.5" index="2011-01-02 00:00" />
</timeseries>
</profile>
</profiles>
</model>
<model uir="model2">
<profile>
<profiles>
<timeseries>
<value double="24.7" index="2011-01-01 00:00" />
<value double="24.5" index="2011-01-02 00:00" />
<value index="2011-04-23 00:00" /> //Here for some reason i must have double=null
<value index="2011-04-24 00:00" />
<value double="24.7" index="2011-01-01 00:00" />
<value double="24.5" index="2011-01-02 00:00" />
</timeseries>
</profile>
</profiles>
</model>
This that i want is to take the value of the attribute double and to store it in a list (or vector) for each model. When the node value hasn't an attribute double to insert a null. In first level i tried the following but when it reaches in a node with no attribute double stack.
using System;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
class MyClass
{
static void Main(string[] args)
{
string file = #"C:\RDMS.xml";
XDocment doc = XDocument.Load(file);
foreach (var item in doc.Descendants("value"))
{
Console.WriteLine(item.Attribute("double").Value);
}
Console.ReadKey();
}
}
You did not specify the error, but I am guessing you are getting a NullReferenceException? You need to check if item.Attribute actually returns a value.
var doubleAttr = item.Attribute("double");
if(doubleAttr == null) continue;
Console.WriteLine(item.Attribute("double").Value);
If that is not your problem, then please be more specific as to what your problem is.
Further clarification from trope's comment below:
...you are getting a NullReferenceException because nothing returns from item.Attribute("double") for the elements that lack that attribute, therefore you cannot then call .Value, as item.Attribute("double") is null, and you receive a NullReferenceException. This does not happen with your index attribute, because all of your "value" elements have index attributes...
You can check the result of Attribute(string) if it is null:
var attrib = item.Attribute("double");
if (attrib != null)
Console.WriteLine(attrib.Value);
else
Console.WriteLine("attribute not found!");