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.
Related
This question already has answers here:
Force XmlDocument to save empty elements with an explicit closing tag
(1 answer)
Forcing XDocument.ToString() to include the closing tag when there is no data
(2 answers)
Closed 11 months ago.
I am looking for a good approach to write a LINQ query that can preserve empty tags in XML.
Input XElement:
<LayoutDefinition >
<ScreenDefinitions>
<ScreenDefinition Monitor="1">
<SSBS>
<SSB Id="1" />
<SSB Id="2" />
</SSBS>
</ScreenDefinition>
<ScreenDefinition Monitor="2">
<SSBS>
<SSB Id="1" />
<SSB Id="2" />
</SSBS>
</ScreenDefinition>
</ScreenDefinitions>
<CustomProperties>
<Property Name="SynchronizationCollection" />
</CustomProperties>
</LayoutDefinition>
This is being process by the below method:
private static IList<XElement> ProcessLayoutDefinition(XElement layoutDefinition)
{
IList<XElement> resultLayoutDefinitions = new List<XElement>();
layoutDefinition?.XPathSelectElement($"/CustomProperties/Property[#Name=\"SynchronizationCollection\"]").Remove();
resultLayoutDefinitions.Add(layoutDefinition);
return resultLayoutDefinitions;
}
The Result should yield the below output:
<LayoutDefinition >
<ScreenDefinitions>
<ScreenDefinition Monitor="1">
<SSBS>
<SSB Id="1" />
<SSB Id="2" />
</SSBS>
</ScreenDefinition>
<ScreenDefinition Monitor="2">
<SSBS>
<SSB Id="1" />
<SSB Id="2" />
</SSBS>
</ScreenDefinition>
</ScreenDefinitions>
<CustomProperties>
</CustomProperties>
</LayoutDefinition>
But gives the below output with only <CustomProperties />
<LayoutDefinition >
<ScreenDefinitions>
<ScreenDefinition Monitor="1">
<SSBS>
<SSB Id="1" />
<SSB Id="2" />
</SSBS>
</ScreenDefinition>
<ScreenDefinition Monitor="2">
<SSBS>
<SSB Id="1" />
<SSB Id="2" />
</SSBS>
</ScreenDefinition>
</ScreenDefinitions>
<CustomProperties />
</LayoutDefinition>
Is there anyway I can force to preserve the <CustomProperties> </CustomProperties> tags although <CustomProperties /> is a correct tag.
I have the following xml code which I want to read into a dataset:
<?xml version="1.0" standalone="yes"?>
<jlqn:Root xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:jlqn="http://jlqn/1.0">
<element xsi:type="jlqn:Processor" name="test1" processor="test">
<metadata name="Removed" value="False" />
<task id="6">
<entry id="8" entry="" />
</task>
</element>
<element xsi:type="jlqn:Processor" name="test1" processor="test" id="1">
<metadata name="Removed" value="True" />
<metadata name="Removed1" value="Removed1" />
</element>
<element xsi:type="jlqn:Processor" name="test1" processor="test" id="3">
<metadata name="Removed" value="False" />
<task id="45" name="">
<metadata />
<entry id="10" entry="">
<metadata />
</entry>
</task>
</element>
<element name="test" />
</jlqn:Root>
I use the following C# code
DataSet newTable = new DataSet();
newTable.ReadXml(#"F:\QVT\runtime-EclipseApplication\dr5\HJYU.jlqn");
But when I check the dataset , I notice all the attributes with prefix are not read ..such as this : xsi:type="jlqn:Processor"
what can I do ?
The XML references an namespace prefix "xsi", but it is not defined. Change the root element to:
<jlqn:Root xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:jlqn="http://jlqn/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
Once I did this, I got the following tables and columns:
Root
Root_Id
version
element
element_Id
name
processor
id
Root_Id
metadata
name
value
element_Id
task
metadata
task_Id
id
name
element_Id
entry
metadata
id
entry
task_Id
That is the output from:
DataSet newTable = new DataSet();
newTable.ReadXml(#"XMLFile1.xml");
foreach(DataTable table in newTable.Tables)
{
Console.WriteLine(table.TableName);
foreach(DataColumn column in table.Columns)
{
Console.WriteLine(" " + column.ColumnName);
}
}
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!
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));
}
}
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