Omit nodes in XML based on Attribute - c#

Using C# LINQ to XML
I'm trying to omit the first and last nodes in the following XML.
I'm trying to process each node between <node id="2" one="start"> and <node id="4" one="finish">
<root>
<node id="1">
<element two="3"/>
<element two="7"/>
</node>
<node id="2" one="start">
<element two="1"/>
<element two="2"/>
</node>
<node id="3">
<element two="4"/>
<element two="4"/>
<element two="4"/>
<element two="2"/>
<element two="6"/>
</node>
<node id="4">
<element two="3"/>
<element two="7"/>
</node>
<node id="5" one="finish">
<element two="3"/>
<element two="7"/>
</node>
<node id="6">
<element two="3"/>
<element two="7"/>
</node>
<root>
Is there a to standard approach to this?

If you have a sequence of XElements and you want to filter them based on your condition, I don't think there is anything built-in in LINQ to do exactly that (there is SkipWhile() and TakeWhile() that do something similar).
I think what you should do is to create a generic extension method that filter a collection based on first and last condition, something like:
public static IEnumerable<T> GetBetween<T>(
this IEnumerable<T> source,
Func<T, bool> firstPredicate, Func<T, bool> lastPredicate)
{
bool foundFirst = false;
foreach (var item in source)
{
if (!foundFirst)
foundFirst = firstPredicate(item);
if (foundFirst)
{
yield return item;
if (lastPredicate(item))
break;
}
}
}
You would then use it like this:
elements.GetBetween(
e => (string)e.Attribute("one") == "start",
e => (string)e.Attribute("one") == "finish")

I went with some very simple logic to handle this.
bool processFlag = false;
Toggling this boolean from false to true when the node.element.attribute = "start" then back to false when the = "finish" did the trick!

Related

Remove element from XML based on condition [duplicate]

XML
<WorkTable>
<Days>
<Day id="0" name="Monday"/>
<Day id="1" name="Tuesday"/>
<Day id="2" name="Wednesday"/>
<Day id="3" name="Thursday" />
<Day id="4" name="Friday"/>
<Day id="5" name="Saturday"/>
<Day id="6" name="Sunday"/>
</Days>
<SpecialDays>
<Day date="22.07.2015"/>
<Day date="24.07.2015"/>
</SpecialDays>
</WorkTable>
This code doesn't remove the node from xml. Could you help me to find problem?
XmlDocument doc = new XmlDocument();
doc.Load(localXMLpath + xmlFileName);
XmlNode delNode= doc.SelectSingleNode("/WorkTable/SpecialDays/Day[#date='24.07.2015']");
delNode.ParentNode.RemoveChild(delNode);
doc.Save(localXMLpath + xmlFileName);
This should works:
XDocument xdoc = XDocument.Load(filename);
xdoc.Element("WorkTable").Element("SpecialDays").Elements("Day")
.Where(x => (string)x.Attribute("date") == "24.07.2015")
.Remove();
xdoc.Save(filename);
Your code works OK, the problem is that you're trying to overwrite the file you've read the data from.
See this answer C# : the close method of Xml.Load(file)

How to remove XML node by attribute

XML
<WorkTable>
<Days>
<Day id="0" name="Monday"/>
<Day id="1" name="Tuesday"/>
<Day id="2" name="Wednesday"/>
<Day id="3" name="Thursday" />
<Day id="4" name="Friday"/>
<Day id="5" name="Saturday"/>
<Day id="6" name="Sunday"/>
</Days>
<SpecialDays>
<Day date="22.07.2015"/>
<Day date="24.07.2015"/>
</SpecialDays>
</WorkTable>
This code doesn't remove the node from xml. Could you help me to find problem?
XmlDocument doc = new XmlDocument();
doc.Load(localXMLpath + xmlFileName);
XmlNode delNode= doc.SelectSingleNode("/WorkTable/SpecialDays/Day[#date='24.07.2015']");
delNode.ParentNode.RemoveChild(delNode);
doc.Save(localXMLpath + xmlFileName);
This should works:
XDocument xdoc = XDocument.Load(filename);
xdoc.Element("WorkTable").Element("SpecialDays").Elements("Day")
.Where(x => (string)x.Attribute("date") == "24.07.2015")
.Remove();
xdoc.Save(filename);
Your code works OK, the problem is that you're trying to overwrite the file you've read the data from.
See this answer C# : the close method of Xml.Load(file)

C#/XML Populate TreeView with XML file

im trying to populate a treeview from a xml file.
Image of the output: http://i.stack.imgur.com/3HSCu.png
So as you can see the parents are loaded well, but the childs (the elements) not. All the child nodes are loaded in all parent nodes. But the xml is not like that.
This is the XML code:
<?xml version="1.0" encoding="utf-8" ?>
<toolbox>
<parent id="p1" caption="All Elements" class="parent">
<element id="1" name="Button" />
<element id="2" name="Label" />
<element id="3" name="Inputfield" />
<element id="4" name="Textarea" />
<element id="5" name="Image" />
<element id="6" name="Background" />
<element id="7" name="TreeView" />
</parent>
<parent id="p2" caption="Some Elements 1" class="parent">
<element id="1" name="Button" />
<element id="2" name="Label" />
<element id="3" name="Inputfield" />
</parent>
<parent id="p3" caption="Some Elements 2" class="parent">
<element id="4" name="Textarea" />
<element id="5" name="Image" />
<element id="6" name="Background" />
<element id="7" name="TreeView" />
</parent>
</toolbox>
This is the C# code:
public void loadElements(string XML_Elements, TreeView Elements_Tree){
XmlDocument XMLDocument = new XmlDocument();
XMLDocument.Load(XML_Elements);
Elements_Tree.Nodes.Clear();
Elements_Tree.BeginUpdate();
XmlNodeList XMLParent = XMLDocument.SelectNodes("toolbox/parent");
foreach(XmlNode xmlparent in XMLParent){
//add parents
string Parent_Caption = xmlparent.Attributes["caption"].Value;
TreeNode parents = Elements_Tree.Nodes.Add(Parent_Caption);
//add childs
XmlNodeList XMLChilds = XMLDocument.SelectNodes("toolbox/parent/element");
foreach (XmlNode xmlchild in XMLChilds)
{
string Child_Name = xmlchild.Attributes["name"].Value;
parents.Nodes.Add(Child_Name);
}
}
}
You have to search for elements only within current parent element. Try that:
XmlNodeList XMLChilds = XMLDocument.SelectNodes("toolbox/parent[#caption='" + Parent_Caption + "']/element");
Or maybe even better:
XmlNodeList XMLChilds = xmlparent.SelectNodes("element");
XMLDocument.SelectNodes("toolbox/parent/element") selects all nodes that match in the document. You need to get the children of the current XmlNode, not start at the XMLDocument.

