Daniel
Daniel

Reputation: 1481

Select single XML node from document

I'm trying to load some values from an XML document returned by a Philips Hue Bridge. Here is an example of the XML I'm trying to parse (example taken from Philips' website):

<root xmlns="urn:schemas-upnp-org:device-1-0">
    <specVersion>
        <major>1</major>
        <minor>0</minor>
    </specVersion>
    <URLBase>https://192.168.1.130:80/</URLBase>
    <device>
        <deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>
        <friendlyName>Philips hue (192.168.1.130)</friendlyName>
        <manufacturer>Royal Philips Electronics</manufacturer><manufacturerURL>https://www.philips.com</manufacturerURL>
        <modelDescription>Philips hue Personal Wireless Lighting</modelDescription>
        <modelName>Philips hue bridge 2012</modelName>
        <modelNumber>929000226503</modelNumber>
        <modelURL>https://www.meethue.com</modelURL>
        <serialNumber>001788102201</serialNumber>
        <UDN>uuid:2f402f80-da50-11e1-9b23-001788102201</UDN>
        <presentationURL>index.html</presentationURL>
        <iconList>
            <icon>
                <mimetype>image/png</mimetype>
                <height>48</height>
                <width>48</width>
                <depth>24</depth>
                <url>hue_logo_0.png</url>
            </icon>
            <icon>
                <mimetype>image/png</mimetype>
                <height>120</height>
                <width>120</width>
                <depth>24</depth>
                <url>hue_logo_3.png</url>
            </icon>
        </iconList>
    </device>
</root>

I've looked at several examples online and tried various paths, but the result is always null. I've put multiple examples in comments to show you some of the things I've tried.

var xml = webClient.DownloadString($"http://{bridge.internalipaddress.ToString()}/description.xml");
XmlDocument bridgeDetails = new XmlDocument();
bridgeDetails.LoadXml(xml);
XmlNode root = bridgeDetails.DocumentElement;
var friendlyName = bridgeDetails.SelectSingleNode("//device/friendlyName");
//var friendlyName = bridgeDetails.SelectSingleNode("/device/friendlyName");
//var friendlyName = bridgeDetails.SelectSingleNode("device/friendlyName");
//var friendlyName = bridgeDetails.SelectSingleNode("descendant::device/friendlyName");
//var friendlyName = root.SelectSingleNode("//device/friendlyName");
//var friendlyName = root.SelectSingleNode("/device/friendlyName");
//var friendlyName = root.SelectSingleNode("device/friendlyName");
//var friendlyName = root.SelectSingleNode("descendant::device/friendlyName");
//I've also tried adding .Value.ToString() at the end of some of these, but that just caused a null reference exception

EDIT:

I've worked out that I'm missing the namespace. But I'm not sure how as there is no URL to the namespace. E.g.

var nsmgr = new XmlNamespaceManager(bridgeDetails.NameTable);
nsmgr.AddNamespace("upnp", "urn:schemas-upnp-org:device-1-0");

XmlNode root = bridgeDetails.DocumentElement;
var friendlyName = bridgeDetails.SelectSingleNode("//upnp:device/friendlyName");

Any help would be appreciated.

Upvotes: 1

Views: 88

Answers (1)

zx485
zx485

Reputation: 29042

Your solution after adding the namespace was close.
Now use the following XPath-1.0 expression, because all children or <root> are in this namespace:

//upnp:device/upnp:friendlyName

In complete, this should look like

var friendlyName = bridgeDetails.SelectSingleNode("//upnp:device/upnp:friendlyName", nsmgr);

and return you the value

Philips hue (192.168.1.130)


Relating to your statement

But I'm not sure how as there is no URL to the namespace.

The namespace URI does not have to be a valid URL. It just has to be a unique string (in your "name"-space) and it doesn't have to point to a retrievable file. That's not neccessary.

Upvotes: 1

Related Questions