XPath Select 最上面的节点只包含一个无子节点

XPath Select the uppermost nodes that contains just one childless node

我正在寻找 .NET 中的 XPath,它只选择 最上面的节点,其中包含 只有一个 终端(无子)节点

A <--
    B
        C
D
    E <--
        F
    G <--
    H <--
        I 
            J


C, F, G, J are terminal

我想摆脱无用的嵌套项并尽可能推广终端:

C to A position; F to E position; G remains in place; ...

编辑

我想我已经接近

.//*[count(descendant-or-self::terminal)=1] [ not(ancestor::*[count(.//terminal)=1]) ]

终端可以选择

*[not(*)]

只是 //*[count(descendant-or-self::*)=1] 有什么问题吗?

% cat uppermost.xml 
<R>
  <A><B><C/></B></A>
  <D>
    <E><F/></E>
    <G/>
    <H><I><J/></I></H>
  </D>
</R>
% xpquery '//*[count(descendant-or-self::*)=1]' uppermost.xml
<C/>
<F/>
<G/>
<J/>
% 

我自己的解决方案:

.//*[count(descendant-or-self::terminal)=1] [ not(ancestor::*[count(descendant::terminal)=1]) ]

其中 terminal = *[not(*)]

第一个条件 [count(descendant-or-self::terminal)=1] 我们 select <-- 项。对于第二个条件 [ not(ancestor::*[count(descendant::terminal)=1]) ] 我们 select <··· items:

A             <--    <···     A
    B         <--
        C     <--
D                    <···
    E         <--    <···        E
        F     <--
    G         <--    <···        G
    H         <--    <···        H
        I     <--
            J <--

交叉路口是 { A E G H } 如所愿

实现终端项提升和移除one-childparents最好的方法是通过递归的方法:

Public Sub XmlTrimOneChildParents(ByRef xE As XmlElement)
    Dim xNodeList As XmlNodeList = xE.SelectNodes("./*")
    Select Case xNodeList.Count
        Case 0 'terminal
        Case 1 'Remove And Promote Child
            Dim xChild As XmlNode = xNodeList(0)
            XmlTrimOneChildParents(xChild)
            xE.ParentNode.InsertBefore(xChild, xE)
            xE.ParentNode.RemoveChild(xE)
        Case else
            For Each xChild As XmlNode In xNodeList
                XmlTrimOneChildParents(xChild)
            Next
    End Select
End Sub