XSLT для преобразования XML в JSON (формат Solr) - PullRequest
0 голосов
/ 08 февраля 2019

Я пытаюсь сгенерировать XSLT для преобразования XML-файла в формат JSON, включая все необходимые поля для правильной индексации в Solr.

Мой текущий XSLT-файл имеет следующий вид:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:template match="/">{
        <xsl:apply-templates select="*"/>}
    </xsl:template>

    <!-- Object or Element Property-->
    <xsl:template match="*">
        <xsl:variable name="childDoc" select="name(*[1])"/>
        <xsl:choose>
            <xsl:when test="count(*[name()=$childDoc]) >= 1">
    "Document":"<xsl:value-of select="name()"/>", <xsl:call-template name="Properties"/>
                </xsl:when>
                <xsl:otherwise>
    "<xsl:value-of select="name()"/>": <xsl:call-template name="Properties"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:template>

        <!-- Array Parent Node -->
        <xsl:template match="*" mode="ArrayParentNode">
            <xsl:call-template name="Path"/>
        </xsl:template>

        <!-- Array Element -->
        <xsl:template match="*" mode="ArrayElement">
            <xsl:call-template name="Properties"/>
        </xsl:template>

        <!-- Object Properties -->
        <xsl:template name="Properties">
            <xsl:variable name="childName" select="name(*[1])"/>
            <xsl:choose>
                <xsl:when test="not(*|@*)">"<xsl:value-of select="."/>"</xsl:when>
                <xsl:when test="count(*[name()=$childName]) > 1">
    "_childDocuments_":[
    { "<xsl:value-of select="$childName"/>" :[<xsl:apply-templates select="*" mode="ArrayElement"/>] 
        }
    ]
                </xsl:when>
                <xsl:otherwise>
    "_childDocuments_":[
        {
                    <xsl:apply-templates select="." mode="ArrayParentNode"/>
                    <xsl:apply-templates select="@*"/>
                    <xsl:apply-templates select="*"/>                   
        }
        ]
                </xsl:otherwise>
            </xsl:choose>
            <xsl:if test="following-sibling::*">,</xsl:if>

        </xsl:template>

        <!-- Attribute Property -->
        <xsl:template match="@*">"<xsl:value-of select="name()"/>" : "<xsl:value-of select="."/>",</xsl:template>

        <!--Path-->
        <xsl:template name = "Path">
            <xsl:variable name="ances" select="ancestor-or-self::node()"/>
            <xsl:variable name="ancestros">
                <xsl:for-each select="$ances">
                    <xsl:value-of select="concat(name(),'.')"/>
                </xsl:for-each>
            </xsl:variable>
        "Path": "<xsl:value-of select = "substring($ancestros,2,string-length($ancestros)-2)"/>",
        </xsl:template>

    </xsl:stylesheet>

Учитывая следующий пример:

<Reporte>
    <id>10</id>
    <Operacion>
        <id>10.1</id>
        <Tipo_Operacion>Ejemplo</Tipo_Operacion>
        <Fecha>10/10/2010</Fecha>
        <Monto>12345</Monto>
    </Operacion>
    <bla>123456ytgfde</bla>
    <Persona_Fisica>
        <id>10.2</id>
        <Nombre>Juan</Nombre>
        <Apellido>Perez</Apellido>
        <Domicilio>
            <id>10.2.1</id>
            <Calle>Yrigoyen</Calle>
            <Numero>123</Numero>
        </Domicilio>
        <Telefono>
            <id>10.2.2</id>
            <Prefijo>11</Prefijo>
            <Numero>12345678</Numero>
        </Telefono>
    </Persona_Fisica>
    <Persona_Fisica>
        <id>10.3</id>
        <Nombre>Roberto Carlos</Nombre>
        <Apellido>De Souza</Apellido>
        <PEP>true</PEP>
        <Domicilio>
            <id>10.3.1</id>
            <Calle>Falsa</Calle>
            <Numero>678</Numero>
        </Domicilio>
        <Telefono>
            <id>10.3.2</id>
            <Prefijo>11</Prefijo>
            <Numero>87654321</Numero>
        </Telefono>
    </Persona_Fisica>
    <Persona_Juridica>
        <id>10.4</id>
        <Denominacion>Lavado SRL</Denominacion>
        <CUIT>23-32480636-9</CUIT>
    </Persona_Juridica>
</Reporte>

Мой вывод следующий:

