views:

4534

answers:

8

Hey there!

I am having the problem that I cannot select a specific XML node which needs to be deleted. I have already tried to select the node by using the XPath which works fine for some XML files but I cannot figure out the correct XPath for a node in a more complex file.

Does anybody know a freeware tool which can load a XML file so that the user can select a specific node and receives the accurate XPath without having an enumeration in the path?

"/root/anything[2]" <-- unfortunatly I cannot use such a statement because the number of the element might change. I need an expression that is based on an attribute.

In case that there is no freeware tool for this operation, does anybody know another way how I can select the needed node?

XML Sample:

Root Node: SmsFormData

Attributes: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" FormatVersion="1.0" xmlns="http://schemas.microsoft.com/SystemsManagementServer/2005/03/ConsoleFramework"

Child node: Form

Attributes: Id="some GUID" CustomData="Some data" FormType="some type" ForceRefresh="false"

Child/Child node: Pages

Child/Child/Child node: Page

Attributes: VendorId="VendorName" Id="some GUID" Assembly="dll File name" Namespace="some Namespace" Type="some Type" HelpID="">

My xPath expression to select this specific page would now be:

xPath = "/SmsFormData/Form/Pages/Page[@Id="some Guid"]"

To do the selection I am using the following vbscript code:

Set objDOM = CreateObject("Msxml2.DOMDocument.4.0") objDOM.async = false objDOM.load(file) set objNode = objDOM.selectSingleNode(xPath)The problem is now that the objNode object is empty. The node is not selected, but why?

Thanks in advance!

A: 

Given the following XML:

<root>
  <anything foo="bar">value1</anything>
  <anything foo="qux">value2</anything>
</root>

...you can obtain the value of the second anything node using the XPath expression:

/root/anything[@foo="qux"]

(so, instead of the numbered index, you use @property="value" as the selector).

As for a tool to generate queries like this automagically, the aptly named Visual XPath should do the trick, and it's free (it even comes with C# source code).

Edit, after followup by poster: this form of XPath selection works as well for 'simple cases' as it does for the most complicated documents. You do have to make sure your XPath expression is correct, of course, and although Visual XPath will indeed use numeric indexes, it at least gives you the rest of the expression, and you can easily substitute the @property="value" selector for the number, and test the result.

Given the example XML above, this VBscript:

objDOM.selectSingleNode("/root/anything[@foo=""qux""]/text()").nodeValue

...returns the string "value2": depending on your needs, you may need to tweak things a little bit (again, tools like Visual XPath, or any good XPath reference will help you with that).

mdb
A: 

Thanks for your fast reply! This surely works in simple cases but this does not work in my specific case :(

So therefore lets go into the details:

Root Node: SmsFormData

Attributes: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" FormatVersion="1.0" xmlns="http://schemas.microsoft.com/SystemsManagementServer/2005/03/ConsoleFramework"

Child node: Form

Attributes: Id="some GUID" CustomData="Some data" FormType="some type" ForceRefresh="false"

Child/Child node: Pages

Child/Child/Child node: Page

Attributes: VendorId="VendorName" Id="some GUID" Assembly="dll File name" Namespace="some Namespace" Type="some Type" HelpID="">

My xPath expression to select this specific page would now be:

xPath = "/SmsFormData/Form/Pages/Page[@Id="some Guid"]"

To do the selection I am using the following vbscript code:

Set objDOM = CreateObject("Msxml2.DOMDocument.4.0") 

objDOM.async = false     
objDOM.load(file)    

set objNode = objDOM.selectSingleNode(xPath)

The problem is now that the objNode object is empty. The node is not selected, but why?

Oh and by the way: Thanks for the Visual XPath tip! I have tried using it but unfortunately it does the enumeration way :/

Marcus
You should really edit your question to include these details
AnthonyWJones
A: 

You need to set the Selection Language to XPath using

objDOM.SetProperty "SelectionLanguage", "XPath"

Once you have set this property you can then use full XPath to access any elements you want

Xetius
A: 

If you have the Firefox browser, you can simply install the DOM Inspector (required only for Firefox 3.0), and the XPather extensions. Then, you can traverse to the node you want in the DOM Inspector window, and the corresponding XPath will be displayed in the XPather toolbar in the same window.

DOM Inspector: https://addons.mozilla.org/en-US/firefox/addon/6622

XPather: https://addons.mozilla.org/en-US/firefox/addon/1192?id=1192

The XPather seems to use attributes (and not enumeration) whenever possible to identify nodes (atleast that's what I found in my little experimentation...). Hope that helps...

sundar
A: 

Hm,... I get the impression that the problem must be file based. Even if I set the property for the SelectionLanguage and if I use an enumerated XPath (which I got by using the FireFox XPather) the node Object still remains empty.

Does anybody has an idea what could be wrong? The xml file comes with an Microsoft application and therefore should be valid. At least I don't have any problems while opening the file or using it within the application so the syntax should be ok.

Or does maybe somebody have a vbscript function that iterates through the whole xml file to find the needed node in order to delete it?

Marcus
Please stop adding answers as if this were a forum discussion, stackoverflow has a different paradigm.
AnthonyWJones
A: 

You problem is that you have a default namespace. XPaths default name space is always the 'no name' name space.

You need:-

sNamespaces = "xmlns:cf='http://schemas.microsoft.com/SystemsManagementServer/2005/03/ConsoleFramework'"
objDOM.setProperty "SelectionNamespaces", sNamespaces

Now you can use in your XPath:-

xPath = "/cf:SmsFormData/cf:Form/cf:Pages/cf:Page[@Id=""some Guid""]"
AnthonyWJones
Thanks as well but the brackets need to be deleted.-> objDOM.setPropery "SelectionNamespaces", sNamespaces
Marcus
+2  A: 

This is a default namespace issue. Try including the following code after you load in the XML:

objDom.SetProperty "SelectionNamespaces", "xmlns:cf=""http://schemas.microsoft.com/SystemsManagementServer/2005/03/ConsoleFramework"""

You then use this cf prefix in your XPath eg:

objDom.SelectSingleNode("/cf:SmsFormData/cf:Form/cf:Pages/cf:Page[@Id='Some Guid']")

Whilst this may seem a bit quirky, it is intentional behaviour. Take a look at http://support.microsoft.com/kb/288147 for more information, and you may find http://msdn.microsoft.com/en-us/library/ms950779.aspx useful as well.

Luke Bennett
A: 

Not sure if it is any use to you, but I had a similar problem.

I had an app generated XML file that I needed to manage. It was of the format: <LoginData> <GeneralData> <LoginMask>65537</LoginMask> </GeneralData> <UserData> <User> <Username>TEST0</Username> ... </User> <User> <Username>TEST1</Username> ... </User> </UserData> </LoginData>

I needed to match on a Username and remove the other User tags.

Took me (being a complete XML noob) ages to work it out, but in the end I hit on matching on a node, and deleting the child of the parent node:

for each x in oXmlDoc.documentElement.selectSingleNode("UserData").childNodes if x.getElementsByTagName("Username").item(0).text = "TEST1" then set exx = x.getElementsByTagName("Username").item(0) wscript.echo(x.getElementsByTagName("Username").item(0).text) wScript.echo(x.nodename & ": " & x.text) x.parentNode.removeChild(x) end if next