简体   繁体   中英

problems to convert single xml element to Json Array using XSLT

I am using below generic xslt to convert xml into json, but few values are missing under first array attributes. Expectation is to use make ns1:Value as JSON array even if we get single xml element Here I have mentioned sample xml and json messages. XSLT code:

<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:value-of select="name()"/>" : <xsl:call-template name="Properties"/>
    </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="$childName = 'ns1:Value' or count(*[name()=$childName]) > 1">{ "<xsl:value-of select="$childName"/>" :[<xsl:apply-templates select="*" mode="ArrayElement"/>] }</xsl:when>
        <xsl:otherwise>{
            <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>
</xsl:stylesheet>

Sample XML:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:ns1="urn://opten/soap/cegtar/unique">
    <SOAP-ENV:Body>
        <ns1:abcResponse>
            <ns1:abc id="10485824">
                <ns1:Rovatok/>
                <ns1:ScoringAdatok>
                    <ns1:XData tipus="11" megnevezes="APEH hátralék (rendezetlen)">
                        <ns1:Value num="1" subtype="xbool">false</ns1:Value>
                    </ns1:XData>
                    <ns1:XData tipus="13" megnevezes="Egyéb VH (rendezetlen)">
                        <ns1:Value num="1" subtype="xbool">false</ns1:Value>
                    </ns1:XData>
                    <ns1:XData tipus="33" megnevezes="Opten kockázati besorolás">
                        <ns1:Value num="1" subtype="xstring">A</ns1:Value>
                    </ns1:XData>
                    <ns1:XData tipus="46" megnevezes="Cég-állapot (APAFI helyett)">
                        <ns1:Value num="1" subtype="xdate">0000-00-00</ns1:Value>
                        <ns1:Value num="2" subtype="xnum">0</ns1:Value>
                    </ns1:XData>
                    <ns1:XData tipus="68" megnevezes="Kockázati index szám">
                        <ns1:Value num="1" subtype="xnum">9</ns1:Value>
                    </ns1:XData>
                    <ns1:XData tipus="69" megnevezes="A cég keresdelmi hitelkerete">
                        <ns1:Value num="1" subtype="xnum">250000</ns1:Value>
                        <ns1:Value num="2" subtype="xnum">0</ns1:Value>
                    </ns1:XData>
                    <ns1:XData tipus="77" megnevezes="Adószám állapot">
                        <ns1:Value num="1" subtype="xnum">0</ns1:Value>
                        <ns1:Value num="2" subtype="xstring">Érvényes az adószám</ns1:Value>
                    </ns1:XData>
                </ns1:ScoringAdatok>
                <ns1:MerlegAdatok/>
            </ns1:abc>
        </ns1:abcResponse>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Missing attribute values values in JSON response which is "tipus and megnevezes" values: Expected JSON response:

{
    "SOAP-ENV:Envelope": {
        "SOAP-ENV:Body": {
            "ns1:MultiInfoResponse": {
                "ns1:MultiInfo": {
                    "id": "10485824",
                    "ns1:Rovatok": "",
                    "ns1:ScoringAdatok": {
                        "ns1:XData": [{
                            "tipus": "11",
                            "megnevezes": "APEH hátralék (rendezetlen)",
                            "ns1:Value": [{
                                "num": "1",
                                "subtype": "xbool",
                                "text": "false"
                            }]
                        }, {
                            "tipus": "13",
                            "megnevezes": "Egyéb VH (rendezetlen)",
                            "ns1:Value": [{
                                "num": "1",
                                "subtype": "xbool",
                                "text": "false"
                            }]
                        }, {
                            "tipus": "33",
                            "megnevezes": "Opten kockázati besorolás",
                            "ns1:Value": [{
                                "num": "1",
                                "subtype": "xstring",
                                "text": "A"
                            }]
                        }, {
                            "tipus": "46",
                            "megnevezes": "Cég-állapot (APAFI helyett)",
                            "ns1:Value": [{
                                "num": "1",
                                "subtype": "xdate",
                                "text": "0000-00-00"
                            }, {
                                "num": "2",
                                "subtype": "xnum",
                                "text": "0"
                            }]
                        }, {
                            "tipus": "68",
                            "megnevezes": "Kockázati index szám",
                            "ns1:Value": [{
                                "num": "1",
                                "subtype": "xnum",
                                "text": "9"
                            }]
                        }, {
                            "tipus": "69",
                            "megnevezes": "A cég keresdelmi hitelkerete",
                            "ns1:Value": [{
                                "num": "1",
                                "subtype": "xnum",
                                "text": "250000"
                            }, {
                                "num": "2",
                                "subtype": "xnum",
                                "text": "0"
                            }]
                        }, {
                            "tipus": "77",
                            "megnevezes": "Adószám állapot",
                            "ns1:Value": [{
                                "num": "1",
                                "subtype": "xnum",
                                "text": "0"
                            }, {
                                "num": "2",
                                "subtype": "xstring",
                                "text": "Érvényes az adószám"
                            }]
                        }]
                    },
                    "ns1:MerlegAdatok": ""
                }
            }
        }
    }
}

EDIT

Some years ago I made a xml2json with xslt 1.0. This handles also escaping specials json-chars and other bonuses.

I adjusted it a little to your needs. Enjoy:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text" encoding="utf-8"  media-type="application/json" />
  
  <xsl:strip-space elements="*"/>
  <xsl:variable name="separator"><xsl:text>,</xsl:text></xsl:variable>
  
  <xsl:template match="/">{
    <xsl:apply-templates select="*" mode="obj-key"/>}
  </xsl:template>

  <xsl:template match="*[not(* or @* )]" mode="obj-key">
    <xsl:variable name="name" select="name()"/>
    <xsl:if test="not(preceding-sibling::*[name()=$name])">
      <xsl:text>"</xsl:text>
      <xsl:call-template name="nameWithOptinalMinusToCamelcase"/>
      <xsl:text>": "</xsl:text>
      <xsl:value-of select="."/>
      <xsl:text>"</xsl:text>
      <xsl:if test="following-sibling::*[not(name()=$name)]">
        <!--  If other elements with different name use comma -->
        <xsl:value-of select="$separator"/>
      </xsl:if>
    </xsl:if>
  </xsl:template>
    
  <!-- Recursieve element node -->
  <xsl:template match="*" mode="obj-key">
    <xsl:variable name="name" select="name()"/>
    <xsl:variable name="treatAsArray" select="count(parent::*/*[name()=$name]) &gt; 1 or $name='ns1:Value'"/>
    <xsl:if test="not(preceding-sibling::*[name()=$name])">
      <xsl:text>"</xsl:text>
      <xsl:call-template name="nameWithOptinalMinusToCamelcase"/>
      <xsl:text>": </xsl:text>
      <xsl:choose>
        <xsl:when test="$treatAsArray">
          <xsl:text>[</xsl:text>
          <xsl:apply-templates select="." mode="obj-content"/>
          <xsl:text>]</xsl:text>
        </xsl:when>
        <xsl:otherwise>
          <xsl:apply-templates select="." mode="obj-content"/>
        </xsl:otherwise>
      </xsl:choose>
      <xsl:if test="following-sibling::*[not(name()=$name)]">
        <!--  If other elements with different name use comma -->
        <xsl:value-of select="$separator"/>
      </xsl:if>
    </xsl:if>
  </xsl:template>
  
  <xsl:template match="*[* or @*]" mode="obj-content">
    <xsl:variable name="is-child-element-model" select="count(*) &gt; 0"/>
    <xsl:variable name="is-text-node-model"     select="count(*)=0 and normalize-space(.) !=''"/>
    <xsl:text>{</xsl:text>
    <xsl:apply-templates select="@*" mode="attributes"/>
    <xsl:if test="@* and ($is-child-element-model or $is-text-node-model)">
      <xsl:value-of select="$separator"/>
    </xsl:if>
    <xsl:choose>
      <xsl:when test="$is-text-node-model">
        <xsl:apply-templates select="text()" mode="content-text"/>
      </xsl:when>
      <xsl:when test="$is-child-element-model">
        <xsl:apply-templates select="*" mode="obj-key"/>
      </xsl:when>
    </xsl:choose>
    <xsl:text>}</xsl:text>
    <xsl:apply-templates select="." mode="following-sibling"/>
  </xsl:template>
  
  <xsl:template mode="content" match="text()[     string-length(translate(.,'0123456789',''))=0 
    and string-length(normalize-space(.)) &gt; 0 
    and (substring(.,1,1)!='0' or string-length(.)=1)]" 
    priority="2">
    <xsl:value-of select="."/>
  </xsl:template>
  
  <xsl:template mode="content"      match="text()[translate(.,'TRUE','true')='true']" priority="2">true</xsl:template>
  <xsl:template mode="content"      match="text()[translate(.,'FALSE','false')='false']" priority="2">false</xsl:template>

  <xsl:template mode="content-text"  match="*[@* or *]/text()">
    <xsl:text>"text": </xsl:text>
    <xsl:apply-templates select="." mode="content"/>
  </xsl:template>

  <xsl:template match="*" mode="following-sibling">
    <xsl:variable name="name" select="name()"/>
    <xsl:if test="following-sibling::*[name()=$name]">
      <!--  If there are other elements with same name use comma -->
      <xsl:value-of select="$separator"/>
      <xsl:apply-templates select="following-sibling::*[name()=$name][1]" mode="obj-content"/>
    </xsl:if>
  </xsl:template>

  <xsl:template mode="content"  match="text()">
    <xsl:text>"</xsl:text>
    <xsl:value-of select="."/>
    <xsl:text>"</xsl:text>
  </xsl:template>
  <!-- 
  || Attributen met text dienen ge-escaped te worden
  -->
  <xsl:template match="@*" mode="attributes">
    <xsl:text>"</xsl:text>
    <xsl:value-of select="name()"/>
    <xsl:text>": "</xsl:text>
    <xsl:call-template name="escape-special-json-chars"/>
    <xsl:text>"</xsl:text>
    <xsl:if test="position() &lt; last()">
      <xsl:value-of select="$separator"/>
    </xsl:if>
  </xsl:template>  
  
  <xsl:template match="@*[translate(.,'FALSE','false')='false']" mode="attributes">
    <xsl:text>"</xsl:text>
    <xsl:value-of select="name()"/>
    <xsl:text>": false</xsl:text>
    <xsl:if test="position() &lt; last()">
      <xsl:value-of select="$separator"/>
    </xsl:if>
  </xsl:template>  

  <xsl:template match="@*[translate(.,'TRUE','true')='true']" mode="attributes">
    <xsl:text>"</xsl:text>
    <xsl:value-of select="name()"/>
    <xsl:text>": true</xsl:text>
    <xsl:if test="position() &lt; last()">
      <xsl:value-of select="$separator"/>
    </xsl:if>
  </xsl:template>  

  <xsl:template match="@*[string-length(translate(.,'0123456789',''))=0 and string-length(normalize-space(.)) &gt; 0 and (substring(.,1,1)!='0' or string-length(.)=1)]" mode="attributes" >
    <xsl:text>"</xsl:text>
    <xsl:value-of select="name()"/>
    <xsl:text>": </xsl:text>
    <xsl:value-of select="."/>
    <xsl:if test="position() &lt; last()">
      <xsl:value-of select="$separator"/>
    </xsl:if>
  </xsl:template>
  
  <xsl:variable name="escChr" select="'\'"/>
  <xsl:variable name="dqt">"</xsl:variable>
  <xsl:variable name="cr" select="'&#13;'"/>
  <xsl:variable name="nl" select="'&#10;'"/>
  <xsl:variable name="sls" select="'\'"/>
  <xsl:variable name="tab" select="'&#09;'"/>
  
  <xsl:template name="escape-special-json-chars">
    <xsl:variable name="textSlsEsc">
      <xsl:call-template name="escapeString">
        <xsl:with-param name="text"   select="." />
        <xsl:with-param name="escape" select="$sls"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="textCrEsc">
      <xsl:call-template name="escapeString">
        <xsl:with-param name="text"   select="$textSlsEsc" />
        <xsl:with-param name="escape" select="$cr"/>
        <xsl:with-param name="with"   select="'\\r'"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="textLfEsc">
      <xsl:call-template name="escapeString">
        <xsl:with-param name="text"   select="$textCrEsc" />
        <xsl:with-param name="escape" select="$nl"/>
        <xsl:with-param name="with"   select="'\\n'"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="textDqtEsc">
      <xsl:call-template name="escapeString">
        <xsl:with-param name="text"   select="$textLfEsc" />
        <xsl:with-param name="escape" select="$dqt"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="textTabEsc">
      <xsl:call-template name="escapeString">
        <xsl:with-param name="text"   select="$textDqtEsc" />
        <xsl:with-param name="escape" select="$tab"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:value-of select="$textTabEsc"/>
  </xsl:template>
  
  <xsl:template name="escapeString">
    <xsl:param name="text"/>
    <xsl:param name="escape"/>
    <xsl:param name="with" select="concat($escChr,$escape)"/>
    <xsl:param name="prevText" select="'ditWordtNooitGevonden'"/>
    <xsl:choose>
      <xsl:when test="contains($text,$escape)">
        <xsl:variable name="testChar">
          <xsl:choose>
            <xsl:when test="$escape=$escChr">
              <xsl:value-of select="substring(substring-after($text,$escape),1,1)"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:variable name="before" select="substring-before($text,$escape)"/>
              <xsl:value-of select="substring($before, string-length($before))"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:variable>
        <xsl:variable name="testCharIsEscapeChr" select="$testChar=$escChr"/>
        <xsl:choose>
          <xsl:when test="$testCharIsEscapeChr">
            <!-- Karakter is al ge-escaped -->
            <xsl:value-of select="substring-before($text,$with)"/>
            <xsl:value-of select="$with"/>
            <xsl:call-template name="escapeString">
              <xsl:with-param name="text"       select="substring-after($text,$with)"/>
              <xsl:with-param name="escape"     select="$escape"/>
              <xsl:with-param name="with"       select="$with"/>
              <xsl:with-param name="prevText"   select="$text"/>
            </xsl:call-template>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="substring-before($text,$escape)"/>
            <xsl:value-of select="$with"/>
            <xsl:call-template name="escapeString">
              <xsl:with-param name="text"       select="substring-after($text,$escape)"/>
              <xsl:with-param name="escape"     select="$escape"/>
              <xsl:with-param name="with"       select="$with"/>
              <xsl:with-param name="prevText"   select="$text"/>
            </xsl:call-template>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$text"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  
  <xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
  <xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>
  <!-- 
  || json properties can't have a minus sign  
  -->
  <xsl:template name="nameWithOptinalMinusToCamelcase">
    <xsl:param name="name" select="name()"/>
    <xsl:choose>
      <xsl:when test="contains($name,'-')">
        <xsl:value-of select="substring-before($name,'-')"/>
        <xsl:variable name="after" select="substring-after($name,'-')"/>
        <xsl:value-of select="translate(substring($after,1,1), $lower, $upper)"/>
        <xsl:call-template name="nameWithOptinalMinusToCamelcase">
          <xsl:with-param name="name" select="substring($after,2)"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$name"/>
      </xsl:otherwise>
    </xsl:choose>
    
  </xsl:template>
</xsl:stylesheet>

Applying on this xml:

<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn://xyz/soap/cegtar/unique">
  <SOAP-ENV:Body>
    <ns1:abcResponse>
      <ns1:abc id="12345">
        <ns1:Rovatok/>
        <ns1:ScoringAdatok>
          <ns1:XData tipus="11" megnevezes="APEH hátralék (rendezetlen)">
            <ns1:Value num="1" subtype="xbool">false</ns1:Value>
          </ns1:XData>
          <ns1:XData tipus="13" megnevezes="Egyéb VH (rendezetlen)">
            <ns1:Value num="1" subtype="xbool">false</ns1:Value>
          </ns1:XData>
          <ns1:XData tipus="46" megnevezes="Cég-állapot (APAFI helyett)">
            <ns1:Value num="1" subtype="xdate">0000-00-00</ns1:Value>
            <ns1:Value num="2" subtype="xnum">0</ns1:Value>
          </ns1:XData>
        </ns1:ScoringAdatok>
      </ns1:abc>
    </ns1:abcResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Results in:

{
  "SOAP-ENV:Envelope": {
    "SOAP-ENV:Body": {
      "ns1:abcResponse": {
        "ns1:abc": {
          "id": 12345,
          "ns1:Rovatok": "",
          "ns1:ScoringAdatok": {
            "ns1:XData": [
              {
                "tipus": 11,
                "megnevezes": "APEH hátralék (rendezetlen)",
                "ns1:Value": [
                  {
                    "num": 1,
                    "subtype": "xbool",
                    "text": false
                  }
                ]
              },
              {
                "tipus": 13,
                "megnevezes": "Egyéb VH (rendezetlen)",
                "ns1:Value": [
                  {
                    "num": 1,
                    "subtype": "xbool",
                    "text": false
                  }
                ]
              },
              {
                "tipus": 46,
                "megnevezes": "Cég-állapot (APAFI helyett)",
                "ns1:Value": [
                  {
                    "num": 1,
                    "subtype": "xdate",
                    "text": "0000-00-00"
                  },
                  {
                    "num": 2,
                    "subtype": "xnum",
                    "text": 0
                  }
                ]
              }
            ]
          }
        }
      }
    }
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM