简体   繁体   English

使用自定义属性(在这种情况下为名称)为XML中的每个节点生成XPath

[英]Generate XPath for each node in XML, using custom attribute (name in this case)

I have a huge XML file, that look like this (but is much larger): 我有一个巨大的XML文件,看起来像这样(但更大):

<?xml version="1.0" encoding="UTF-8"?>
<suite id="1" name="SuiteName">
  <displayNameKey>something</displayNameKey>
  <displayName>something</displayName>
  <application id="2" name="Manager">
    <displayNameKey>appName</displayNameKey>
    <displayName>appName</displayName>
    <category id="12" name="navigation">
      <displayNameKey>managerNavigation</displayNameKey>
      <displayName>managerNavigation</displayName>
      <description>mgr_navigation</description>
      <property id="13" name="httpPort" type="integer_property" width="40">
        <displayNameKey>managerHttpPort</displayNameKey>
        <displayName>managerHttpPort</displayName>
        <value>80</value>
      </property>
      <property id="14" name="httpsPort" type="integer_property" width="40">
        <displayNameKey>managerHttpsPort</displayNameKey>
        <displayName>managerHttpsPort</displayName>
        <value>443</value>
      </property>
      <property id="15" name="welcomePageURI" type="url_property" width="40" hidden="true">
        <displayNameKey>welcomePageURI</displayNameKey>
        <displayName>welcomePageURI</displayName>
        <value>jsp/index.jsp</value>
      </property>
      <property id="16" name="serverURL" type="url_property" width="40">
        <displayNameKey>serverURL</displayNameKey>
        <displayName>serverURL</displayName>
        <value>somevalue</value>
      </property>
    </category>
    <category id="17" name="datafiltering">
      <displayNameKey>managerDataFiltering</displayNameKey>
      <displayName>managerDataFiltering</displayName>
      <description>mgr_data_filtering</description>
      <property id="18" name="defaultTableName" type="string_property" width="40">
        <displayNameKey>defaultTableName</displayNameKey>
        <displayName>defaultTableName</displayName>
      </property>
      <property id="19" name="defaultAudienceName" type="string_property" width="40">
        <displayNameKey>defaultAudienceName</displayNameKey>
        <displayName>defaultAudienceName</displayName>
      </property>
    </category>
  </application>
</suite>

What I need to do is generate an XPath expression for each property but not using positions or IDs, but name attribute. 我需要做的是为每个属性生成一个XPath表达式,但不使用位置或ID,而是使用name属性。 That is, for the file above the desired output is similar to: 也就是说,对于上方的文件,所需的输出类似于:

/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="httpPort"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="httpsPort"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="welcomePageURI"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="serverURL"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="datafiltering"]/property[@name="defaultTableName"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="datafiltering"]/property[@name="defaultAudienceName"]

All XPath generators I found only generate XPath using name attribute or position, such as /suite[0]/application[0]/category[1]/... 我发现的所有XPath生成器仅使用名称属性或位置生成XPath,例如/ suite [0] / application [0] / category [1] / ...

Can you please recommend me a way how to generate XPaths for all properties in my file? 您能为我推荐一种如何为文件中的所有属性生成XPath的方法吗? And one more thing - the structure is variable - that is there can be 0 to N nested categories, such as 还有一件事-结构是可变的-也就是说可以有0到N个嵌套类别,例如

/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="cat1"]/category[@name="cat2"]/category[@name="cat3"]/property[@name="property1"]
/suite[@name="SuiteName"]/application[@name="Manager"]/property[@name="property2"]

You could do it in php like this: 您可以像这样在php中完成此操作:

<?php

