[英]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="', @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>
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="', @name, '"]')"/>
</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.