Hy at all,
i am new in C# programming so maybe this is a easy question.
I try to read an XML-file with XDocument and write out the local name of a Element as string.
For the output i use the listing window of SiemensNX but every other output window or txt-file is suitable.
Here is the Input-XML:
<?xml version="1.0" encoding="utf-8"?>
<Rootlvl>
<Lvl_1>
<Lvl_2/>
</Lvl_1>
</Rootlvl>
Here is the C# code:
using System.Xml.Linq;
using NXOpen;
namespace XmlElementName
{
class Program
{
private static ListingWindow lw = s.ListingWindow;
public static void Main()
{
string XmlFilePath = #"C:\Users\XXX\Desktop\TestXML.xml"; //XML path
string testnode = "Lvl_2"; //local name of a optional XML element
lw.Open(); //open NX listinwindow for output
//=============LoadXmlFile================
//get main Input
XDocument xml = XDocument.Load(XmlFilePath); //load XmlFile
//====================WriteOutElementName====================
XElement node;
if (testnode == null) //if no optional Element name --> take root element of XML
{
node = xml.Root;
}
else
{
// Find node to passed string "testnode" --> here "Lvl_2"
node = xml.Element(testnode);
}
lw.WriteLine("Test"); //Test if output works --> !yes it works
if(node != null)
{
string output = node.Name.LocalName;
//local name of XElement-variable "node" to string
lw.WriteLine(output); //output the local name of variable "node"
}
else
{
lw.WriteLine("Element with Name = " + testnode + "not found")
}
}
}
}
The Output if variable testnode = "Lvl_2" should be:
Test
Lvl_2
The Output if variable testnode = null schould be:
Test
Rootlvl
The VS Debugger show me that
node = xml.Element(testnode); //testnode = Lvl_2
can't find an element in the XML with the the name "Lvl_2". So it set "node" to "Null" and throw the following exeption:
"System.NullReferenceException: Object reference not set to an instance of an object."
But i know that "Lvl_2" is a subelement of the XML. What can i do to found this element "Lvl_2" in the XML?
What should i change in this line
node = xml.Element(testnode)
to find the element by name?
Thanks for your help guys.
According to MSDN:
Method XDocument.Element(XName) gets the first (in document order)
child element with the specified XName.
So, in your case,
node = xml.Element(testnode)
returns null if testnode = "Lvl_2", because Lvl_2 is not a child element of the xml document (only the root node Rootlvl is considered as a child element is this context).
Try using Descendants method instead:
node = xml.Descendants(testnode).FirstOrDefault();
If you put a testnode that is not present in the xml then node is null and it causes an exception, do the check before printing
if (node!=null){
string output = node.Name.LocalName;
//local name of XElement-variable "node" to string
lw.WriteLine(output); //output the local name of variable "node"
}
Thanks #jdweng,
i take the following changes. Now it works.
using system.linq;
and replace
node.xml.Element(testnode);
with
node = xml.Descendents(testnode).FirstOrDefault();
Related
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.
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);
I am new to XML files and how to manage them. This is for a web app I am writing (aspx).
At the present time I am able to find the first instance of a node and add an item to it with the following code:
xmlClone.Element("PCs").Element("PC").Element("pc_hwStatus").AddAfterSelf(new XElement("user_name", txt_v0_nombre.Text));
What I really want is to add ("user_name", txt_v0_nombre.Text) to a node in particular, not the first one. The content of my XML file is:
<PCs>
<PC>
<pc_name>esc01</pc_name>
<pc_ip>10.10.10.10</pc_ip>
<pc_hwStatus>Working</pc_hwStatus>
</PC>
<PC>
<pc_name>esc02</pc_name>
<pc_ip>10.10.10.11</pc_ip>
<pc_hwStatus>Under Maintenance</pc_hwStatus>
</PC>
</PCs>
The decision of what node to update is made selecting an item from a dropdown list (the PC name).
With my current code, the new item is always added as last line of node with "pc_
name = esc01". I want to be able to added it to esc02 or esc03 and so on... How can this be accomplished? (Using xdocument)
If I understand you correctly, what you are looking for is the FirstOrDefault extension method. In there specify which node you are wanting, in this case a string from your dropdown box, which can be passed in. So to get the first node:
var pc = xmlClone.Element("PCs").Elements("PC").FirstOrDefault(e => e.Element("pc_name").Value == "esc01");
Now you have this in your XElement:
<PC>
<pc_name>esc01</pc_name>
<pc_ip>10.10.10.10</pc_ip>
<pc_hwStatus>Working</pc_hwStatus>
</PC>
To get any element like that, just replace this clause:
.FirstOrDefault(e => e.Element("pc_name").Value == "esc01");
with this one
.FirstOrDefault(e => e.Element("pc_name").Value == desiredPC);
where desiredPC is the value of the xml node: pc_name.
Now to add your data just call the plain old Add method:
pc.Add(new XElement("user_name", txt_v0_nombre.Text);
That should do the trick for you.
Here's a solution that uses LINQ query syntax with LINQ to XML:
XDocument document = XDocument.Parse(xmlContent);
string pcName = "esc02";
IEnumerable<XElement> query =
from pc in document.Element("PCs").Elements("PC")
where pc.Element("pc_name").Value.Equals(pcName)
select pc;
XElement xe = query.FirstOrDefault();
if (xe != null)
{
xe.Add(new XElement("user_name", "DMS"));
}
I have incorporated your sample data and this query into a demonstration program. Please see below for the output from the demonstration program followed by the program itself.
Expected Output
<PC>
<pc_name>esc02</pc_name>
<pc_ip>10.10.10.11</pc_ip>
<pc_hwStatus>Under Maintenance</pc_hwStatus>
<user_name>DMS</user_name>
</PC>
Demonstration Program
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace LinqToXmlDemo
{
public class Program
{
public static void Main(string[] args)
{
string xmlContent = GetXml();
XDocument document = XDocument.Parse(xmlContent);
XElement xe = FindPCName(document, "esc02");
if (xe != null)
{
xe.Add(new XElement("user_name", "DMS"));
Console.WriteLine(xe);
}
else
{
Console.WriteLine("Query returned no results.");
}
}
private static XElement FindPCName(XDocument document, String pcName)
{
IEnumerable<XElement> query =
from pc in document.Element("PCs").Elements("PC")
where pc.Element("pc_name").Value.Equals(pcName)
select pc;
return query.FirstOrDefault();
}
private static String GetXml()
{
return
#"<?xml version='1.0' encoding='utf-8'?>
<PCs>
<PC>
<pc_name>esc01</pc_name>
<pc_ip>10.10.10.10</pc_ip>
<pc_hwStatus>Working</pc_hwStatus>
</PC>
<PC>
<pc_name>esc02</pc_name>
<pc_ip>10.10.10.11</pc_ip>
<pc_hwStatus>Under Maintenance</pc_hwStatus>
</PC>
</PCs>";
}
}
}
Method .Element returns the first element with the specified name.
You can get the whole list with method .Elements, and iterate this list to find the one you are looking for.
I have an xml like :
<RunResult>
<PreviewRecords></PreviewRecords>
<RecordsProcessed>100</RecordsProcessed>
<LogError>false</LogError>
</RunResult>
I am using the following command to fetch the value of the node RecordsProcessed ,
int NofRecords = 0;
NofRecords = Convert.ToInt32(xdRunResultDoc.Root.Element("RunResult").Element("RecordsProcessed").Value;
But at this line it is throwing exception " object reference not set to an instance of an object".
Please suggest where I am going wrong.
xdRunResultDoc.Root already points to <RunRdesult> element, so you don't have to call Element("RunResult") again.
And I suggest using (int) casting on XElement instead of Convert.ToInt32: XElement Explicit Conversion (XElement to Int32)
int NofRecords = (int)xdRunResultDoc.Root.Element("RecordsProcessed");
Try this
int NofRcord = Convert.ToInt32(xdRunResultDoc.Root.Element("RecordsProcessed").Value);
See this link for more details http://msdn.microsoft.com/en-us/library/bb675196.aspx
this works for me:
string xmlstring = #"<?xml version='1.0' encoding='utf-8'?>
<RunResult>
<PreviewRecords></PreviewRecords>
<RecordsProcessed>100</RecordsProcessed>
<LogError>false</LogError>
</RunResult>";
System.Xml.Linq.XDocument doc = XDocument.Parse(xmlstring);
int NofRecords = 0;
NofRecords = Convert.ToInt32(doc.Element("RunResult").Element("RecordsProcessed").Value);
You can Find the solution by your self, Why don't you debug and see which causing "object reference not set to an instance of an object" error?
You can add watch to xdRunResultDoc and in the run time you can check what is root node and what you get for xdRunResultDoc.Root.Element("RunResult") etc..
Here your root node is RunResult you can't find Elements inside root node called RunResult
You better check this code project tutorial.
Try this code
read entire text from the xml file and load that to a XmlDocument
XmlNode rootNode = xDoc.SelectSingleNode("RunResult");
if (rootNode.HasChildNodes)
{
foreach (XmlNode node in rootNode.ChildNodes)
{
if (node.Name =="RecordsProcessed")
{
NofRecords=Convert.ToInt32(node.InnerText);
}
}
}
I was working on a bunch of XMLs that all share an attribute that contains the string "name" in them. The following code selects the attribute with string "name" in it and assign a new value to it.
public void updateXmlFile(string strFileName)
{
try
{
//Load the Document
XmlDocument doc = new XmlDocument();
doc.Load(strFileName);
//Set the changed Value
string newValue = GetUniqueKey();
//Select all nodes in the XML then choose from them
//the first node that contain string "name" in it
XmlNodeList list = doc.SelectNodes("//#*");
XmlNode filteredNode = list.Cast<XmlNode>()
.First(item => item.Name.ToLower().Contains("name"));
//Assign the newValue to the value of the node
filteredNode.Value = newValue;
doc.Save(strFileName);
}
catch (XmlException xex) { Console.WriteLine(xex); }
}
Now a new XMLs were added that dosen't have the string "name" in them, so instead of modifying the attribute with string "name" in it I decided to simply modify the last attribute no matter what it was (not the first)
Can anybody tell me how to do that?
EDIT
Here is an example of my XML
<?xml version="1.0" encoding="utf-8"?>
<CO_CallSignLists Version="24" ModDttm="2010-09-13T06:45:38.873" ModUser="EUADEV\SARE100" ModuleOwner="EUADEVS06\SS2008" CreateDttm="2009-11-05T10:19:31.583" CreateUser="EUADEV\A003893">
<CoCallSignLists DataclassId="E3FC5E2D-FE84-492D-AD94-3ACCED870714" EntityId="E3FC5E2D-FE84-492D-AD94-3ACCED870714" MissionID="4CF71AB2-0D92-DE11-B5D1-000C46F3773D" BroadcastType="S" DeputyInSpecialList="1" SunSpots="1537634cb70c6d80">
<CoCallSigns EntityId="DEBF1DDB-3C92-DE11-A280-000C46F377C4" CmdID="C45F3EF1-1292-DE11-B5D1-000C46F3773D" ModuleID="6CB497F3-AD63-43F1-ACAE-2C5C3B1D7F61" ListType="HS" Name="Reda Sabassi" Broadcast="INTO" PhysicalAddress="37" IsGS="1" HCId="0" CommonGeoPos="1" GeoLat="0.0000000" GeoLong="0.0000000">
<CoRadios EntityId="E1BF1DDB-3C92-DE11-A280-000C46F377C4" RadioType="HF" />
</CoCallSigns>
</CoCallSignLists>
</CO_CallSignLists>
#Alex: You notice that the "SunSpots" attribute (last attribute in the first child element) is successfully changed. But now when I wanna load the XML back into the DB it gives me an error
Here is the modified code
public void updateXmlFile(string strFileName)
{
try
{
XDocument doc = XDocument.Load(strFileName);
XAttribute l_attr_1 = (doc.Elements().First().Elements().First().Attributes().Last());
l_attr_1.Value = GetUniqueKey();
Console.WriteLine("Name: {0} Value:{1}", l_attr_1.Name, l_attr_1.Value);
doc.Save(strFileName);
}
catch (XmlException xex) { Console.WriteLine(xex); }
}
I was thinking of making an if statment which checks if the XML has an attribute that contains string "name" in it (since most of my XMLs has an attribute that contains name in them) if it does then change the attribute's value if not look for the last attribute and change it.. not the best solution but just throwing it out there
Then definitely use Linq to XML.
Example:
using System.Xml.Linq;
string xml = #"<?xml version=""1.0"" encoding=""utf-8""?>
<Commands Version=""439"" CreateUser=""Reda"">
<CmCommands DataclassId=""57067ca8-ef96-4d2e-a085-6bd7e8b24126"" OrderName = ""Tea"" Remark=""Black"">
<CmExecutions EntityId=""A9A5B0F2-6AB4-4619-9106-B0F85F86EE01"" Lock=""n"" />
</CmCommands>
</Commands>";
XDocument x = XDocument.Parse(xml);
Debug.Print(x.Elements().First().Elements().First().Attributes().Last().Value);
// Commands ^ CmCommands ^ Remark ^
That is, word for word, the last attribute of the first child of the first element.
You can also query for element/attribute names, like:
Debug.Print(x.Descendants(XName.Get("CmCommands", "")).First().Attribute(XName.Get("Remark", "")).Value);
And of course you can use all of the Linq goodness like Where, Select, Any, All etc.
Note: replace XDocument.Parse with XDocument.Load if appropriate etc.
I've not tested this but you should be able to do all of this in the XPath expression. Something like this:
//#*[contains(node-name(.), 'name')][last()]
This will return only the last attribute with the string name anywhere in its name.
If you only want the last attribute, irrespective of it's name, use this:
//#*[last()]
Look at class XmlAttributeCollection. You can get this collection by reading property Attributes of XmlNode. Just get the last by index.
Instead of .First(), use an extension method like this:
public static T LastOrDefault<T>(this IEnumerable<T> list)
{
T val = null;
foreach(T item in list)
{
val = item;
}
return val;
}