$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<suite id="1" name="SuiteName">
  <displayNameKey>something</displayNameKey>
  <displayName>something</displayName>
  <application id="2" name="Manager">
    <displayNameKey>appName</displayNameKey>
    <displayName>appName</displayName>
    <category id="12" name="navigation">
      <displayNameKey>managerNavigation</displayNameKey>
      <displayName>managerNavigation</displayName>
      <description>mgr_navigation</description>
      <property id="13" name="httpPort" type="integer_property" width="40">
        <displayNameKey>managerHttpPort</displayNameKey>
        <displayName>managerHttpPort</displayName>
        <value>80</value>
      </property>
      <property id="14" name="httpsPort" type="integer_property" width="40">
        <displayNameKey>managerHttpsPort</displayNameKey>
        <displayName>managerHttpsPort</displayName>
        <value>443</value>
      </property>
      <property id="15" name="welcomePageURI" type="url_property" width="40" hidden="true">
        <displayNameKey>welcomePageURI</displayNameKey>
        <displayName>welcomePageURI</displayName>
        <value>jsp/index.jsp</value>
      </property>
      <property id="16" name="serverURL" type="url_property" width="40">
        <displayNameKey>serverURL</displayNameKey>
        <displayName>serverURL</displayName>
        <value>somevalue</value>
      </property>
    </category>
    <category id="17" name="datafiltering">
      <displayNameKey>managerDataFiltering</displayNameKey>
      <displayName>managerDataFiltering</displayName>
      <description>mgr_data_filtering</description>
      <property id="18" name="defaultTableName" type="string_property" width="40">
        <displayNameKey>defaultTableName</displayNameKey>
        <displayName>defaultTableName</displayName>
      </property>
      <property id="19" name="defaultAudienceName" type="string_property" width="40">
        <displayNameKey>defaultAudienceName</displayNameKey>
        <displayName>defaultAudienceName</displayName>
      </property>
    </category>
  </application>
</suite>
XML;


function genXpath($xml, $att, $current = null)
{
    if($current == null) $current = '/*';
    $new = $current.'[@'.$att.']';

    $result = $xml->xpath($new);

    if($current[strlen($current) - 1] == '*')
    {
        $current = substr($current, 0, strlen($current) - 1);
    }

    if(count($result))
    {
        foreach($result as $node)
        {
            $prev = $current;
            $current .= $node->getName().'[@'.$att.'="'.$node->attributes()->$att.'"]/*';
            genXpath($xml, $att, $current);
            $current = $prev;
        }

    }
    else
    {
        $current = substr($current, 0, strlen($current) - 1);
        echo $current.'<br />';
    }

}

// how to use
$xml = new SimpleXMLElement($xml);
genXpath($xml, "name");

?>

It outputs something like this: 它输出如下内容:

/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="httpPort"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="httpsPort"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="welcomePageURI"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="serverURL"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="datafiltering"]/property[@name="defaultTableName"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="datafiltering"]/property[@name="defaultAudienceName"]

I hope it helps. 希望对您有所帮助。 And also you can set the attribute name you want. 您还可以设置所需的属性名称。

The function itself and the use of it is: 函数本身及其用途是:

<?php

function genXpath($xml, $att, $current = null)
{
    if($current == null) $current = '/*';
    $new = $current.'[@'.$att.']';

    $result = $xml->xpath($new);

    if($current[strlen($current) - 1] == '*')
    {
        $current = substr($current, 0, strlen($current) - 1);
    }

    if(count($result))
    {
        foreach($result as $node)
        {
            $prev = $current;
            $current .= $node->getName().'[@'.$att.'="'.$node->attributes()->$att.'"]/*';
            genXpath($xml, $att, $current);
            $current = $prev;
        }

    }
    else
    {
        $current = substr($current, 0, strlen($current) - 1);
        echo $current.'<br />';
    }

}

// how to use
$xml = "your xml string"; // you can read it from a file   
$xml = new SimpleXMLElement($xml);
genXpath($xml, "name");

The algorithm is what's important here, you can easily port it in any other programming language. 该算法在这里很重要,您可以轻松地将其移植到任何其他编程语言中。 All that is needed is the support for xpath, and to change the way you should obtain information from the result given by xpath query. 所需要的就是对xpath的支持,并改变您从xpath查询给出的结果中获取信息的方式。

Best regards, 最好的祝福,
blind

