如何在反向域名排序和自定义筛选上规范化XML
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在反向域名排序和自定义筛选上规范化XML相关的知识,希望对你有一定的参考价值。
我一直在研究Geo应用程序。随着时间的推移,产品的XML变得有点混乱。当在多个环境(例如Dev,Test等)中同步更改时会出现问题。我正在尝试找到一种规范化内容的方法,因此我可以在编辑和合并时避免一些麻烦,因此可以进行高效的开发。我知道这听起来很疯狂,而且背景很多,但让我跳到实际的问题上留下历史。
这是问题所在:
- 应用了多个排序顺序,例如:
基于反向域名排序。例如,它应该将
d.c.b.a
读作a.b.c.d
或map.google.com
作为com.google.map
进行排序。 当域包含非字母数字char时,如*,?,[,]等,则该节点应位于特定的节点之后,因为范围很宽。 在端口和路径上排序为第二次后续排序。 对<tgt>
元素下的标记应用类似的排序顺序(如果存在)。 - 当值是通用的时,消除
<scheme>
和<port>
标签,例如用于scheme标签的http / https和用于端口标签的80或443,否则保留。另外,如果没有值,请删除,例如<scheme/>
。 - 按原样保留所有其他标记和值。
- 琐碎的事情,如缩进到2个空格字符和实际数据,而不需要样板材料。
这里有一些有问题的XML:
XML
<?xml version='1.0' encoding='UTF-8' ?>
<?tapia chrome-version='2.0' ?>
<mapGeo>
<a>blah</a>
<b>blah</b>
<maps>
<mapIndividual>
<src>
<scheme>https</scheme>
<domain>photos.yahoo.com</domain>
<path>somepath</path>
<query>blah</query>
</src>
<loc>C:var mp</loc>
<x>blah</x>
<y>blah</y>
</mapIndividual>
<mapIndividual>
<src>
<scheme>tcp</scheme>
<domain>map.google.com</domain>
<port>80</port>
<path>/value</path>
<query>blah</query>
</src>
<tgt>
<scheme>https</scheme>
<domain>map.google.com</domain>
<port>443</port>
<path>/value</path>
<query>blah</query>
</tgt>
<x>blah</x>
<y>blah</y>
</mapIndividual>
<mapIndividual>
<src>
<scheme>http</scheme>
<domain>*.c.b.a</domain>
<path>somepath</path>
<port>8085</port>
<query>blah</query>
</src>
<tgt>
<domain>r.q.p</domain>
<path>somepath</path>
<query>blah</query>
</tgt>
<x>blah</x>
<y>blah</y>
</mapIndividual>
<mapIndividual>
<src>
<scheme>http</scheme>
<domain>d.c.b.a</domain>
<path>somepath</path>
<port>8085</port>
<query>blah</query>
</src>
<tgt>
<domain>r.q.p</domain>
<path>somepath</path>
<query>blah</query>
</tgt>
<x>blah</x>
<y>blah</y>
</mapIndividual>
<maps>
</mapGeo>
我能够按原样对值进行基本排序,但无法找到生成反向域名的方法。我遇到了XSL扩展,但还没有尝试过。这是我正在研究的解决方案的开始部分,这是非常基本的。
XSL
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="maps">
<xsl:copy>
<xsl:apply-templates select="*">
<xsl:sort select="src/domain" />
<xsl:sort select="src/port" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
预期产出
<?xml version='1.0' encoding='UTF-8' ?>
<?tapia chrome-version='2.0' ?>
<mapGeo>
<a>blah</a>
<b>blah</b>
<maps>
<mapIndividual>
<src>
<domain>d.c.b.a</domain>
<path>somepath</path>
<port>8085</port>
<query>blah</query>
</src>
<tgt>
<domain>r.q.p</domain>
<path>somepath</path>
<query>blah</query>
</tgt>
<x>blah</x>
<y>blah</y>
</mapIndividual>
<mapIndividual>
<src>
<domain>*.c.b.a</domain>
<path>path1</path>
<port>8085</port>
<query>blah</query>
</src>
<tgt>
<domain>r.q.p</domain>
<path>path2</path>
<query>blah</query>
</tgt>
<x>blah</x>
<y>blah</y>
</mapIndividual>
<mapIndividual>
<src>
<scheme>tcp</scheme>
<domain>map.google.com</domain>
<path>/value</path>
<query>blah</query>
</src>
<tgt>
<domain>map.google.com</domain>
<path>/value</path>
<query>blah</query>
</tgt>
<x>blah</x>
<y>blah</y>
</mapIndividual>
<mapIndividual>
<src>
<domain>photos.yahoo.com</domain>
<path>somepath</path>
<query>blah</query>
</src>
<loc>C:var mp</loc>
<x>blah</x>
<y>blah</y>
</mapIndividual>
<maps>
</mapGeo>
注意:我更喜欢XSLT 1.0,因为在当前环境中支持XSLT 1.0。 XSLT 2.0将是一个加号。
更新:我找到了支持XSLT 2.0和XSLT 3.0的解决方案,所以请忽略我之前关于XSLT 1.0的说明。
先感谢您!
干杯,
答案
我认为不可能使用XSLT 1.0以单一传递的相反顺序进行排序。请考虑以下简化示例:
XML
<root>
<item>
<domain>t.q.p</domain>
</item>
<item>
<domain>s.q.p</domain>
</item>
<item>
<domain>photos.yahoo.com</domain>
</item>
<item>
<domain>map.google.com</domain>
</item>
<item>
<domain>aap.google.com</domain>
</item>
<item>
<domain>r.q.p</domain>
</item>
<item>
<domain>*.c.b.a</domain>
</item>
<item>
<domain>d.c.b.a</domain>
</item>
</root>
XSLT 1.0(+ EXSLT节点集)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/root">
<!-- 1st pass -->
<xsl:variable name="items">
<xsl:for-each select="item">
<xsl:copy>
<xsl:attribute name="sort-string">
<xsl:call-template name="reverse-tokens">
<xsl:with-param name="text" select="domain"/>
</xsl:call-template>
</xsl:attribute>
<xsl:copy-of select="@*|node()"/>
</xsl:copy>
</xsl:for-each>
</xsl:variable>
<!-- output -->
<xsl:copy>
<xsl:apply-templates select="exsl:node-set($items)/item">
<xsl:sort select="@sort-string" data-type="text" order="ascending"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="@sort-string"/>
<xsl:template name="reverse-tokens">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="'.'"/>
<xsl:variable name="token" select="substring-before(concat($text, $delimiter), $delimiter)"/>
<xsl:if test="contains($text, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="reverse-tokens">
<xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
</xsl:call-template>
<xsl:value-of select="$delimiter"/>
</xsl:if>
<xsl:choose>
<xsl:when test="$token = '*'">
<xsl:text>zzzz</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$token"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
结果
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<domain>d.c.b.a</domain>
</item>
<item>
<domain>*.c.b.a</domain>
</item>
<item>
<domain>aap.google.com</domain>
</item>
<item>
<domain>map.google.com</domain>
</item>
<item>
<domain>photos.yahoo.com</domain>
</item>
<item>
<domain>r.q.p</domain>
</item>
<item>
<domain>s.q.p</domain>
</item>
<item>
<domain>t.q.p</domain>
</item>
</root>
另一答案
这个XSLT 1.0样式表(没有扩展名)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="maps">
<xsl:copy>
<xsl:apply-templates select="*">
<xsl:sort
select="translate(src/domain,translate(src/domain,'.',''),'')"
order="descending"/>
<xsl:sort
select="
substring-after(
substring-after(
substring-after(translate(src/domain,'*','~'),'.'),'.'),'.')"/>
<xsl:sort
select="
substring-after(
substring-after(translate(src/domain,'*','~'),'.'),'.')"/>
<xsl:sort
select="substring-after(translate(src/domain,'*','~'),'.')"/>
<xsl:sort select="translate(src/domain,'*','~')" />
<xsl:sort select="src/port" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
产量
<?xml version="1.0" encoding="UTF-8"?>
<?tapia chrome-version='2.0' ?>
<mapGeo>
<a>blah</a>
<b>blah</b>
<maps>
<mapIndividual>
<src>
<scheme>http</scheme>
<domain>d.c.b.a</domain>
<path>somepath</path>
<port>8085</port>
<query>blah</query>
</src>
<tgt>
<domain>r.q.p</domain>
<path>somepath</path>
<query>blah</query>
</tgt>
<x>blah</x>
<y>blah</y>
</mapIndividual>
<mapIndividual>
<src>
<scheme>http</scheme>
<domain>*.c.b.a</domain>
<path>somepath</path>
<port>8085</port>
<query>blah</query>
</src>
<tgt>
<domain>r.q.p</domain>
<path>somepath</path>
<query>blah</query>
</tgt>
<x>blah</x>
<y>blah</y>
</mapIndividual>
<mapIndividual>
<src>
<scheme>tcp</scheme>
<domain>map.google.com</domain>
<port>80</port>
<path>/value</path>
<query>blah</query>
</src>
<tgt>
<scheme>https</scheme>
<domain>map.google.com</domain>
<port>443</port>
<path>/value</path>
<query>blah</query>
</tgt>
<x>blah</x>
<y>blah</y>
</mapIndividual>
<mapIndividual>
<src>
<scheme>https</scheme>
<domain>photos.yahoo.com</domain>
<path>somepath</path>
<query>blah</query>
</src>
<loc>C:var mp</loc>
<x>blah</x>
<y>blah</y>
</mapIndividual>
</maps>
</mapGeo>
请注意:这是因为.
(点)位于前面,而~
按字母顺序跟随(波浪形)字母(至少对于美国而言)。也许(原文如此)不能很好地扩展......
如何对返回 orm 对象和自定义列的元组的查询进行正确排序、分组?