6

I'm a noobie to stackoverflow and xslt so I hope I don't sound unintelligent!

So I am working with SDI for a GIS company and I have a task that requires me to convert points that are in one spacial reference system (SRS) coordinate plane, such as EPSG:4035, to the world SRS, aka EPSG:4326. This really isn't a problem for me since I have the accessibility of an online service that will just give me what I want. However, the format that it outputs is in either JSON or HTML. I have browsed for a while to find a way to extract information from a JSON file but most of the techniques I have seen use xslt:stylesheet version 2.0, and I have to use version 1.0. One method I thought about doing was using the document($urlWithJsonFormat) xslt function, however this only accepts xml files.

Here is an example of the JSON formatted file that I would retrieve after asking for the conversion:

{
  "geometries" : 
  [{
      "xmin" : -4, 
      "ymin" : -60, 
      "xmax" : 25, 
      "ymax" : -41
    }
  ]
}

All I simply want are the xmin, ymin, xmax, and ymax values, that's all! It just seems so simple yet nothing works for me...

Sean B. Durkin
  • 12,659
  • 1
  • 36
  • 65
user1634609
  • 59
  • 1
  • 3
  • 1
    Can you upgrade to XSLT 2.0? It would make things a lot easier for you. – Sean B. Durkin Aug 30 '12 at 04:01
  • 1
    Depending on your XSLT processor, you could pass the entire jason string in via a style-sheet parameter. What is your processor? Is it server-side or client-side? – Sean B. Durkin Aug 30 '12 at 04:09
  • should be a .NET Processor and switching to XSLT 2.0 is out of the question. – user1634609 Aug 30 '12 at 16:48
  • XSLT 1.0 is really the wrong tool for this. It requires XML as its (main) input. As others point out, you can find ways to pass in non-XML, but they are awkward. Is there a reason why you must use XSLT? – LarsH Aug 30 '12 at 01:19
  • @SeanB.Durkin: OK regarding unparsed-text() - I usually don't think about non-standard extensions, though they certainly are an option in many situations. As mentioned above, I agree with you that XSLT 1.0 can take non-XML input. Nevertheless it still requires XML input (hence I question the "No"), even if the stylesheet ignores it. However the way I phrased it made it sound like XSLT requires XML for *all* of its input. I've revised the answer. – LarsH Aug 30 '12 at 13:46
  • @LarsH The reason I "want" to use XSLT is because there is a lot of underlying java code that I am not really allowed to touch for the time being. So implementing a way to do this using java would be a bit rough for me, although now that I think about it Javascript could work. – user1634609 Aug 30 '12 at 17:01
  • No. Most XSLT 1.0 processors can take text input, either through the document() function applied on a parameter value, or by parameter directly. – Sean B. Durkin Aug 30 '12 at 04:08
  • The X in XSLT stands for XML, after all. You're trying to use a hammer to pound in a screw here. –  Aug 30 '12 at 02:55
  • For a style-sheet which chooses to ignore the main input document, but instead reads and processes other files, whose URIs are supplied by parameter, for these style-sheets, I think it is fair enough to say the true input to the style-sheet are these parameter referenced documents. When I said the document() function, I actually meant the unparsed-text() function via the exslt extension. Of course, this only works if supported. In this sense (in terms of actual use), and in this special circumstance (support for unparsed-text), an XSLT 1.0 can take non-XML text input. – Sean B. Durkin Aug 30 '12 at 07:58
  • @SeanB.Durkin: true that XSLT 1.0 processors can take (non-XML) text input, e.g. via a parameter. This does not negate the statement that the processors require XML input... presumably that's the statement your "No" is aimed at? Also I'm not sure what you mean about `document()` applied on a parameter value allowing non-XML input. In general, you can wrap non-XML in an XML wrapper, and then throw away the wrapper (as Mads does). That's a good solution if you must use XSLT, but why use XSLT? – LarsH Aug 30 '12 at 06:42

2 Answers2

5

You could use an external entity to include the JSON data as part of an XML file that you then transform.

For instance, assuming the example JSON is saved as a file called "geometries.json" you could create an XML file like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE wrapper [
<!ENTITY otherFile SYSTEM "geometries.json">
]>
<wrapper>&otherFile;</wrapper>

And then transform it with the following XSLT 1.0 stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

<xsl:template match="wrapper">
    <geometries>
        <xsl:call-template name="parse-json-member-value">
            <xsl:with-param name="member" select="'xmin'"/>
        </xsl:call-template>
        <xsl:call-template name="parse-json-member-value">
            <xsl:with-param name="member" select="'ymin'"/>
        </xsl:call-template>
        <xsl:call-template name="parse-json-member-value">
            <xsl:with-param name="member" select="'xmax'"/>
        </xsl:call-template>
        <xsl:call-template name="parse-json-member-value">
            <xsl:with-param name="member" select="'ymax'"/>
        </xsl:call-template>
    </geometries>
</xsl:template>

    <xsl:template name="parse-json-member-value">
        <xsl:param name="member"/>
        <xsl:element name="{$member}">
            <xsl:value-of select="normalize-space(
                                    translate(
                                        substring-before(
                                            substring-after(
                                                substring-after(.,
                                                    concat('&quot;', 
                                                           $member, 
                                                          '&quot;'))
                                                , ':')
                                            ,'&#10;')
                                    , ',', '')
                                  )"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

To produce the following output:

<geometries>
   <xmin>-4</xmin>
   <ymin>-60</ymin>
   <xmax>25</xmax>
   <ymax>-41</ymax>
</geometries>
Mads Hansen
  • 63,927
  • 12
  • 112
  • 147
  • Nice answer. You could take it a step further and not even require the constructed wrapper document. Text input can be taken directly from the the jason file using the document() function, and having the jason document passed in by parameter. – Sean B. Durkin Aug 30 '12 at 04:06
  • 1
    @SeanB.Durkin: are you sure that `document()` can access a non-XML document in XSLT 1.0? http://www.w3.org/TR/xslt#function-document "The document function allows access to XML documents other than the main source document." – LarsH Aug 30 '12 at 06:49
  • @Mads +1, good answer. If he has the opportunity to download and save the json to a file, so that the external entity would work, then he could also save it to a file with an XML wrapper in the same file; in that case, even XML parsers that don't process DTDs (as not all conformant XML parsers are required to do, right?) would be able to handle the wrapped JSON. – LarsH Aug 30 '12 at 06:55
  • 1
    @LarsH My bad. I was thinking of unparsed-text(). Although this is an XSLT 2.0 function, it is supposed to be available to supporting XSLT 1.0 processors via exslt. – Sean B. Durkin Aug 30 '12 at 07:48
  • 1
    Although the wrapper technique is simple and it works, a little bit of care needs to be taken around edge cases. If wrapping text directly, the text needs to be XML text encoded, otherwise < as data can be either misinterpreted or result in a malformed document. If wrapping an entity reference you need to confirm that your XSLT processor supports entity definitions this way. This support is not mandatory. – Sean B. Durkin Aug 30 '12 at 08:07
  • 1
    @LarsH - it would also be possible to reference a URL for the hosted JSON, rather than downloading a local file first. i.e. `<!ENTITY otherFile SYSTEM "http://example.com/geometries.json">` – Mads Hansen Aug 30 '12 at 12:35
  • Mads: Yes, but if you do that, you either have to have a constant URL (which may or may not be possible, depending on the web data sources available to the OP), or you have to be modifying the XML file to change the URL. If you're modifying the XML file, you could just as well use the wrapper method, with fewer constraints on the XML parser (which may be part of the XSLT processor). As @Sean alludes to, you'd want to XML-escape the json data, probably by using CDATA markup as part of the wrapper. – LarsH Aug 30 '12 at 13:41
  • @MadsHansen I like your answer but this works only if I have saved the document somewhere first, right? I guess I wasn't quite clear with my method of calling this Json file. I have an xslt file that is transforming a response sent from a WFS. I use the information being passed to me to retrieve the initial SRS x/y min/max and then later in the xslt file I want to convert these values to a different SRS, and I do this by doing a HTTP GET request using a url with filters (i.e. http:////projection?). Is it possible to save this output and then open it in the same xslt? – user1634609 Aug 30 '12 at 17:14
  • @user1634609 I put in one of the previous comments a note that you could reference the URL of the JSON file, rather than saving locally first. However, if the URL is not static, you would still need to somehow dynamically create the "wrapper file". Creating multiple output files within a single XSLT is only possible in XSLT 2.0. However, you could perform multiple XSLT transformations. – Mads Hansen Aug 30 '12 at 20:19
  • @MadsHansen Thanks for the advice! I talked to the higher ups and they are letting me modify the Java code, which makes things easier :) – user1634609 Aug 31 '12 at 00:29
1

The two main choices here seem to be:

  1. write (or use) a JSON parser in XSLT 1.0, or
  2. use some other language than XSLT.

Since XSLT 1 engines generally can't process JSON directly I'd recommend using some other language to convert to XML.

https://github.com/WelcomWeb/JXS may help you too, if this is XSLT in a Web browser.

Mads Hansen
  • 63,927
  • 12
  • 112
  • 147
barefootliam
  • 619
  • 3
  • 7