How can I filter xml sections and save it as a file

I am a beginner in using LINQ. I have an xml data with the format below
<Root>
<Global>
</Global>
<local>
<element name="A">
<subelement name="A">
<element name="A1">
<subelement name="A1">
<Property>
</Property>
</subelement>
</element>
<element name="B">
<Property>
</Property>
</element>
</subelement>
<subelement name="B">
<element name="A">
<Property>
</Property>
</element>
<element name="B">
<Property>
</Property>
</element>
</subelement>
</element>
</local>
</Root>
I want to save each section of the Property element into it's own xml file as below while keeping the hierarchy structure intact
<Root>
<Global>
</Global>
<local>
<element name="A">
<subelement name="A">
<element name="A1">
<subelement name="A1">
<Property>
</Property>
</subelement>
</element>
</subelement>
</element>
</local>
</Root>
and
<Root>
<Global>
</Global>
<local>
<element name="A">
<subelement name="A">
<element name="B">
<Property>
</Property>
</element>
</subelement>
</element>
</local>
</Root>
and so on for all the Property elements. Each element can have a child subelement and each subelement can have element child
Can you provide a solution on how to save each section of Property element as a new xml file with the current hierarchy structure intact. I have tried using the Ancestor() method of the XElement class
XDocument xml = XDocument.Load(filename);
XDocument convertedQuery = new XDocument(
new XElement(xml.Root.Name, from x in xml.Descendants("local")
select x.Ancestors())
);
but it is returning other Property elements as well, as below
<Root>
<Global></Global>
<local>
<element name="A">
<subelement name="A">
<element name="A1">
<subelement name="A1">
<Property></Property>
</subelement>
</element>
<element name="B">
<Property></Property>
</element>
</subelement>
<subelement name="B">
<element name="A">
<Property></Property>
</element>
<element name="B">
<Property></Property>
</element>
</subelement>
</element>
</local>
Any help would be much appreciated. Thanks
Here is some C# sample that should do what you want or at least give you an idea on how to approach that. The sample for testing simply writes each created XDocument to Console.Out but of course you could change that to save to a file on disk:
static void Main(string[] args)
{
XDocument doc = XDocument.Load(#"..\..\XMLFile1.xml");
foreach (XElement prop in doc.Descendants("Property"))
{
XDocument result = new XDocument(
new XElement(doc.Root.Name, doc.Root.Attributes(),
doc.Root.Elements("Global"),
CopySubtree(prop.Ancestors("local").First(), prop)));
result.Save(Console.Out);
Console.WriteLine();
}
}
static XElement CopySubtree(XElement ancestor, XElement descendant)
{
if (ancestor == descendant)
{
return descendant;
}
else
{
return
new XElement(
ancestor.Name,
ancestor.Attributes(),
CopySubtree(
ancestor
.Elements()
.First(e => e.DescendantsAndSelf().Any(d => d == descendant)),
descendant));
}
}
[edit]
For the new requirement to copy not only the Property ancestors but also its siblings you could adapt above method as follows:
static XElement CopySubtree(XElement ancestor, XElement descendant)
{
if (ancestor == descendant.Parent)
{
return ancestor;
}
else
{
return
new XElement(
ancestor.Name,
ancestor.Attributes(),
CopySubtree(
ancestor
.Elements()
.First(e => e.DescendantsAndSelf().Any(d => d == descendant)),
descendant));
}
}

C# XML De serialization and arrays

There is next xml file:
<element Name="root">
<SubFields>
<element Name="subroot">
<SubFields>
<element1 Name="element1" customatt1 = "12313" customatt2 = "asdfasfadsfasd">
<subelement Name="subelement" />
</element1>
<element1 Name="element11" customatt1 = "12313" customatt2 = "asdfasfadsfasd">
<subelement Name="subelement" />
</element1>
<element1 Name="element111" customatt1 = "12313" customatt2 = "asdfasfadsfasd">
<subelement Name="subelement" />
</element1>
<element2 Name="element2" path = "asdfdsf" widget="asdasdasd">
<subelement Name="subelement" />
</element2>
<element2 Name="element22" path = "asdfdsf" widget="asdasdasd">
<subelement Name="subelement" />
</element2>
<element2 Name="element222" path = "asdfdsf" widget="asdasdasd">
<subelement Name="subelement" />
</element2>
</SubFields>
</element>
</SubFields>
</element>
I mapped the array of elements as [XmlArray("SubFields")] where SubFields is root of arrays and Question:
How to map differences types of elements in object ?
And I can have a lot of subroot -s elements.
I used xsd.exe to do it.
Use the XMLSerializer class. Near the bottom theres a section about using Property attributes to map the object to the xml elements

Categories

Resources