Check an element existing or not in xml file - c#

I have a xml file.
<?xml version="1.0"?>
<RCATS xsi:noNamespaceSchemaLocation="/opt/radical/xml/schemas/RcatsExternalInterface.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<IDENTIFICATION-RECORD ACTION="ADD">
<ID>1200020100</ID>
<TRANSACTION-ID>3r7we43556564c6r34vl6z)zM6KF8i</TRANSACTION-ID>
<LAST-NAME>GEORGE</LAST-NAME>
<FIRST-NAME>BUSH</FIRST-NAME>
<MIDDLE-NAME>W</MIDDLE-NAME>
</IDENTIFICATION-RECORD>
</RCATS>
Then I have C# code to parse it.
XDocument doc = XDocument.Load(fileName);
var a = from x in doc.Descendants()
select x;
var d = from x in a
where x.Name.LocalName == "IDENTIFICATION-RECORD"
select x;
foreach (var i in d)
{
y = where x.Name.LocalName == "DISPOSITION"
select x).First().Value.ToLower() == "active" ? true : false;
The thing is sometimes there is no "DISPOSITION" element, in this case, I want
y = true; // if no "DISPOSITION" element found in file
Otherwise, keep the original code there if "DISPOSITION" is there.
How to check it?

This should do the work.
XDocument doc = XDocument.Load("test.xml");
var a = from x in doc.Descendants()
select x;
var d = from x in a
where x.Name.LocalName == "IDENTIFICATION-RECORD"
select x;
foreach (var i in d)
{
var disp = i.Element("DISPOSITION");
var y = disp == null ? true : (disp.Value.ToLower() == "active" ? true : false);
}

This worked for me:
foreach (XElement element in doc.Descendants().
Where(x=>x.Name.LocalName=="RCATS").
Descendants().
Where(y=>y.Name.LocalName=="IDENTIFICATION-RECORD"))
{
foreach (XElement node in element.Descendants())
{
if (node.Name.LocalName == "DISPOSITION")
if (node.Value == "ACTIVE")
Console.Write("Disposition exists and is true");
}
}

Related

Linq to XML, simpler way to select element with highest depth level in hierarchy

I am trying to select the element with the highest depth (most nested) in hierarchy.
var elems = my_list.Elements()
.Where(x => x.Attribute("Name") != null && x.Attribute("Name").Value == "John")
;
Is there a simpler method than this one, together with filtering?
XElement elem2 = null;
int i = 0;
foreach (var elem in elems)
{
var depth = elem.AncestorsAndSelf().Count();
if(depth >= i)
{
i = depth;
elem2 = elem;
}
}
You can use MaxBy() (either from .NET 6 or from the MoreLinq package) along with your ancestor counting:
// Note the change to use Descendants, as otherwise only direct
// children will be returned, which will all have the same "level"
var element = list.Descendants()
.Where(x => (string) x.Attribute("Name") == "John")
.MaxBy(x => x.AncestorsAndSelf().Count());

How to find out duplicate Elements in Xelement

I am trying to find out the duplicate Elements in XElement , and make a generic function to remove duplicates .Something like:
public List<Xelement>RemoveDuplicatesFromXml(List<Xelement> xele)
{ // pass the Xelement List in the Argument and get the List back , after deleting the duplicate entries.
return xele;
}
the xml is as follows:
<Execute ID="7300" Attrib1="xyz" Attrib2="abc" Attrib3="mno" Attrib4="pqr" Attrib5="BCD" />
<Execute ID="7301" Attrib1="xyz" Attrib2="abc" Attrib3="mno" Attrib4="pqr" Attrib5="BCD" />
<Execute ID="7302" Attrib1="xyz1" Attrib2="abc" Attrib3="mno" Attrib4="pqr" Attrib5="BCD" />
I want get duplicates on every attribute excluding ID ,and then delete the one having lesser ID.
Thanks,
You can implement custom IEqualityComparer for this task
class XComparer : IEqualityComparer<XElement>
{
public IList<string> _exceptions;
public XComparer(params string[] exceptions)
{
_exceptions = new List<string>(exceptions);
}
public bool Equals(XElement a, XElement b)
{
var attA = a.Attributes().ToList();
var attB = b.Attributes().ToList();
var setA = AttributeNames(attA);
var setB = AttributeNames(attB);
if (!setA.SetEquals(setB))
{
return false;
}
foreach (var e in setA)
{
var xa = attA.First(x => x.Name.LocalName == e);
var xb = attB.First(x => x.Name.LocalName == e);
if (xa.Value == null && xb.Value == null)
continue;
if (xa.Value == null || xb.Value == null)
return false;
if (!xa.Value.Equals(xb.Value))
{
return false;
}
}
return true;
}
private HashSet<string> AttributeNames(IList<XAttribute> e)
{
return new HashSet<string>(e.Select(x =>x.Name.LocalName).Except(_exceptions));
}
public int GetHashCode(XElement e)
{
var h = 0;
var atts = e.Attributes().ToList();
var names = AttributeNames(atts);
foreach (var a in names)
{
var xa = atts.First(x => x.Name.LocalName == a);
if (xa.Value != null)
{
h = h ^ xa.Value.GetHashCode();
}
}
return h;
}
}
Usage:
var comp = new XComparer("ID");
var distXEle = xele.Distinct(comp);
Please note that IEqualityComparer implementation in this answer only compare LocalName and doesn't take namespace into considerataion. If you have element with duplicate local name attribute, then this implementation will take the first one.
You can see the demo here : https://dotnetfiddle.net/w2DteS
Edit
If you want to
delete the one having lesser ID
It means you want the largest ID, then you can chain the .Distinct call with .Select.
var comp = new XComparer("ID");
var distXEle = xele
.Distinct(comp)
.Select(z => xele
.Where(a => comp.Equals(z, a))
.OrderByDescending(a => int.Parse(a.Attribute("ID").Value))
.First()
);
It will guarantee that you get the element with largest ID.
Use Linq GroupBy
var doc = XDocument.Parse(yourXmlString);
var groups = doc.Root
.Elements()
.GroupBy(element => new
{
Attrib1 = element.Attribute("Attrib1").Value,
Attrib2 = element.Attribute("Attrib2").Value,
Attrib3 = element.Attribute("Attrib3").Value,
Attrib4 = element.Attribute("Attrib4").Value,
Attrib5 = element.Attribute("Attrib5").Value
});
var duplicates = group1.SelectMany(group =>
{
if(group.Count() == 1) // remove this if you want only duplicates
{
return group;
}
int minId = group.Min(element => int.Parse(element.Attribute("ID").Value));
return group.Where(element => int.Parse(element.Attribute("ID").Value) > minId);
});
Solution above will remove elements with lesser ID which have duplicates by attributes.
If you want return only elements which have duplicates then remove if fork from last lambda

How iterate on a Jdom Element that contains a list of node?

I am pretty new in XML parsing in Java using org.jdom.** and I don't know C#.
In this time I have to translate some method from C# to Java and I have the following problem.
In C# I have something like this:
System.Xml.XmlNodeList nodeList;
nodeList = _document.SelectNodes("//root/settings/process-filters/process");
if (nodeList == null || nodeList.Count == 0) {
return risultato;
}
Objects.MyItem o;
foreach (System.Xml.XmlNode n in nodeList){
o = new Objects.MyItem(n.ChildNodes[1].InnerText, n.ChildNodes[0].InnerText);
risultato.Add(o);
}
And I have translate it in Java in this way:
public List<ProcessiDaEscludere> getProcessiDaEscludere() {
Vector<ProcessiDaEscludere> risultato = new Vector<ProcessiDaEscludere>();
Element nodeList;
XPath xPath;
try {
// Query XPath che accede alla root del tag <process-filters>:
xPath = XPath.newInstance("//root/settings/process-filters/process");
nodeList = (Element) xPath.selectSingleNode(CONFIG_DOCUMENT);
if (nodeList == null || nodeList.getChildren().size() == 0)
return risultato;
ProcessiDaEscludere processoDaEscludere = new ProcessiDaEscludere();
}catch (JDOMException e){
}
return risultato;
}
My problem is that now I have no idea about how iterate on the Element nodeList variable as do in C# by these lines:
foreach (System.Xml.XmlNode n in nodeList){
o = new Objects.MyItem(n.ChildNodes[1].InnerText, n.ChildNodes[0].InnerText);
risultato.Add(o);
}
Someone can help me?

C# XML Parsing- find position of an element and read next elements

Hi I have a sample xml as follows
<ROOTELEMENT>
<RECORDSET>
<ROW><VALUE>AAA</VALUE></ROW>
<ROW><VALUE>0</VALUE></ROW>
<ROW><VALUE>00</VALUE></ROW>
<ROW><VALUE>BBB</VALUE></ROW>
<ROW><VALUE>1</VALUE></ROW>
<ROW><VALUE>2</VALUE></ROW>
<ROW><VALUE>CCC</VALUE></ROW>
<ROW><VALUE>3</VALUE></ROW>
<ROW><VALUE>30</VALUE></ROW>
</RECORDSET>
<RECORDSET>
<ROW><VALUE>DDD</VALUE></ROW>
<ROW><VALUE>4</VALUE></ROW>
<ROW><VALUE>40</VALUE></ROW>
<ROW><VALUE>EEE</VALUE></ROW>
<ROW><VALUE>5</VALUE></ROW>
<ROW><VALUE>6</VALUE></ROW>
<ROW><VALUE>FFF</VALUE></ROW>
<ROW><VALUE>7</VALUE></ROW>
<ROW><VALUE>70</VALUE></ROW>
</RECORDSET>
</ROOTELEMENT>
I have to get the position of particular ROW with some VALUE. After that, I have to read the VALUE of a speicifed number of ROWs from that position onwards.
Ex: If i give some value as 'BBB', for this i have to get the next two values '1' and '2'.If i give some value as 'FFF', for this i have to get the next two values '7' and '70'.
I am using .Net framework2.0. I can not use LINQ. Please help me.
You can use the code below. It iterates through the nodes and stores the values you expect in foundValues
string valueToFind = "FFF";
string xml = #"<ROOTELEMENT>
<RECORDSET>
<ROW><VALUE>AAA</VALUE></ROW>
<ROW><VALUE>0</VALUE></ROW>
<ROW><VALUE>00</VALUE></ROW>
<ROW><VALUE>BBB</VALUE></ROW>
<ROW><VALUE>1</VALUE></ROW>
<ROW><VALUE>2</VALUE></ROW>
<ROW><VALUE>CCC</VALUE></ROW>
<ROW><VALUE>3</VALUE></ROW>
<ROW><VALUE>30</VALUE></ROW>
</RECORDSET>
<RECORDSET>
<ROW><VALUE>DDD</VALUE></ROW>
<ROW><VALUE>4</VALUE></ROW>
<ROW><VALUE>40</VALUE></ROW>
<ROW><VALUE>EEE</VALUE></ROW>
<ROW><VALUE>5</VALUE></ROW>
<ROW><VALUE>6</VALUE></ROW>
<ROW><VALUE>FFF</VALUE></ROW>
<ROW><VALUE>7</VALUE></ROW>
<ROW><VALUE>70</VALUE></ROW>
</RECORDSET>
</ROOTELEMENT>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
int count = 0;
List<string> foundValues = new List<string>();
foreach (XmlNode root in doc.ChildNodes)
foreach (XmlNode recorset in root.ChildNodes)
foreach (XmlNode row in recorset.ChildNodes)
foreach (XmlNode value in row.ChildNodes)
{
if (value.InnerText == valueToFind || count == 1 || count == 2)
{
if (count == 1 || count == 2)
foundValues.Add(value.InnerText);
count++;
}
}
Bit late, but here is a Linq to XML alternative:
private static string getXML()
{
return #"<ROOTELEMENT>
<RECORDSET>
<ROW><VALUE>AAA</VALUE></ROW>
<ROW><VALUE>0</VALUE></ROW>
<ROW><VALUE>00</VALUE></ROW>
<ROW><VALUE>BBB</VALUE></ROW>
<ROW><VALUE>1</VALUE></ROW>
<ROW><VALUE>2</VALUE></ROW>
<ROW><VALUE>CCC</VALUE></ROW>
<ROW><VALUE>3</VALUE></ROW>
<ROW><VALUE>30</VALUE></ROW>
</RECORDSET>
<RECORDSET>
<ROW><VALUE>DDD</VALUE></ROW>
<ROW><VALUE>4</VALUE></ROW>
<ROW><VALUE>40</VALUE></ROW>
<ROW><VALUE>EEE</VALUE></ROW>
<ROW><VALUE>5</VALUE></ROW>
<ROW><VALUE>6</VALUE></ROW>
<ROW><VALUE>FFF</VALUE></ROW>
<ROW><VALUE>7</VALUE></ROW>
<ROW><VALUE>70</VALUE></ROW>
</RECORDSET>
</ROOTELEMENT>";
}
private static void parseXML()
{
var xmlString = getXML();
var xml = XDocument.Parse(xmlString);
var values = xml.Descendants("VALUE");
var groups = values.Select((value, index) => new
{
Index = index,
Value = value
})
.GroupBy(x => x.Index / 3)
.Select(g => new Tuple<XElement, XElement, XElement>(g.ElementAt(0).Value,
g.ElementAt(1).Value,
g.ElementAt(2).Value));
}

Building the 'where' clause in a Linq query

In this query, I always want the 'normal' type element.
If the _includeX flag is set, I want the 'workspace' type elements, too.
Is there a way to write this as one query? Or build the where clause based on _includeX before submitting the query?
if (_includeX) {
query = from xElem in doc.Descendants(_xString)
let typeAttributeValue = xElem.Attribute(_typeAttributeName).Value
where typeAttributeValue == _sWorkspace ||
typeAttributeValue == _sNormal
select new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
};
}
else {
query = from xElem in doc.Descendants(_xString)
where xElem.Attribute(_typeAttributeName).Value == _sNormal
select new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
};
}
You can break it into a separate predicate:
Predicate<string> selector = x=> _includeX
? x == _sWorkspace || x == _sNormal
: x == _sNormal;
query = from xElem in doc.Descendants(_xString)
where selector(xElem.Attribute(_typeAttributeName).Value)
select new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
};
Or inline the condition:
query = from xElem in doc.Descendants(_xString)
let typeAttributeValue = xElem.Attribute(_typeAttributeName).Value
where (typeAttributeValue == _sWorkspace && _includeX) ||
typeAttributeValue == _sNormal
select new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
};
Or remove query expression usage and do it this way:-
var all = doc.Descendants(_xString);
var query = all.Where( xElem=> {
var typeAttributeValue = xElem.Attribute(_typeAttributeName).Value;
return typeAttributeValue == _sWorkspace && includeX ) || typeAttributeValue == _sNormal;
})
.Select( xElem =>
select new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
})
Or combine the first and third and do:
Predicate<string> selector = x=> _includeX
? x == _sWorkspace || x == _sNormal
: x == _sNormal;
query = doc.Descendants(_xString)
.Where(xElem => selector(xElem.Attribute(_typeAttributeName).Value))
.Select(xElem => new xmlThing
{
_location = xElem.Attribute(_nameAttributeName).Value,
_type = xElem.Attribute(_typeAttributeName).Value,
};)
It all depends what's going to work cleanest in your context.
Do yourself a favour and buy (and read!) C# in Depth and it'll all make sense a lot more quickly that learning this stuff bit by bit...

Categories

Resources