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
Related
I am trying to remove some specified attributed from the XML file sample code was below. string[] szNodeList is the array list so node contains name in string array will be removed ans save again
Any help will be appreciated.
var doc = new System.Xml.XmlDocument();
doc.Load("attrs.xml");
var root = doc.DocumentElement;
string[] szNodeList = new string[] { "titleTextColor"
,"isLightTheme"
,"showText"
};
foreach (System.Xml.XmlElement child in root )
{
foreach (string sz in szNodeList)
{
root.RemoveAttribute(sz);
//if (child.Attributes[sz] != null)
//{
// child.Attributes.Remove(child.Attributes[sz]);
//}
}
}
doc.Save("build.xml");
XML CODE
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="cropImageStyle" format="reference" />
<attr name="drawerArrowStyle" format="reference" />
<attr name="height" format="dimension" />
<attr name="isLightTheme" format="boolean" />
<attr name="title" format="string" />
<attr name="navigationMode">
<enum name="listMode" value="1" />
<enum name="normal" value="0" />
<enum name="tabMode" value="2" />
</attr>
</resources>
But saving as original file without changes i thing remove is not working.
Try this:
doc
// select all `resources/attr` node
.SelectNodes("resources/attr")
.Cast<XmlNode>()
// that contains the `name` attribute whose value is in `szNodeList`
.Where(x => !string.IsNullOrEmpty(x.Attributes["name"]?.Value) && szNodeList.Contains(x.Attributes["name"].Value))
.ToList()
// and, remove them from their parent
.ForEach(x => x.ParentNode.RemoveChild(x));
One of the problems here is terminology. You're not trying to remove attributes, as I understand it - you're trying to remove whole elements, based on the value of the name attribute.
If you can possibly use LINQ to XML for this, I would do so. It generally makes working with XML a lot easier. Here's a complete program to do what you want:
using System;
using System.Linq;
using System.Xml.Linq;
class Test
{
static void Main()
{
var namesToRemove = new[]
{
"titleTextColor",
"isLightTheme",
"showText"
};
XDocument doc = XDocument.Load("test.xml");
// For all the elements directly under the document root...
doc.Root.Elements()
// Where the array contains the value of the "name" attribute...
.Where(x => namesToRemove.Contains((string) x.Attribute("name")))
// Remove them from the document
.Remove();
doc.Save("output.xml");
}
}
Output:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="cropImageStyle" format="reference" />
<attr name="drawerArrowStyle" format="reference" />
<attr name="height" format="dimension" />
<attr name="title" format="string" />
<attr name="navigationMode">
<enum name="listMode" value="1" />
<enum name="normal" value="0" />
<enum name="tabMode" value="2" />
</attr>
</resources>
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);
}
}
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.
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));
}
}