我有一个巨大的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"]
答案 0 :(得分:1)
你可以在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查询给出的结果中获取信息的方式。
最好的问候,
盲
答案 1 :(得分:1)
这可能是最短和最简单的(没有命名模板,根本没有明确的条件指令,没有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="', @name, '"]')"/>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="*" mode="build">
<xsl:value-of select=
"concat('/',name(),'[@name="', @name, '"]')"/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
将此转换应用于提供的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>
产生了想要的正确结果:
/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"]
答案 2 :(得分:0)
我会做这样的事情:
<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="', @name, '"]')"/>
</xsl:template>
</xsl:transform>
首先选择每个属性节点,然后递归地爬回到套件节点。当递归解开时,它会发出xpath来选择该节点,因此你得到套件的xpath,然后是下一级,依此类推,一直回到属性。