如何在反向域名排序和自定义筛选上规范化XML

问题描述 投票:0回答:2

我一直在研究Geo应用程序。随着时间的推移,产品的XML变得有点混乱。当在多个环境(例如Dev,Test等)中同步更改时会出现问题。我正在尝试找到一种规范化内容的方法,因此我可以在编辑和合并时避免一些麻烦,因此可以进行高效的开发。我知道这听起来很疯狂,而且背景很多,但让我跳到实际的问题上留下历史。

这是问题所在:

  1. 应用了多个排序顺序,例如: 基于反向域名排序。例如,它应该将d.c.b.a读作a.b.c.dmap.google.com作为com.google.map进行排序。 当域包含非字母数字char时,如*,?,[,]等,则该节点应位于特定的节点之后,因为范围很宽。 在端口和路径上排序为第二次后续排序。 对<tgt>元素下的标记应用类似的排序顺序(如果存在)。
  2. 当值是通用的时,消除<scheme><port>标签,例如用于scheme标签的http / https和用于端口标签的80或443,否则保留。另外,如果没有值,请删除,例如<scheme/>
  3. 按原样保留所有其他标记和值。
  4. 琐碎的事情,如缩进到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\tmp</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\tmp</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的说明。

先感谢您!

干杯,

xml xslt xslt-1.0 xslt-2.0 xslt-3.0
2个回答
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>

0
投票

这个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\tmp</loc>
         <x>blah</x>
         <y>blah</y>
      </mapIndividual>
   </maps>
</mapGeo>

请注意:这是因为.(点)位于前面,而~按字母顺序跟随(波浪形)字母(至少对于美国而言)。也许(原文如此)不能很好地扩展......

我和Martin Honnen comment:在XSLT 2.0中可以更好地解决这个问题

© www.soinside.com 2019 - 2024. All rights reserved.