This is probably the shortest and simplest (no named templates, no explicit conditional instructions at all, no xsl:for-each and no use of // ) XSLT transformation that implements the wanted processing : 这可能是最短和最简单的(没有命名模板,根本没有明确的条件指令,没有xsl:for-each ,也没有使用// )实现所需处理的XSLT转换

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:template match="property">
  <xsl:apply-templates select="ancestor::*" mode="build"/>
  <xsl:value-of select=
  "concat('/property[@name=&quot;', @name, '&quot;]')"/>
  <xsl:text>&#xA;</xsl:text>
 </xsl:template>

 <xsl:template match="*" mode="build">
  <xsl:value-of select=
  "concat('/',name(),'[@name=&quot;', @name, '&quot;]')"/>
 </xsl:template>

 <xsl:template match="text()"/>
</xsl:stylesheet>

when this transformation is applied on the provided XML document : 当此转换应用于提供的XML文档时

<?xml version="1.0" encoding="UTF-8"?>
<suite id="1" name="SuiteName">
  <displayNameKey>something</displayNameKey>
  <displayName>something</displayName>
  <application id="2" name="Manager">
    <displayNameKey>appName</displayNameKey>
    <displayName>appName</displayName>
    <category id="12" name="navigation">
      <displayNameKey>managerNavigation</displayNameKey>
      <displayName>managerNavigation</displayName>
      <description>mgr_navigation</description>
      <property id="13" name="httpPort" type="integer_property" width="40">
        <displayNameKey>managerHttpPort</displayNameKey>
        <displayName>managerHttpPort</displayName>
        <value>80</value>
      </property>
      <property id="14" name="httpsPort" type="integer_property" width="40">
        <displayNameKey>managerHttpsPort</displayNameKey>
        <displayName>managerHttpsPort</displayName>
        <value>443</value>
      </property>
      <property id="15" name="welcomePageURI" type="url_property" width="40" hidden="true">
        <displayNameKey>welcomePageURI</displayNameKey>
        <displayName>welcomePageURI</displayName>
        <value>jsp/index.jsp</value>
      </property>
      <property id="16" name="serverURL" type="url_property" width="40">
        <displayNameKey>serverURL</displayNameKey>
        <displayName>serverURL</displayName>
        <value>somevalue</value>
      </property>
    </category>
    <category id="17" name="datafiltering">
      <displayNameKey>managerDataFiltering</displayNameKey>
      <displayName>managerDataFiltering</displayName>
      <description>mgr_data_filtering</description>
      <property id="18" name="defaultTableName" type="string_property" width="40">
        <displayNameKey>defaultTableName</displayNameKey>
        <displayName>defaultTableName</displayName>
      </property>
      <property id="19" name="defaultAudienceName" type="string_property" width="40">
        <displayNameKey>defaultAudienceName</displayNameKey>
        <displayName>defaultAudienceName</displayName>
      </property>
    </category>
  </application>
</suite>

the wanted, correct result is produced : 产生想要的正确结果

/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="httpPort"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="httpsPort"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="welcomePageURI"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="navigation"]/property[@name="serverURL"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="datafiltering"]/property[@name="defaultTableName"]
/suite[@name="SuiteName"]/application[@name="Manager"]/category[@name="datafiltering"]/property[@name="defaultAudienceName"]

I would do something like this: 我会做这样的事情:

<xsl:transform version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" />
    <xsl:template match="/">
    <xsl:for-each select="//property">
        <xsl:call-template name="add-parent-xpath"/>
        <xsl:text>
</xsl:text>
    </xsl:for-each>
    </xsl:template>

    <xsl:template name="add-parent-xpath">
        <xsl:if test="name(.) != 'suite'">
            <xsl:for-each select="..">
                <xsl:call-template name="add-parent-xpath" />
            </xsl:for-each>
        </xsl:if>
        <xsl:value-of select="concat('/', name(.), '[@name=&quot;', @name, '&quot;]')"/>
    </xsl:template>

</xsl:transform>

It starts by selecting each property node, and then recursively climbs all the away back up to the suite node. 首先选择每个属性节点,然后递归地爬回所有套件节点。 As the recursion unravels, it emits the xpath to select that node, so you get the xpath for suite, then the next level, and so on, all the way back down to the property. 递归解散时,它会发出xpath来选择该节点,因此您将获得套件的xpath,然后是下一个级别,依此类推,一直返回到属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 PowerShell - xpath 不区分大小写 XML 属性名称 - PowerShell - xpath for Case Insensitive XML attribute name 如何使用Java为XML中的节点生成Xpath? - How to generate Xpath for a node in XML using java? 在JAXP中使用XPath检索XML节点和节点属性的值 - Retrieve value of XML node and node attribute using XPath in JAXP 如何遍历 xml 文件中的每个节点并在节点名称与字符串匹配时返回 true,否则使用 XQuery 和 XPath 返回 false? - How to go through each node in a xml file and return true if the name of the node matches a string and false otherwise using XQuery and XPath? 如何使用XPath获取XML节点的属性值 - How to get the attribute value of a XML node using XPath 使用SQL XPATH查询获取XML元素名称和属性值 - Get XML element name and the attribute value using SQL XPATH query 使用PHP XPATH中的attribute值读取节点的名称和值 - Read name and values of node using value of attribute in PHP XPATH 通过XPath选择具有属性不区分大小的节点 - Select node with attribute case insensitive via XPath 当属性名称是每个节点上的属性时,将XML反序列化为类 - Deserializing XML to class when the property name is an attribute on each node 如何使用xpath / php获取xml文件中节点的名称? - How to get the name of a node in a xml file using xpath/php?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM