0

INPUT I'm getting:

  <table>   
    <source1>
      <ptr>
          <data name="text1">20431</data>
          <data name="text2">collins</data>
          <data name="text3">20141231></data>
          <data name="text4">regard</data>
      </ptr>
    </source1>
    <source2>
        <ptr>
            <data name="note1">C</data>
            <data name="note2">A</data>
            <data name="note3">22356</data>
            <data name="note4">465506</data>
            <data name="note5">434562491057912</data>
            <data name="note6">milk</data>
            <data name="note7">dfRTA</data>
        </ptr>
    </source2>
    <source3>
    <enroll>n</enroll>
    </source3>
    </table>

need to convert this to json as

{
"table":
{
"source1":
{"text1":"20431",
"text2": "collins",
"text3": "20141231",
"text4" :"regard"
},
"source2":
{"note1":"20431",
"note2": "A",
"note3":"22356",
"note4":"465506",
"note5":"434562491057912",
"note6":"milk",
"note7":"dfRTA"
}
}}

How to convert using xsl any generic one instead of matching on each attribute name. Any one has the stylesheet which will do the above transformation

mnvbrtn
  • 558
  • 1
  • 8
  • 27
  • What determines which elements are used in the JSON (eg. `table`, `source1`) and which are not (eg. `ptr`, `enroll`)? – Richard Dec 22 '14 at 16:14
  • Those are fixed all elements with "data" needs to be converted to json. that is the reqmnet – mnvbrtn Dec 22 '14 at 17:34
  • 2
    ["XSLT is generally the wrong tool to produce JSON"](http://stackoverflow.com/questions/22645060/transforming-xml-to-json-with-custom-xslt-looses-curly-braces). – Mathias Müller Dec 22 '14 at 18:04
  • But `table`, `source1`, and `source2` appear in the output but are not "data" (and `table` is a parent of a data element either). Hence the question: an example does not make a specification. – Richard Dec 22 '14 at 18:48
  • I second the suggestion of avoiding square-peg-through-round-hole with XSLT. This is problematic due to content impedance (JSON != XML) and related lack of metadata that is needed to do proper conversion. If you happen to have actual proglang Objects that express content, reading XML into that, then writing as JSON leads to much better translation of data. – StaxMan Dec 23 '14 at 01:25

1 Answers1

1

As already mentioned at the comment, XSLT is generally the wrong tool to produce JSON. But if you already checked the link provided and still want to test if it works for your requirements or other reasons, following XSLT generates a text file which is, for your example input XML, valid JSON format:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" doctype-public="XSLT-compat" 
     omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
  <xsl:template match="table">
    <xsl:text>{ "table" : {</xsl:text>
    <xsl:apply-templates/>
    <xsl:text>}}</xsl:text>
  </xsl:template>
  <xsl:template match="*[starts-with(local-name(),'source') and .//data]">
    <xsl:text>"</xsl:text>
    <xsl:value-of select="local-name()"/>
    <xsl:text>" :</xsl:text>
    <xsl:text>{</xsl:text>
    <xsl:apply-templates select="@*|node()"/>
    <xsl:text>}</xsl:text>
    <xsl:if test="following-sibling::*[starts-with(local-name(),'source') 
                                            and .//data]">
        <xsl:text>, </xsl:text>
    </xsl:if>
  </xsl:template>
  <xsl:template match="data">
    <xsl:text>"</xsl:text>
    <xsl:value-of select="@name"/>
    <xsl:text>" : "</xsl:text>
    <xsl:value-of select="."/>
    <xsl:text>"</xsl:text>
    <xsl:if test="following-sibling::data">
        <xsl:text>, </xsl:text>
    </xsl:if>
  </xsl:template>
  <xsl:template match="*[starts-with(local-name(),'source') 
                              and not(.//data)]"/>
</xsl:transform>

when applied to your input XML produces the following output:

{ "table" : {   
"source1" :{

      "text1" : "20431", 
      "text2" : "collins", 
      "text3" : "20141231>", 
      "text4" : "regard"
}, 
"source2" :{

        "note1" : "C", 
        "note2" : "A", 
        "note3" : "22356", 
        "note4" : "465506", 
        "note5" : "434562491057912", 
        "note6" : "milk", 
        "note7" : "dfRTA"
}
}}

Though not totally clear from the input, you mentioned as comment that all data elements need to be converted, so I excluded every parent element which name starts with source and has no children nodes with the name data (source3 in your example) and applied an empty template at the end to remove it from the ouput.
Note that for performance the expressions checking if source nodes contains any data nodes could be improved, depending on the input - e.g. in case it's known that sources containing data have this always wrapped in ptr, it's better to adjust match patterns like <xsl:template match="*[starts-with(local-name(),'source') and .//data]"> to <xsl:template match="*[starts-with(local-name(),'source') and ptr]"> ( or to ptr/data in case empty ptr elements could occur).
In case you want to remove unnecessary space from the output, you can add <xsl:strip-space elements="*"/> below the <xsl:output> to have the resulting text/"JSON" in one line.

Community
  • 1
  • 1
matthias_h
  • 11,356
  • 9
  • 22
  • 40