I have seen a couple of examples on here where Xpath is used in conjunction with XmlDocument to get a specific attribute from an XmlDocument Node.... Example
Console.WriteLine(xmlDocument.SelectSingleNode("//dataTemplateSpecification/templates/template/elements/element/#name").Value.ToString());
For some reason I am getting a "Object reference not set to an instance of an object." exception. Whenever I run across that particular line of code. I have a little test app that I have set up to test out different things before I put them into my main project...
Here is the code for that...
namespace ReadXml
{
class Program
{
static void Main(string[] args)
{
//string fulXmlPath = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/templateExample.xml");
XDocument xDocument = XDocument.Load("C:\\Users\\derekww\\Documents\\XML Documents\\templateExample.xml");
XElement elem = xDocument.Element("dataTemplateSpecification"); ;
XmlDocument xmlDocument = new XmlDocument();
StreamReader file = new StreamReader("C:\\Users\\derekww\\Documents\\XML Documents\\templateExample.xml");
xmlDocument.Load(file);
//XmlDocument theDoc = new XmlDocument();
//using (var xmlReader = xDocument.CreateReader())
//{
// xmlDocument.Load(xmlReader);
//}
//Console.WriteLine(elem.ToString());
XmlNode xNode = xmlDocument.SelectSingleNode("//dataTemplateSpecification/templates/template/elements/element");
Console.WriteLine("WORK PLEASE!!!! {0}", xNode.Value.ToString());
//Console.WriteLine(xmlDocument.SelectSingleNode("//dataTemplateSpecification/templates/template/elements/element/#name").Value.ToString());
//Console.WriteLine("This better Work>>>> {0}", xmlDocument.Attributes["/dataTemplateSpecification/templates/template/elements/element/#name"].Value);
Console.ReadLine();
//Console.WriteLine("This better Work>>>> {0}", xmlDocument.SelectSingleNode("//dataTemplateSpecification/templates/template/elements/element/#name").Value);
//foreach (String AttVal in xmlDocument.SelectSingleNode("//dataTemplateSpecification/templates/template/elements/element/#name").Value)
{
//Console.WriteLine("This better Work>>>> {0}", AttVal);
}
}
}
}
Here is part of the XML that I used...
<?xml version="1.0" encoding="utf-8"?>
<dataTemplateSpecification id="id1" name="name1" xmlns="http://EADIS.upmc.com /DataTemplateSpecification.xsd">
<description xmlns="">
<html>text</html>
</description>
<templates xmlns="">
<template>
<elements>
<element id="element0" name="PatientId" display="Patient ID" dataType="String" value="0101010111111" visable="false" readOnly="true">
<validation>
<rules>
<rule id="0" test="#element0.value == ''">
<fail>
<html><b>Patient ID is null, value must be present</b></html>
</fail>
</rule>
</rules>
</validation>
</element>
</elements>
</template>
<templates>
I just showed you the part that you need to understand the xml structure. I assure you that it is well formed. I think I asked this question before but somehow or the other it didn't get posted (maybe I forgot, who knows). Any help with this would be greatly appreciated. If I come up with a reason for why it isn't working I will be sure to let you guys know.
Thank You.
Why can't you use this XPath:
xmlDocument.SelectSingleNode("//templates/template/elements/element/#name").Value
You need to specify the namespace of the XML file in your code.
See here for more info: How to select xml root node when root node has attribute?
Related
I'm struggling to read in a "GPX" file to a WPF (c#) project. Sample GPX is provided below. I've tried a number of different options with the same result.
Document is loading ok, but I'm unable to break it down to access the Nodes Directly.
Any help would be greatly appreciated.
Thanks.
private void Simple_Click(object sender, RoutedEventArgs e)
{
XmlDocument xml = new XmlDocument();
xml.Load(#"C:\Users\Jonathon\Desktop\GPX_Data.gpx");
XmlNodeList nodes = xml.SelectNodes("trkpt"); // have tried: double '/' to get nodes at any level (XPath syntax)
//XmlNodeList nodes = xml.SelectNodes("/gpx/trk/trkseg/trkpt");
int count = 0;
foreach (XmlNode xn in nodes)
{
count++;
}
}
}
Sample GPX File
<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="Endomondo.com"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1
http://www.topografix.com/GPX/1/1/gpx.xsd
http://www.garmin.com/xmlschemas/GpxExtensions/v3
http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd
http://www.garmin.com/xmlschemas/TrackPointExtension/v1
http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd"
xmlns="http://www.topografix.com/GPX/1/1"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<metadata>
<author>
<name>Jonathon Ralfe</name>
<email id="jonathon" domain="ralfe.net"/>
</author>
<link href="http://www.endomondo.com">
<text>Endomondo</text>
</link>
<time>2015-01-27T18:31:26Z</time>
</metadata>
<trk>
<src>http://www.endomondo.com/</src>
<link href="https://www.endomondo.com/workouts/463986953/2256850">
<text>endomondo</text>
</link>
<type>SKIING_DOWNHILL</type>
<trkseg>
<trkpt lat="45.576892" lon="6.894079">
<time>2015-01-26T09:49:57Z</time>
</trkpt>
<trkpt lat="45.576892" lon="6.894079">
<ele>1595.0</ele>
<time>2015-01-26T09:49:59Z</time>
</trkpt>
<trkpt lat="45.577109" lon="6.893946">
<ele>1581.0</ele>
<time>2015-01-26T09:51:46Z</time>
</trkpt>
<trkpt lat="45.5772" lon="6.894084">
<ele>1575.0</ele>
<time>2015-01-26T09:52:02Z</time>
</trkpt>
<trkpt lat="45.577247" lon="6.894212">
<ele>1577.0</ele>
<time>2015-01-26T09:52:05Z</time>
</trkpt>
<trkpt lat="45.577317" lon="6.89452">
<ele>1589.0</ele>
<time>2015-01-26T09:52:11Z</time>
</trkpt>
That's because your xml contains namespaces, so you have to set namespace when querying data.
Consider this approach:
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xml.NameTable);
nsmgr.AddNamespace("x", "http://www.topografix.com/GPX/1/1");
XmlNodeList nodes = xml.SelectNodes("//x:trkpt", nsmgr);
Here we're creating NamespaceManager, setting namespace according to your data xmlns="http://www.topografix.com/GPX/1/1" attribute and using this namespace in XPath.
By XPath selection:
foreach(XElement aElement in xml.XPathSelectElements("/trk/trkseg").Elements())
{
foreach(XNode aXNode in aElement.Nodes())
{
//Access subnodes of trkpt
}
}
I'm trying to read the text in between <keyMaterial></keyMaterial>
I tried using //WLANProfile/MSM/security/sharedKey as the element route, seen in the code below. It refuses to return a value. I have run through the debugger and after the breakpoint at the line: XmlNodeList sharedKeyNodes = wifiProfile.SelectNodes("//WLANProfile/MSM/security/sharedKey");
the SharedKeyNodes object doesn't return a count for. I know it's just a matter of figuring out the element route so I'm not coming here completely hopeless...
System.Xml.XPathNodeList
My XML looks like this:
<?xml version="1.0"?>
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
<name>nosignal</name>
<SSIDConfig>
<SSID>
<hex>6E6F7369676E616C</hex>
<name>nosignal</name>
</SSID>
<nonBroadcast>true</nonBroadcast>
</SSIDConfig>
<connectionType>ESS</connectionType>
<connectionMode>auto</connectionMode>
<autoSwitch>false</autoSwitch>
<MSM>
<security>
<authEncryption>
<authentication>WPA2PSK</authentication>
<encryption>AES</encryption>
<useOneX>false</useOneX>
<FIPSMode xmlns="http://www.microsoft.com/networking/WLAN/profile/v2">false</FIPSMode>
</authEncryption>
<sharedKey>
<keyType>passPhrase</keyType>
<protected>true</protected>
<keyMaterial>01000000D</keyMaterial>
</sharedKey>
</security>
</MSM>
</WLANProfile>
[ EDIT with the help of LB the new code looks like this and it WORKS! ]
[ For anyone that is struggling with a similar problem. ]
My Class is:
class ProfileManager
{
public static string readProfile() {
XmlDocument wifiProfile = new XmlDocument();
string path = #"C:\temp\nosignal.xml";
string password = "";
wifiProfile.Load(path);
XmlNamespaceManager mgr = new XmlNamespaceManager(wifiProfile.NameTable);
mgr.AddNamespace("ns", "http://www.microsoft.com/networking/WLAN/profile/v1");
XmlNodeList sharedKeyNodes = wifiProfile.SelectNodes("//ns:WLANProfile/ns:MSM/ns:security/ns:sharedKey", mgr);
foreach (XmlNode itemNode in sharedKeyNodes)
{
XmlNode keyMaterialNode = itemNode.SelectSingleNode("ns:keyMaterial", mgr);
if (keyMaterialNode != null)
{
password = keyMaterialNode.InnerText;
}
}
return password;
}
}
I'm close, but still just a bit stuck. Any help would be appreciated!!! Thank you!
You don't use the default XmlNamespace "http://www.microsoft.com/networking/WLAN/profile/v1"
wifiProfile.Load(path);
XmlNamespaceManager mgr = new XmlNamespaceManager(wifiProfile.NameTable);
mgr.AddNamespace("ns", "http://www.microsoft.com/networking/WLAN/profile/v1");
XmlNodeList sharedKeyNodes = wifiProfile.SelectNodes("//ns:WLANProfile/ns:MSM/ns:security/ns:sharedKey",mgr);
Okay, this had me occupied for several hours now and still I have no explanation for it:
My XML starts like this:
<?xml version="1.0" encoding="iso-8859-1"?>
<ISO15745Profile xmlns="http://www.profibus.com/GSDML/2003/11/DeviceProfile" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.profibus.com/GSDML/2003/11/DeviceProfile ..\XSD\GSDML-DeviceProfile-v2.1.xsd">
<ProfileHeader>
<ProfileIdentification>PROFINET Device Profile</ProfileIdentification>
<ProfileRevision>1.00</ProfileRevision>
<ProfileName>Device Profile for PROFINET Devices</ProfileName>
<ProfileSource>PROFIBUS Nutzerorganisation e. V. (PNO)</ProfileSource>
<ProfileClassID>Device</ProfileClassID>
<ISO15745Reference>
<ISO15745Part>4</ISO15745Part>
<ISO15745Edition>1</ISO15745Edition>
<ProfileTechnology>GSDML</ProfileTechnology>
</ISO15745Reference>
</ProfileHeader>
<ProfileBody>
<DeviceIdentity DeviceID="0x000A" VendorID="0x00B0">
<InfoText TextId="InfoTextId1"/>
<VendorName Value="Phoenix Contact GmbH"/>
</DeviceIdentity>
<DeviceFunction>
<Family MainFamily="I/O" ProductFamily="Inline"/>
</DeviceFunction>
<ApplicationProcess>
<DeviceAccessPointList>
<DeviceAccessPointItem ID="DIM 1" FixedInSlots="0" PhysicalSlots="0..64" MinDeviceInterval="32" ModuleIdentNumber="0x00000300" DNS_CompatibleName="IL-PN-BK-2TX" ImplementationType="ERTEC200" ObjectUUID_LocalIndex="1">
<ModuleInfo>
<Name TextId="IL PN BK DI8 DO4 2TX"/>
<InfoText TextId="InfoTextId1"/>
<VendorName Value="Phoenix Contact"/>
<OrderNumber Value="2703994"/>
</ModuleInfo>
<SubslotList>
<SubslotItem SubslotNumber="32768" TextId="SubSlot_Interface"/>
<SubslotItem SubslotNumber="32769" TextId="SubSlot_Port1"/>
<SubslotItem SubslotNumber="32770" TextId="SubSlot_Port2"/>
</SubslotList>
<IOConfigData MaxInputLength="512" MaxOutputLength="512"/>
<UseableModules>
<ModuleItemRef FixedInSlots="1" ModuleItemTarget="1"/>
<ModuleItemRef AllowedInSlots="4..64" ModuleItemTarget="2"/>
<ModuleItemRef AllowedInSlots="4..64" ModuleItemTarget="3"/>
<ModuleItemRef AllowedInSlots="4..64" ModuleItemTarget="4"/>
<ModuleItemRef AllowedInSlots="4..64" ModuleItemTarget="5"/>
<ModuleItemRef AllowedInSlots="4..64" ModuleItemTarget="6"/>
...
Now what I'm trying to do is work with the AllowedInSlots, but when creating a XmlNodeList with
XmlDocument gsdml = new XmlDocument();
gsdml.Load(fpfad);
XmlNodeList ModuleItemRef = gsdml.SelectNodes("/ISO15745Profile/ProfileBody/ApplicationProcess/DeviceAccessPointList/DeviceAccessPointItem/UseableModules");
that XmlNodeList remains empty. What am I doing wrong? I thought that maybe I had to work with the Namespacemanager and tried that, but that didn't do anything.
This is what I tried earlier:
XmlDocument gsdml = new XmlDocument();
gsdml.Load(fpfad);
XmlNamespaceManager mgr = new XmlNamespaceManager(gsdml.NameTable);
mgr.AddNamespace("iso", "http://www.profibus.com/GSDML/2003/11/DeviceProfile");
XmlNodeList ModuleItemRef = gsdml.SelectNodes("/iso:ISO15745Profile/ProfileBody/ApplicationProcess/DeviceAccessPointList/DeviceAccessPointItem/UseableModules", mgr);
It didn't work, though, so something there has to be wrong.
2nd Edit:
Including the prefix with every part of the path did the trick.
It is indeed the namespace manager.
xmlns="http://www.profibus.com/GSDML/2003/11/DeviceProfile"
means that everything is in that namespace (unless another default is declared, or an element declares its own namespace). You will need to use a namespace manager to search in that namespace.
i wish to parse following XML
<?xml version="1.0" encoding="UTF-8"?>
<product xmlns="http://products.org">
<make xmlns="http://camera.org">
<model>Camry</model>
</make>
<make xmlns="http://tv.org">
<model>Sony</model>
</make>
</product>
Code written to parse it
This is how i m writing Parsing Code
but in last i m getting null inxmlNode object. Can u tell what more to do .
You can't ignore namespaces in XPath.* The elements in your document all have non-blank namespace URI's.
Your question title indicates you're on the right track: you need to explicitly bind the URI's to prefixes using an XmlNamespaceManager, and use those prefixes in your path expressions.
This program is tested against your input document
using System;
using System.Xml;
public class XPathNamespace
{
public static void Main() {
XmlDocument doc = new XmlDocument();
doc.Load("test1.xml");
XmlNamespaceManager xnm = new XmlNamespaceManager(doc.NameTable);
xnm.AddNamespace("p", "http://products.org");
xnm.AddNamespace("c", "http://camera.org");
xnm.AddNamespace("t", "http://tv.org");
ShowNode(doc.SelectSingleNode("/p:product", xnm));
ShowNode(doc.SelectSingleNode("/p:product/c:make", xnm));
ShowNode(doc.SelectSingleNode("/p:product/t:make", xnm));
}
private static void ShowNode(XmlNode node) {
Console.WriteLine("<{0}> {1}",
node.LocalName,
node.NamespaceURI);
}
}
and it produces the following output
<product> http://products.org
<make> http://camera.org
<make> http://tv.org
Hope this helps.
(*) This doesn't mean you can't ignore the exact namespace in your XPath. For example, you could match
/*[local-name()='product']
But that's a workaround and illustrates that you still have to deal with the presence of a namespace somehow or other.
This is the best example for you. Please have a look.
http://www.codeproject.com/KB/cpp/myXPath.aspx
<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode url="~/" title="Úvodní stránka">
<siteMapNode url="Pocitace" title="Počítače" />
<siteMapNode url="Elektronika" title="Elektronika" />
</siteMapNode>
</siteMap>
And I write to this file new data:
XmlDocument originalXml = new XmlDocument();
originalXml.Load(Server.MapPath("../../Web.sitemap"));
XmlAttribute title = originalXml.CreateAttribute("title");
title.Value = newCategory;
XmlAttribute url = originalXml.CreateAttribute("url");
url.Value = seoCategory;
XmlNode newSub = originalXml.CreateNode(XmlNodeType.Element, "siteMapNode", null);
newSub.Attributes.Append(title);
newSub.Attributes.Append(url);
originalXml.SelectSingleNode("siteMapNode").AppendChild(newSub);
But I get:
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error:
Line 49: newSub.Attributes.Append(title);
Line 50: newSub.Attributes.Append(url);
Line 51: originalXml.SelectSingleNode("siteMapNode").AppendChild(newSub);
Line 51 si red. Can u help me?
(Web.sitemap i have in root file and code I have in Someting/Someting/Someting.aspx, so adrress is correct i think.)
The call to originalXml.SelectSingleNode("siteMapNode") returns null. You need to specify the namespace.
Update:
Use this code instead of the line that throws the exception (Line 51):
XmlNamespaceManager nsmanager = new XmlNamespaceManager(originalXml.NameTable);
nsmanager.AddNamespace("x", "http://schemas.microsoft.com/AspNet/SiteMap-File-1.0");
originalXml.SelectSingleNode("x:siteMap/x:siteMapNode", nsmanager).AppendChild(newSub);
Explanation:
You made two mistakes:
Your XPath query to find the siteMapNode was not correct. The way you wrote it, it looked only at the root tag for the tag with the name "siteMapNode"
The root tag "siteMap" specifies a namespace. You need to use that namespace in your call to SelectSingleNode
I think, the xpath you give to the SelectSingleNode is wrong and it will return with null.