Как нормализовать XML при обратной сортировке доменных имен и пользовательской фильтрации - PullRequest
0 голосов
/ 15 апреля 2019

Я работал над приложением Geo. Со временем XML продукта стал немного грязным. Проблема возникает при синхронизации изменений в нескольких средах, таких как Dev, Test и т. Д. Я пытаюсь найти способ нормализации содержимого, чтобы избежать трудоемких действий при редактировании и слиянии, и, следовательно, получить продуктивную разработку. , Я знаю, это звучит безумно, и на заднем плане многое, но позвольте мне перейти к актуальной проблеме, оставив историю.

Вот проблема:

  1. Применено несколько заказов на сортировку, например:

    • Сортировка по обратному доменному имени. Например, для сортировки следует читать d.c.b.a как a.b.c.d или map.google.com как com.google.map.
    • Если домен содержит не алфавитно-цифровые символы, такие как *,?, [,] И т. Д., Тогда этот узел должен быть после определенного, так как область действия широка.
    • Сортировка по порту и пути как 2-ая последующая сортировка.
    • Применить аналогичный порядок сортировки для тегов в элементе <tgt>, если имеется.
  2. Удалите теги <scheme> и <port>, когда значения являются общими, например, 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 2.0 будет плюсом.

Обновление: я нашел решение для поддержки XSLT 2.0 и XSLT 3.0, поэтому, пожалуйста, игнорируйте мою предыдущую заметку для XSLT 1.0.

Заранее спасибо!

Приветствия

Ответы [ 2 ]

0 голосов
/ 16 апреля 2019

Эта таблица стилей 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>

Примечание : это фактчто . (точка) предшествует, а ~ следует (тильда) буквам в алфавитном порядке (по крайней мере, для США).Также может (sic) не хорошо масштабироваться ...

Я с Комментарий Мартина Хоннена : это будет лучше решено в XSLT 2.0

0 голосов
/ 15 апреля 2019

Я не думаю, что можно отсортировать в обратном порядке, который вы ищете за один проход, используя 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>
...