使用自定义属性(在本例中为name)为XML中的每个节点生成XPath

我有一个巨大的
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>

我需要做的是为每个属性生成一个XPath表达式,但不使用位置或ID,而是使用name属性.
也就是说,对于上面的文件,所需的输出类似于:

/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"]

我发现的所有XPath生成器只使用name属性或位置生成XPath,例如/ suite [0] / application [0] / category [1] / …

能否请您推荐一种如何为我文件中的所有属性生成XPath的方法?
还有一件事 – 结构是可变的 – 即可以有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"]

最佳答案 你可以在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");

?>

它输出这样的东西:

/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"]

我希望它有所帮助.您还可以设置所需的属性名称.

函数本身及其用途是:

<?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");

算法在这里非常重要,您可以轻松地将其移植到任何其他编程语言中.所需要的只是对xpath的支持,并改变从xpath查询给出的结果中获取信息的方式.

最好的祝福,

点赞