{

                "Document":"Reporte", 
    "_childDocuments_":[
        {

        "Path": "Reporte",

                "id": "11",
                "Document":"Operacion", 
    "_childDocuments_":[
        {

        "Path": "Reporte.Operacion",

                "id": "10.1",
                "Tipo_Operacion": "Ejemplo",
                "Fecha": "10/10/2010",
                "Monto": "12345"                    
        }
        ]
                ,
                "bla": "123456ytgfde",
                "Document":"Persona_Fisica", 
    "_childDocuments_":[
        {

        "Path": "Reporte.Persona_Fisica",

                "id": "10.2",
                "Nombre": "Juan",
                "Apellido": "Perez",
                "Document":"Domicilio", 
    "_childDocuments_":[
        {

        "Path": "Reporte.Persona_Fisica.Domicilio",

                "id": "10.2.1",
                "Calle": "Yrigoyen",
                "Numero": "123"                 
        }
        ]
                ,
                "Document":"Telefono", 
    "_childDocuments_":[
        {

        "Path": "Reporte.Persona_Fisica.Telefono",

                "id": "10.2.2",
                "Prefijo": "11",
                "Numero": "12345678"                    
        }
        ]

        }
        ]
                ,
                "Document":"Persona_Fisica", 
    "_childDocuments_":[
        {

        "Path": "Reporte.Persona_Fisica",

                "id": "10.3",
                "Nombre": "Roberto Carlos",
                "Apellido": "De Souza",
                "PEP": "true",
                "Document":"Domicilio", 
    "_childDocuments_":[
        {

        "Path": "Reporte.Persona_Fisica.Domicilio",

                "id": "10.3.1",
                "Calle": "Falsa",
                "Numero": "678"                 
        }
        ]
                ,
                "Document":"Telefono", 
    "_childDocuments_":[
        {

        "Path": "Reporte.Persona_Fisica.Telefono",

                "id": "10.3.2",
                "Prefijo": "11",
                "Numero": "87654321"                    
        }
        ]

        }
        ]
                ,
                "Document":"Persona_Juridica", 
    "_childDocuments_":[
        {

        "Path": "Reporte.Persona_Juridica",

                "id": "10.4",
                "Denominacion": "Lavado SRL",
                "CUIT": "23-32480636-9"                 
        }
        ]

        }
        ]
                }

Независимо от отступа (как это желательно, но не обязательно), я ожидал бы что-то вродеследующее:

{
    "Document": "Reporte",
    "Path": "Reporte",
    "id": "12",
    "bla": "123456ytgfde",
    "_childDocuments_": [
        {
            "Document": "Operacion",
            "Path": "Reporte.Operacion",
            "id": "10.1",
            "Tipo_Operacion": "Ejemplo",
            "Fecha": "10/10/2010",
            "Monto": "12345"
        },
        {
            "Document": "Persona_Fisica",
            "Path": "Reporte.Persona_Fisica",
            "id": "10.2",
            "Nombre": "Juan",
            "Apellido": "Perez",
            "_childDocuments_": [
                {
                    "Document": "Domicilio",
                    "Path": "Reporte.Persona_Fisica.Domicilio",
                    "id": "10.2.1",
                    "Calle": "Yrigoyen",
                    "Numero": "123"
                },
                {
                    "Document": "Telefono",
                    "Path": "Reporte.Persona_Fisica.Telefono",
                    "id": "10.2.2",
                    "Prefijo": "11",
                    "Numero": "12345678"
                }
            ]
        },
        {
            "Document": "Persona_Fisica",
            "Path": "Reporte.Persona_Fisica",
            "id": "10.3",
            "Nombre": "Roberto Carlos",
            "Apellido": "De Souza",
            "PEP": "true",
            "_childDocuments_": [
                {
                    "Document": "Domicilio",
                    "Path": "Reporte.Persona_Fisica.Domicilio",
                    "id": "10.3.1",
                    "Calle": "Falsa",
                    "Numero": "678"
                },
                {
                    "Document": "Telefono",
                    "Path": "Reporte.Persona_Fisica.Telefono",
                    "id": "10.3.2",
                    "Prefijo": "11",
                    "Numero": "87654321"
                }
            ]
        },
        {
            "Document": "Persona_Juridica",
            "Path": "Reporte.Persona_Juridica",
            "id": "10.4",
            "Denominacion": "Lavado SRL",
            "CUIT": "23-32480636-9"
        }
    ]
}

Подводя итог, что все еще отсутствует в моем XSLT:

  1. Элементы внутри "Reporte", которые не имеют никаких подэлементов (как "Документ": "Reporte""," Path ":" Reporte "," id ":" 10 "," bla ":" 123456ytgfde ") должны находиться за пределами первого childDocuments элемента.
  2. Только один childDocuments элемент с несколькими вложенными документами должен быть создан вместо одного для каждого вложенного документа.
  3. Элемент «Document» должен быть после соответствующего childDocuments вместо before (side)рядом с элементом «Путь», который являетсяхорошо).
  4. Довольно отступ (просто желательно).

1 Ответ

0 голосов
/ 08 февраля 2019

