I’m trying to use Linq XML to select a number of nodes and the children but getting terrible confused!
In the example XML below I need to pull out all the <MostWanted> and all the Wanted with their child nodes but without the other nodes in between the Mostwanted and Wanted nodes.
This because each MostWanted can be followed by any number of Wanted and the Wanted relate to the preceding Mostwanted.
I’m even confusing myself typing this up!!!
How can I do this in C#??
<root>
<top>
<NotWanted3>
</NotWanted3>
<MostWanted>
<UniqueKey>1</UniqueKey>
<QuoteNum>1</QuoteNum>
</MostWanted>
<NotWanted2>
<UniqueKey>1</UniqueKey>
<QuoteNum>1</QuoteNum>
</NotWanted2>
<NotWanted1>
<UniqueKey>0001</UniqueKey>
</NotWanted1>
<Wanted>
<Seg>
<SegNum>1</SegNum>
</Seg>
</Wanted>
<Wanted>
<Seg>
<SegNum>2</SegNum>
</Seg>
</Wanted>
<NotWanted>
<V>x</V>
</NotWanted>
<NotWanted3>
</NotWanted3>
<MostWanted>
<UniqueKey>1</UniqueKey>
<QuoteNum>1</QuoteNum>
</MostWanted>
<NotWanted2>
<UniqueKey>1</UniqueKey>
<QuoteNum>1</QuoteNum>
</NotWanted2>
<NotWanted1>
<UniqueKey>0002</UniqueKey>
</NotWanted1>
<Wanted>
<Seg>
<SegNum>3</SegNum>
</Seg>
</Wanted>
<Wanted>
<Seg>
<SegNum>4</SegNum>
</Seg>
</Wanted>
<NotWanted>
<V>x</V>
</NotWanted>
</top>
</root>
Why don't you just use:
XName wanted = "Wanted";
XName mostWanted = "MostWanted";
var nodes = doc.Descendants()
.Where(x => x.Name == wanted || x.Name == mostWanted);
That will retrieve every element called "Wanted" or "MostWanted". From each of those elements you can get to the child elements etc.
If this isn't what you're after, please clarify your question.
Related
Lets say i have this simple xml:
<root>
<dad>
<kid> wee </kid>
</dad>
</root>
My goal is to replace <dad> by <mom> but keeping the kid information.
Ive tryed
xml.XPathSelectElements("dad")
then looping over and
dad.ReplaceWith(new XElement("mom", dad.descendants());
but it doesnt work :(
Any ideas on how to do this ?
It looks like you want to rename the element 'dad' to 'mom':
xml.Root.Elements("dad").ToList().ForEach(d => d.Name = "mom");
If 'mom' is an existing element with its own elements and attributes i would replace the 'dad'-element and add the 'kid'-element to the 'mom' element:
xml.Root.Elements("dad").ToList().ForEach(d =>
{
XElement mom = new XElement("mom"); //use your mom here
mom.Add(d.Descendants());
d.ReplaceWith(mom);
});
My '.xml' file looks this way:
<?xml version="1.0" encoding="utf-8"?>
<Requestes>
<Single_Request num="1">
<numRequest>1</numRequest>
<IDWork>1</IDWork>
<NumObject>1</NumObject>
<lvlPriority>Высокий</lvlPriority>
</Single_Request>
<Single_Request num="2">
<numRequest>2</numRequest>
<IDWork>2</IDWork>
<NumObject>2</NumObject>
<lvlPriority>Средний</lvlPriority>
</Single_Request>
<Periodic_Request num="1">
<numRequest>3</numRequest>
<IDWork>23</IDWork>
<pFrequency>23</pFrequency>
<lvlPriority>Низкий</lvlPriority>
<time_service>23</time_service>
<time_last_service>23</time_last_service>
<relative_time>23</relative_time>
</Periodic_Request>
</Requestes>
So I need to delete Single_Request with atribute value equal to sTxtBlock_numRequest.Text. I have tried to do it this way:
XDocument doc = XDocument.Load(FilePath);
IEnumerable<XElement> sRequest = doc.Root.Descendants("Single_Request").Where(
t => t.Attribute("num").Value =="sTxtBlock_numRequest.Text"); //I'm sure, that problem is here
sRequest.Remove();
doc.Save(FilePath);
Unfortunattly, nothing has happanned, don`t know how to solve the problem.
This is why , I am looking forward to your help.
You are comparing attribute value with string literal "sTxtBlock_numRequest.Text". You should pass value of textbox text instead:
doc.Root.Elements("Single_Request")
.Where(t => (string)t.Attribute("num") == sTxtBlock_numRequest.Text)
.Remove();
Note - it's better to use Elements when you are getting Single_Request elements of root, because Descendants will search whole tree, instead of looking at direct children only. Also you can call Remove() without saving query to local variable.
<root>
<abc:Description abc:about="XXX.XXX_CSData-2">
<xxx:Data.Curve abc:resource="XXX.XXX"/>
<xxx:Data.y2AData abc:datatype="#int">27</xxx:Data.y2AData>
<xxx:Data.y1AData abc:datatype="#int">-27</xxx:Data.y1AData>
<xxx:Data.xAData abc:datatype="#int">60</xxx:Data.xAData>
<xxx:IdentifiedObject.description abc:datatype="#string">SOME NAME</xxx:IdentifiedObject.description>
<xxx:IdentifiedObject.name abc:datatype="#string">XXX_CCC.XX</xxx:IdentifiedObject.name>
<abc:type abc:resource="http://iec.ch/TC57/2008/xxx-schema-xxx13#Data"/>
</abc:Description>
<abc:Description abc:about="XXX.XXX">
<xxx:ConnectivityNode.MemberOf_EquipmentContainer abc:resource="XXX.XXX"/>
<xxx:IdentifiedObject.description abc:datatype="#string">XXX.XXX</xxx:IdentifiedObject.description>
<xxx:IdentifiedObject.name abc:datatype="#string">XXX.XXX</xxx:IdentifiedObject.name>
<abc:type abc:resource="http://iec.ch/TC57/2008/xxx-schema-xxx13#ConnectivityNode"/>
<xxx:ConnectivityNode.Terminals abc:resource="XXX.XXX"/>
<xxx:ConnectivityNode.Terminals abc:resource="XXX.XXX"/>
<xxx:ConnectivityNode.Terminals abc:resource="JXXX.XXX"/>
<xxx:ConnectivityNode.Terminals abc:resource="JXXX.XXX"/>
</abc:Description>
</root>
Hello all,
In the above XML Snippet tha tags are "xxx:IdentifiedObject.description","IdentifiedObject.name" and "abc:type" are common between two child nodes.
I want to write a LINQ query that would return these tag names that are common (appear atleast once) in the child elements. That is need the tage names but the value of the tags
1) "xxx:IdentifiedObject.description",
2) "IdentifiedObject.name" and
3) "abc:type"
It sounds like you want something like:
// Given x and y as the parent elements you're interested in
var commonNames = x.Elements()
.Select(x => x.Name)
.Intersect(y.Elements().Select(y => y.Name));
That will give you an IEnumerable<XName>.
In a XML document such as the following:
<root>
<fish value="Start"/>
<pointlessContainer>
<anotherNode value="Pick me!"/>
<anotherNode value="Pick me too!"/>
<fish value="End"/>
</pointlessContainer>
</root>
How can I use the wonder of LINQ to XML to find any nodes completely contained by the fish nodes? Note that in this example, I have deliberately placed the fish nodes at different levels in the document, as I anticipate this scenario will occur in the wild.
Obviously, in this example, I would be looking to get the two anotherNode nodes, but not the pointlessContainer node.
NB: the two 'delimiting' nodes may have the same type (e.g. fish) as other non-delimiting nodes in the document, but they would have unique attributes and therefore be easy to identify.
For your sample, the following should do
XDocument doc = XDocument.Load(#"..\..\XMLFile2.xml");
XElement start = doc.Descendants("fish").First(f => f.Attribute("value").Value == "Start");
XElement end = doc.Descendants("fish").First(f => f.Attribute("value").Value == "End");
foreach (XElement el in
doc
.Descendants()
.Where(d =>
XNode.CompareDocumentOrder(d, end) == -1
&& XNode.CompareDocumentOrder(d, start) == 1
&& !end.Ancestors().Contains(d)))
{
Console.WriteLine(el);
}
But I haven't tested or thoroughly pondered whether it works for other cases. Maybe you can check for some of your sample data and report back whether it works.
This query seems to be valid, but I have 0 results.
IEnumerable<XElement> users =
(from el in XMLDoc.Elements("Users")
where (string)el.Attribute("GUID") == userGUID.ToString()
select el);
My XML is as follows:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Users>
<User GUID="68327fe2-d6f0-403b-a7b6-51860fbf0b2f">
<Key ID="F7000012ECEAD101">
...
</Key>
</User>
</Users>
Do you have any clues to shed some light onto this?
Well, the Users elements don't have GUID attributes. Two suggested options:
Look at XDocument.Root.Elements("User")
Use Descendants("User") to find all User elements.
I'll stick with the former for the moment. That gives us:
IEnumerable<XElement> users =
(from el in XMLDoc.Root.Elements("User")
where (string) el.Attribute("GUID") == userGUID.ToString()
select el);
Now, we can still tidy this up further. Firstly, let's cast to Guid instead of string:
IEnumerable<XElement> users =
(from el in XMLDoc.Root.Elements("User")
where (Guid) el.Attribute("GUID") == userGUID
select el);
However there's not a lot of reason to use a query expression here... all you're applying is a single predicate. Let's just use the Where method directly:
IEnumerable<XElement> users =
XMLDoc.Root
.Elements("User")
.Where(el => (Guid) el.Attribute("GUID") == userGUID);
How you lay it out is up to you, of course :) With a longer line, you can probably align everything up under a longer first line:
IEnumerable<XElement> users = XMLDoc.Root
. etc
Now, finally - what about if the User element doesn't have a GUID attribute? Currently, this code will throw an exception. That may be exactly what you want - or it may not. If it's not, you can make it ignore such things by casting to Nullable<Guid> aka Guid? instead:
IEnumerable<XElement> users =
XMLDoc.Root
.Elements("User")
.Where(el => (Guid?) el.Attribute("GUID") == userGUID);
change Users in the 2nd line to User. Like this:
IEnumerable<XElement> users = (from el in XMLDoc.Root.Elements("User")
where (string)el.Attribute("GUID") == userGUID.ToString()
select el);
I'm assuming XMLDoc is an XDocument, and not the root element itself.