XPath in SimpleXML for default namespaces without needing prefixes

后端 未结 3 472
逝去的感伤
逝去的感伤 2020-12-09 20:59

I have an XML document that has a default namespace attached to it, eg



        
3条回答
  •  攒了一身酷
    2020-12-09 21:14

    Is there a proper way to handle default namespaces without needing to using prefixes with XPath?

    No. The proper way to handle any namespace is to associate some value (a prefix) with that namespace so that it can be explicitly selected in the XPath expression. The default namespace is no different.

    Think about it this way: an element in some namespace and another element with the same name in some other namespace (or no namespace at all) are different elements. They could mean (i.e. represent) different things. That's the whole point. You need to tell XPath which one you want to select. Without it, XPath doesn't know what you're asking for.

    Adding the prefixes isn't going to be manageable in the long run.

    I really don't see why. Whatever creates the XPath expression should be capable of specifying a proper XPath expression (or it's a broken tool).

    You might be thinking, "why can't I just ignore the namespace and get all elements matching that name?" There are really hacky ways to do this (like the XSLT-based answer already posted), but they are broken by design. An element in XML is identified by the combination of its namespace and local name, just as your house can be identified with a street number (the local name) in some city and state (the namespace). If I tell you that I live on 422 Main St, then you still have no idea where I live until I tell you which city and state.

    You still might be thinking, "enough with the stupid analogies, I really, really want to do this anyway." You can select elements with a given name across all namespaces by matching only the local name portion of the element, like this:

    *[local-name()='level1']/*[local-name()='level2']
        /*[local-name()='level3' and @foo="bar"]/*[local-name()='level4' and 
            @foo="bar"]/*[local-name()='level5']/*[local-name()='level6'][2]');
    

    Note that this does not restrict itself to the default namespace. It ignores namespaces entirely. It's ugly and I don't recommend it, but sometimes you just want to ignore what's best and get something done.

    By the way, this is not PHP's fault. This is what the XPath spec requires. You have to specify a prefix to select a node in a namespace. If PHP were to allow you to do it some other way, then whatever they called it, it would no longer be XPath (according to the spec).

提交回复
热议问题