Если вы можете перейти к XSLT 3, вы можете использовать встроенную поддержку преобразования XML в JSON (например, https://www.w3.org/TR/xslt-30/#json),), создав XML-представление JSON, указанное в спецификации XSLT 3, а затем вызвав xml-to-json функция (https://www.w3.org/TR/xpath-functions/#func-xml-to-json) на что:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:fn="http://www.w3.org/2005/xpath-functions"
    exclude-result-prefixes="#all"
    expand-text="yes"
    version="3.0">

  <xsl:output method="text"/>

  <xsl:mode name="json" on-no-match="shallow-skip"/>

  <xsl:template match="*[*]" mode="json">
      <fn:map>
          <fn:string key="Document">{local-name()}</fn:string>
          <fn:string key="Path">{string-join(ancestor-or-self::*/local-name(), '.')}</fn:string>
          <xsl:apply-templates select="*[not(*)]" mode="#current"/>
          <xsl:where-populated>
              <fn:array key="_childDocument_">
                  <xsl:apply-templates select="*[*]" mode="#current"/>
              </fn:array>
          </xsl:where-populated>
      </fn:map>
  </xsl:template>

  <xsl:template match="*[not(*)]" mode="json">
      <fn:string key="{local-name()}">{.}</fn:string>
  </xsl:template>

  <xsl:variable name="json-xml">
      <xsl:apply-templates mode="json"/>
  </xsl:variable>

  <xsl:template match="/">
      <xsl:sequence select="xml-to-json($json-xml, map { 'indent' : true() })"/>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty -development.net / 94rmq5S / 2

Как вы и просилирешение XSLT 1, по адресу https://xsltfiddle.liberty -development.net / 94rmq5S / 8 Я пытался повторно использовать шаблоны XSLT 3 для преобразования вашего XML в формат XML, указанный в спецификации XSLT 3, для представления JSON,только на этот раз, используя чистый XSLT 1, я добавил несколько шаблонов, чтобы попытаться сериализовать этот XML в XSLT 1 как текст JSON, используя exsl:node-set для преобразования фрагмента промежуточного дерева результатов в набор узлов:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fn="http://www.w3.org/2005/xpath-functions"
    xmlns:exsl="http://exslt.org/common"
    exclude-result-prefixes="fn exsl"
    version="1.0">

  <xsl:param name="indent" select="'  '"/>

  <xsl:output method="text"/>

  <xsl:template match="*[*]" mode="json">
      <xsl:param name="path" select="''"/>
      <xsl:variable name="current-path">
          <xsl:if test="ancestor::*">
              <xsl:value-of select="concat($path, '.')"/>
          </xsl:if>
          <xsl:value-of select="local-name()"/>
      </xsl:variable>
      <fn:map>
          <fn:string key="Document">
              <xsl:value-of select="local-name()"/>
          </fn:string>
          <fn:string key="Path">
              <xsl:value-of select="$current-path"/>
          </fn:string>
          <xsl:apply-templates select="*[not(*)]" mode="json"/>
          <xsl:if test="*[*]">
              <fn:array key="_childDocument_">
                  <xsl:apply-templates select="*[*]" mode="json">
                      <xsl:with-param name="path" select="$current-path"/>
                  </xsl:apply-templates>
              </fn:array>
          </xsl:if>
      </fn:map>
  </xsl:template>

  <xsl:template match="*[not(*)]" mode="json">
      <fn:string key="{local-name()}">
          <xsl:value-of select="."/>
      </fn:string>
  </xsl:template>

  <xsl:variable name="json-xml">
      <xsl:apply-templates mode="json"/>
  </xsl:variable>

  <xsl:template match="/">
      <!--
      <xsl:copy-of select="exsl:node-set($json-xml)/node()"/>
      <hr/>
      -->
      <xsl:apply-templates select="exsl:node-set($json-xml)/node()"/>
  </xsl:template>

  <xsl:template match="fn:map">
      <xsl:param name="current-indent" select="''"/>
      <xsl:if test="position() > 1">,&#10;</xsl:if>
      <xsl:value-of select="concat($current-indent, '{&#10;')"/>
      <xsl:apply-templates>
          <xsl:with-param name="current-indent" select="concat($current-indent, $indent)"/>
      </xsl:apply-templates>
      <xsl:value-of select="concat('&#10;', $current-indent, '}')"/>
  </xsl:template>

  <xsl:template match="fn:array[@key]">
      <xsl:param name="current-indent"/>
      <xsl:if test="position() > 1">,&#10;</xsl:if>
      <xsl:value-of select="concat($current-indent, '&quot;', @key, '&quot; : [&#10;')"/>
      <xsl:apply-templates>
          <xsl:with-param name="current-indent" select="concat($current-indent, $indent)"/>
      </xsl:apply-templates>      
      <xsl:value-of select="concat('&#10;', $current-indent, ']')"/>
  </xsl:template>

  <xsl:template match="fn:string">
      <xsl:param name="current-indent"/>
      <xsl:if test="position() > 1">,&#10;</xsl:if>
      <xsl:value-of select="concat($current-indent, '&quot;', @key, '&quot; : &quot;', ., '&quot;')"/>
  </xsl:template>

</xsl:stylesheet>

Учитывая подход XSLT 3, у этого есть несколько недостатков, я не пытался реализовать все различные типы данных JSON, такие как числа, логические значения, есть только шаблон для строк, я не пытался гарантироватьчто любые символы в строках JSON, которые необходимо экранировать, экранированы правильно.И я уверен, что есть и другие недостатки, поскольку это потребовало бы более широкого и тщательного тестирования, но, возможно, это поможет вам в качестве шага для обработки ваших данных или подхода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...