Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xelement.XPathSelectElement

Tags:

c#

xpath

xelement

I have the following XPath String

"(//DEAL_SETS/DEAL_SET/DEALS/DEAL/PARTIES/PARTY[ROLES/ROLE/
    ROLE_DETAIL/PartyRoleType='Borrower'])  
        [(ROLES/PARTY_ROLE_IDENTIFIERS/PARTY_ROLE_IDENTIFIER/
            PartyRoleIdentifier='1' or position() = 1)]/ROLES/ROLE/BORROWER/
                RESIDENCES/RESIDENCE/ADDRESS/PostalCode[../../RESIDENCE_DETAIL/
                    BorrowerResidencyType='Current']"

which works when I put it in Altova XML Spy and gives me a result.

But when I use it directly as it is Xelement.XPathSelectElement(XPath), it does not work, but what works is Xelement.XPathSelectElement(collective, nameSpaceManager) where collective is namesspace prefix (say named "ns") plus my XPath string.

But the problem is I have to change XPath string to something like this

"(//ns:DEAL_SETS/ns:DEAL_SET/ns:DEALS/ns:DEAL/ns:PARTIES/ns:PARTY[ns:ROLES/
    ns:ROLE/ns:ROLE_DETAIL/ns:PartyRoleType='Borrower'])[(ns:ROLES/
        ns:PARTY_ROLE_IDENTIFIERS/ns:PARTY_ROLE_IDENTIFIER/
            ns:PartyRoleIdentifier='1' or position() = 1)]/ns:ROLES/
                ns:ROLE/ns:BORROWER/ns:RESIDENCES/ns:RESIDENCE/
                    ns:ADDRESS/ns:PostalCode[../../ns:RESIDENCE_DETAIL/
                        ns:BorrowerResidencyType='Current']"

Is there any way to avoid having to put the namespace prefix (ns:) at each node.

Sorry for not posting the sample xml earlier,you may replicate the Party tag and its elements and fill up with false data, PartyRoleType gives me what party it is could be borrower,appraiser etc partyroleidentifier gives me a way to differentiate between two party with same partyroletypes for eg there could be 2 borrowers,the partyroleidentifier differentiates them as 1 and 2

<?xml version="1.0" encoding="utf-8"?>
<MESSAGE xmlns="http://www.example.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xxxxReferenceModelIdentifier="3.0.0.263.12" xsi:schemaLocation="http://www.example.org/schemas C:\Subversion\xxx_3_0.xsd">
 <DEAL_SETS>
    <DEAL_SET>
      <DEALS>
        <DEAL>
          <PARTIES>
            <PARTY>
              <ROLES>
                <ROLE>
                  <PARTY_ROLE_IDENTIFIERS>
                     <PARTY_ROLE_IDENTIFIER>
                       <PartyRoleIdentifier>1</PartyRoleIdentifier>
                     </PARTY_ROLE_IDENTIFIER>
                  </PARTY_ROLE_IDENTIIERS>
                  <BORROWER>
                    <RESIDENCES>
                      <RESIDENCE>
                        <ADDRESS>
                        <PostalCode>56236</PostalCode>
                        </ADDRESS>
                      </RESIDENCE>
                    </RESIDENCES>
                  </BORROWER>
                  <ROLE_DETAIL>
                    <PartyRoleType>Borrower</PartyRoleType>
                  </ROLE_DETAIL>
                </ROLE>
              </ROLES>
            </PARTY>
          </PARTIES>
        </DEAL>
      </DEALS>
    </DEAL_SET>
  </DEAL_SETS>
</MESSAGE>
like image 314
user602182 Avatar asked Dec 14 '11 17:12

user602182


2 Answers

According to the MSDN documentation, when you call AddNamespace for your namespace manager, you can use String.Empty as the prefix for your namespace URI. That should enable you to do an XPath expression without all those ns: prefixes.

Update:

OK, I remember encountering this issue before. The behavior of empty namespace prefixes in XPath expressions is not consistent with the rest of the .NET XML suite. There is some evidence that some Microsoft folks are aware of the issue. See this forum thread. I'll look for a while more for solutions, but I'm thinking that you'll have to make do with using namespace prefixes.

Update 2:

Feel free to vote on this Microsoft Connect Bug that I've just created. I see that they have closed similar bugs in the past, but none of them focused on usability concerns before. Maybe we'll get some traction with more votes.

Update 3:

Just some clarifications, my filed bug is not and never was about compliance with XPath standards, but rather about usability. No matter the standards, users need a way to make XPath queries with namespaced XML more succinct, and that is currently not supported. As far as standards support goes, XSLT 2.0 has a way to do XPath 2.0 with empty namespace prefixes associated with a namespace URI, and XMLSpy has that capability as well. The MSDN documentation claims that .NET has XPath 2.0 support, but the veracity of that claim is under dispute. One could argue that .NET is properly implementing XPath 1.0 by disallowing the modification of what an empty prefix represents.

like image 83
Jacob Avatar answered Oct 21 '22 12:10

Jacob


The currently accepted answer is wrong !

Any compliant XPath processor interprets a non-prefixed name to belong to "no-namespace".

This is the biggest FAQ in XPath:

There is no way in XPath to select elements in a default namespace, by using their un-prefixed name.

The reason is that XPath considers any unprefixed name to reside in "no namespave" and it is looking to find elements with such name that are in "no namespace" Such elements aren't found and selected, because all elements are in a non-empty, default namespace.

Here is a precise quote from the W3C XPath 1.0 specification:

"if the QName does not have a prefix, then the namespace URI is null".

There are two main solutions:

  1. Using the XPath-related API available in your programming language, register an association between the default namespace and a string prefix (lets say "x"). Then modify your wxpression and replace anyunprefixed someElementName with x:someElementName. In .NET, to register the association, an XmlNamespaceManager object typically has to be created, and its AddNamespace() method needs to be used for registering the association. Then, when calling the XPath-selecting method, the XmlNamespaceManager instance is passed as an additional argument.

  2. If one wants to avoid registering the default namespace, one workaround is, instead of:

..

/a/b/c

to use:

/*[name()='a']/*[name()='b']/*[name()='c']
like image 41
Dimitre Novatchev Avatar answered Oct 21 '22 13:10

Dimitre Novatchev