I'm delving into the world of XmlDocument building and thought I'd try to re-build (at least, in part) the Desktop tree given by Microsoft's program UISpy.
So far I am able to grab a child of the desktop and write that to a XML document, and then grab each child of that and write those to an XML document.
So far the code looks like this...
using System.Windows.Automation;
using System.Xml;
namespace MyTestApplication
{
internal class TestXmlStuff
{
public static void Main(string[] args)
{
XmlDocument xDocument = new XmlDocument();
AutomationElement rootElement = AutomationElement.RootElement;
TreeWalker treeWalker = TreeWalker.ContentViewWalker;
XmlNode rootXmlElement = xDocument.AppendChild(xDocument.CreateElement("Desktop"));
AutomationElement autoElement = rootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "GitHub"));
string name = autoElement.Current.Name;
while (autoElement != null)
{
string lct = autoElement.Current.LocalizedControlType.Replace(" ", "");
lct = (lct.Equals("") ? "Cusotm" : lct);
XmlElement temp = (XmlElement)rootXmlElement.AppendChild(xDocument.CreateElement(lct));
//temp.InnerText = lct;
string outerXML = temp.OuterXml;
rootXmlElement = temp;
autoElement = treeWalker.GetNextSibling(autoElement);
}
}
}
}
...and the resulting XML file...
Now, when I add a line to change the InnerText Property of each XML element, like temp.InnerText = lct I get an oddly formated XML file.
What I expected from this was that each InnerText would be on the same line as the start and end tags of the XML element, but instead all but the last element's InnerText is located on a new line.
So my question is, why is that? Is there something else I could be doing with my XML elements to have their InnerText appear on the same line?
As I said in a comment, XML isn't a display format, so it gets formatted however IE chooses to do so.
To get closer to what you were expecting, you might want to consider using an attribute rather than innertext:
XmlElement temp = (XmlElement)rootXmlElement.AppendChild(xDocument.CreateElement(lct));
var attr = xDocument.CreateAttribute("type");
attr.Value = lct;
temp.Attributes.Append(attr);
IE displays the attributes within the opening element, which may be good enough for your purposes.
From the XML perspective, what you're currently creating is called Mixed Content - you have an element that contains both text and other elements. From a hierarchical perspective, those text nodes and other elements occupy the same position within the hierarchy - so I'd assume that this is why IE is displaying them as "equals" - both nested under their parent element and at the same indentation level.
Related
I use "application 1" to create and edit xhtml files.
It has an option to enter annotations into the content of non-empty elements like p, h1, h2, td etc ... which results in mixed xml code sections like this:
<p>Hello <NS1:annotation [...SomeAttributes...]>everybody</NS1:annotation> out there!</p>
For translational purposes I have to export these xhtml files into "application 2" which can't deal with these internal elements. As the annotations are not part of the desired content in the translations removing them before exporting them to application 2 would be a perfect workaround:
<p>Hello everybody out there!</p>
Removing nodes from an XmlDocument reliably finds and removes the internal xml elements but it also deletes the content of the annotation element - loosing the word "everybody" in the example above:
<p>Hello out there!</p>
What I need is rather "unbinding" the content of these internal elemts into the content of the parent element. But so far I haven't found a method using the c# xml tools doing the job.
So far I first save the xhtml file, re-open it as text file and use regedits to remove the annotation. I can even use c# methods for it:
TextFile txt = new TextFile();
string s = txt.ReadFile(filename);
string pattern = #"<NS1:annotation.+>(.+)</NS1:annotation>";
string input = s;
string replacement = "$1";
Regex rgx = new Regex(pattern);
string result = rgx.Replace(input, replacement);
TextFile.Write((filename,result););
This is doubtlessly a better solution as it doesn't loose the content of the annotation but I wonder if there is really not a solution based on the c# Xml-tools that does the job.
Anybody out there who knows it?
I think I found an answer using XmlDocument.
The key is that in mixed xml nodes the text surrounding the node can be adressed as xml nodes too. I wasn't aware of this ...
The following function unbinds the content of the mixed node and releases it into the content of the parent node. I haven't tested it for nodes containing multiple annotations, but that's enough for me at the moment ...
private void removeAnnotations(XmlDocument doc)
{
XmlNamespaceManager manager = new XmlNamespaceManager(new NameTable());
manager.AddNamespace("NS1","http://www.someurl.net");
XmlNodeList annotations = doc.SelectNodes("//NS1:annotation", manager);
int i = 0;
while (i < annotations.Count)
{
//in mixed xml the Siblings are xml text nodes. Therefore we write them into buffers:
string s0 = "";
if(annotations[i].PreviousSibling != null) s0 = annotations[i].PreviousSibling.InnerText;
string s2 = "";
if(annotations[i].NextSibling != null) s2 = annotations[i].NextSibling.InnerText;
//buffer the content of the annotation itself
string s1 = annotations[i].InnerText;
//buffer the link to the parent node before we remove the annotation,
XmlNode parent = annotations[i].ParentNode;
//now remove the annotation
parent.RemoveChild(annotations[i]);
//and apply the new Text to the parent element
parent.InnerText = s0 + s1 + s2;
i++;
}
}
<TestCase Name="DEBUG">
<ActionEnvironment Name="Carved records indication">
<Define Name="_TestedVersionPath" Value="{CustomParam {paramName=PA tested version installer folder path}, {appName=PA installer}, {hint=\\ptnas1\builds\Temp Builds\Forensic\Physical Analyzer\PA.Test\UFED_Analyzer_17.02.05_03-00_6.0.0.128\EncryptedSetup}}"/>
<Define Name="_PathOfdata" Value="SharedData\myfolder\mydata.xml"/>
<ActionSet Name="DEBUG">
<Actions>
<SpecialAction ActionName="myactionname">
<CaseName>123</CaseName>
<UaeSendQueryValues>
<URL>192.168.75.133</URL>
<RestURL></RestURL>
<UserName>user1</UserName>
<Password>aaa</Password>
<PathOfQuery>_PathOfdata</PathOfQuery>
<Method>GET</Method>
<ParamsFromFile></ParamsFromFile>
</UaeSendQueryValues>
</SpecialAction>
</Actions>
</ActionSet>
</ActionEnvironment>
I have the above xml. i need to find every PathOfQuery tag, get the text of it (in the example _PathOfdata) and then go up in the xml tree and find the first Define tag who's name = to text of PathofQuery tag and get its value (in the example "SharedData\myfolder\mydata.xml")
then i would like to replace that value with another string.
i need to do this for each PathofQuery tag that appears in the xml (it could be more then one) and i want to find always the first apparition of the Define tag (could be more than one) when i travel the tree up from the point where the PathofQuery tag was found.
I want to do this on C Sharp
any help will be appreciated.
Let's assume string s holds the above Xml. Then the following code will work for you:
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(s);
XmlNode pathOfQuery = xDoc.SelectSingleNode("//PathOfQuery");
string pathOfQueryValue = pathOfQuery.InnerText;
Console.WriteLine(pathOfQueryValue);
XmlNode define = xDoc.SelectSingleNode("//Define[#Name='" + pathOfQueryValue + "']");
if(define!=null)
{
string defineTagValue = define.Attributes["Value"].Value;
Console.WriteLine(defineTagValue);
pathOfQuery.InnerText = defineTagValue;
Console.WriteLine(pathOfQuery.InnerText);
}
I have string XML. I loaded to XmlDocument. How can I add, edit and delete by simplest method by line, because I know only line which I should edit. It's better work wih XML like with string, or better work with XmlDocuments?
using System;
using System.Xml;
namespace testXMl
{
class Program
{
static void Main(string[] args)
{
string xml="<?xml version=\"1.0\"?>\r\n<application>\r\n<features>\r\n<test key=\"some_key\">\r\n</features>\r\n</application>";
XmlDocument xm = new XmlDocument();
xm.LoadXml(xml);
//Edit third line
//xm[3].EditName(featuresNew);
//xml->"<?xml version=\"1.0\"?>\r\n<application>\r\n<featuresNew>\r\n<test key=\"some_key\">\r\n</featuresNew>\r\n</application>"
//Add fourth line the Node
//xm[4].AddNode("FeatureNext");
//xml->"<?xml version=\"1.0\"?>\r\n<application>\r\n<FeatureNext>\r\n<FeatureNext>\r\n</features2>\r\n<test key=\"some_key\">\r\n</features>\r\n</application>"
//Delete sixth line
//xm[6].DeleteNode;
//xml->"<?xml version=\"1.0\"?>\r\n<application>\r\n<FeatureNext>\r\n<FeatureNext>\r\n</features2>\r\n</features>\r\n</application>"
}
}
}
Thanks, in advance.
You should always work with XDocument/XmlDocument objects. A key knowledge is the XPath query language.
This a quick XML crash course. Run with debugger and inspect the XML variable as you move on.
var xml = new XmlDocument();
xml.LoadXml(#"<?xml version='1.0'?>
<application>
<features>
<test key='some_key' />
</features>
</application>");
// Select an element to work with; I prefer to work with XmlElement instead of XmlNode
var test = (XmlElement) xml.SelectSingleNode("//test");
test.InnerText = "another";
test.SetAttribute("sample", "value");
var attr = test.GetAttribute("xyz"); // Works, even if that attribute doesn't exists
// Create a new element: you'll need to point where you should add a child element
var newElement = xml.CreateElement("newElement");
xml.SelectSingleNode("/application/features").AppendChild(newElement);
// You can also select elements by its position;
// in this example, take the second element inside "features" regardless its name
var delete = xml.SelectSingleNode("/application/features/*[2]");
// Trick part: if you found the element, navigate to its parent and remove the child
if (delete != null)
delete.ParentNode.RemoveChild(delete);
Load function is already defined in xmlData class
public class XmlData
{
public void Load(XElement xDoc)
{
var id = xDoc.XPathSelectElements("//ID");
var listIds = xDoc.XPathSelectElements("/Lists//List/ListIDS/ListIDS");
}
}
I'm just calling the Load function from my end.
XmlData aXmlData = new XmlData();
string input, stringXML = "";
TextReader aTextReader = new StreamReader("D:\\test.xml");
while ((input = aTextReader.ReadLine()) != null)
{
stringXML += input;
}
XElement Content = XElement.Parse(stringXML);
aXmlData.Load(Content);
in load function,im getting both id and and listIds as null.
My test.xml contains
<SEARCH>
<ID>11242</ID>
<Lists>
<List CURRENT="true" AGGREGATEDCHANGED="false">
<ListIDS>
<ListID>100567</ListID>
<ListID>100564</ListID>
<ListID>100025</ListID>
<ListID>2</ListID>
<ListID>1</ListID>
</ListIDS>
</List>
</Lists>
</SEARCH>
EDIT: Your sample XML doesn't have an id element in the namespace with the nss alias. It would be <nss:id> in that case, or there'd be a default namespace set up. I've assumed for this answer that in reality the element you're looking for is in the namespace.
Your query is trying to find an element called id at the root level. To find all id elements, you need:
var tempId = xDoc.XPathSelectElements("//nss:id", ns);
... although personally I'd use:
XDocument doc = XDocument.Parse(...);
XNamespace nss = "http://schemas.microsoft.com/SQLServer/reporting/reportdesigner";
// Or use FirstOrDefault(), or whatever...
XElement idElement = doc.Descendants(nss + "id").Single();
(I prefer using the query methods on LINQ to XML types instead of XPath... I find it easier to avoid silly syntax errors etc.)
Your sample code is also unclear as you're using xDoc which hasn't been declared... it helps to write complete examples, ideally including everything required to compile and run as a console app.
I am looking at the question 3 hours after it was submitted and 41 minutes after it was (last) edited.
There are no namespaces defined in the provided XML document.
var listIds = xDoc.XPathSelectElements("/Lists//List/ListIDS/ListIDS");
This XPath expression obviously doesn't select any node from the provided XML document, because the XML document doesn't have a top element named Lists (the name of the actual top element is SEARCH)
var id = xDoc.XPathSelectElements("//ID");
in load function,im getting both id and and listIds as null.
This statement is false, because //ID selects the only element named ID in the provided XML document, thus the value of the C# variable id is non-null. Probably you didn't test thoroughly after editing the XML document.
Most probably the original ID element belonged to some namespace. But now it is in "no namespace" and the XPath expression above does select it.
string xmldocument = "<response xmlns:nss=\"http://schemas.microsoft.com/SQLServer/reporting/reportdesigner\"><action>test</action><id>1</id></response>";
XElement Content = XElement.Parse(xmldocument);
XPathNavigator navigator = Content.CreateNavigator();
XmlNamespaceManager ns = new XmlNamespaceManager(navigator.NameTable);
ns.AddNamespace("nss", "http://schemas.microsoft.com/SQLServer/reporting/reportdesigner");
var tempId = navigator.SelectSingleNode("/id");
The reason for the null value or system returned value is due to the following
var id = xDoc.XPathSelectElements("//ID");
XpathSElectElements is System.xml.linq.XElment which is linq queried date. It cannot be directly outputed as such.
To Get individual first match element
use XPathSelectElement("//ID");
You can check the number of occurrences using XPathSelectElements as
var count=xDoc.XPathSelectElements("//ID").count();
you can also query the linq statement as order by using specific conditions
Inorder to get node value from a list u can use this
foreach (XmlNode xNode in xDoc.SelectNodes("//ListIDS/ListID"))
{
Console.WriteLine(xNode.InnerText);
}
For Second list you havnt got the value since, the XPath for list items is not correct
I decided to try out the tutorial on this website
http://www.csharphelp.com/2006/05/creating-a-xml-document-with-c/
Here's my code, which is more or less the same but a bit easier to read
using System;
using System.Xml;
public class Mainclass
{
public static void Main()
{
XmlDocument XmlDoc = new XmlDocument();
XmlDocument xmldoc;
XmlNode node1;
node1 = XmlDoc.CreateNode(XmlNodeType.XmlDeclaration, "", "");
XmlDoc.AppendChild(node1);
XmlElement element1;
element1 = XmlDoc.CreateElement("", "ROOT", "");
XmlText text1;
text1 = XmlDoc.CreateTextNode("this is the text of the root element");
element1.AppendChild(text1);
// appends the text specified above to the element1
XmlDoc.AppendChild(element1);
// another element
XmlElement element2;
element2 = XmlDoc.CreateElement("", "AnotherElement", "");
XmlText text2;
text2 = XmlDoc.CreateTextNode("This is the text of this element");
element2.AppendChild(text2);
XmlDoc.ChildNodes.Item(1).AppendChild(element2);
}
}
So far, I'm liking XmlDocument, but I can't figure out how this line works
XmlDoc.ChildNodes.Item(1).AppendChild(element2);
Specifically, the Item() part of it
according to MSDN...
//
// Summary:
// Retrieves a node at the given index.
//
// Parameters:
// index:
// Zero-based index into the list of nodes.
//
// Returns:
// The System.Xml.XmlNode in the collection. If index is greater than or equal
// to the number of nodes in the list, this returns null.
However, I'm still not really sure what "index" refers to, or what Item() does. Does it move down the tree or down a branch?
Also, when I was looking at it, I thought it would end up like this
what I thought would happen:
<?xml version="1.0"?>
<ROOT>this is the text of the root element</ROOT>
<AnotherElement>This is the text of this element</AnotherElement>
but it ended up like this
Actual output
<?xml version="1.0"?>
<ROOT>this is the text of the root element
<AnotherElement>This is the text of this element</AnotherElement>
</ROOT>
(formatting added)
The ChildNodes property returns the XmlNodeList of immediate children of what you call it on. Item then finds the nth member of that list. It won't recurse into grand-children etc. In particular, I believe in this case Item(0) would return the XML declaration, and Item(1) returns the root element. A nicer way of expressing "get to the root element" would be to use XmlDocument.DocumentElement.
Note that your "expected" output wouldn't even be valid XML - an XML document can only have one root element.
To be honest, this isn't a terribly nice use of it - and in particular I would recommend using LINQ to XML rather than XmlDocument if you possibly can. It's not particularly clear what you're trying to achieve with the code you've given, but it would almost certainly be much simpler in LINQ to XML.