xsl-list
[Top] [All Lists]

Re: [xsl] Better "Report Map" Function?

2018-10-06 10:24:31
Now that I look at my code again I think the lack of return type was in 
intentional in that I was trying to have both the overall map data shown as 
well as get the Saxon element serialization when outside of a "value-of" 
context. But now I realize that the way I've used it is usually in 
"{local:report-map($map)}", which of course undoes that. Hmph. But setting the 
return type to xs:string without doing more with element() values would be bad.

Cheers,

E.

--
Eliot Kimber
http://contrext.com
 

On 10/6/18, 10:10 AM, "Eliot Kimber ekimber(_at_)contrext(_dot_)com" 
<xsl-list-service(_at_)lists(_dot_)mulberrytech(_dot_)com> wrote:

    You're right, I failed to set a return type (which I would normally always 
do). The intent is definitely to return a string.
    
    I haven't tried to make it 100% general at this point since this was 
something I hacked quickly in the context of debugging other code.
    
    I will correct the key sorting--I knew assuming the keys were strings was 
not sufficient but all the maps in my current projects only use string keys.
    
    For handling the possible entry values, I was pretty much just adding 
handlers as I needed them. For example, using this on the result of Saxon's 
collection() extensions that return maps of file information made it clear that 
I needed an "other" category...
    
    Cheers,
    
    Eliot
    
    --
    Eliot Kimber
    http://contrext.com
     
    
    On 10/6/18, 9:49 AM, "Michael Kay mike(_at_)saxonica(_dot_)com" 
<xsl-list-service(_at_)lists(_dot_)mulberrytech(_dot_)com> wrote:
    
        I wonder if this is really what you want?
        
        You haven't declared a return type for the function, so there's going 
to be no conversion applied, which means it's going to output a sequence of 
interspersed text and element nodes. That's probably fine if the function call 
always appears within xsl:value-of, but it's not very convenient if, for 
example, you want to call it as an argument of contains() or substring(). I 
would have thought a function that returned a string would be more useful.
        
        And a function that only handles map values that are maps, elements, 
document nodes, single strings, single booleans, or single integers doesn't 
feel very general-purpose.
        
        But you're right that there's probably nothing off-the-shelf that gives 
you exactly what you want. For many purposes (e.g. diagnostic output) the 
serialize() function with method=adaptive will be adequate, but if you want 
something better then you have to do it yourself.
        
        Incidentally, sorting the keys of a map like this will fail if the keys 
include non-ordered values such as QNames, of if they include a mixture of 
strings and numbers. If you want to be sure the sort won't fail, convert keys 
to strings before sorting.
        
        Michael Kay
        Saxonica
        
        > On 6 Oct 2018, at 14:33, Eliot Kimber ekimber(_at_)contrext(_dot_)com 
<xsl-list-service(_at_)lists(_dot_)mulberrytech(_dot_)com> wrote:
        > 
        > I needed a way to report the contents of maps as nicely-formatted 
strings using XSLT 3 and Saxon (Saxon's built-in serialization of maps did not 
really satisfy me).
        > 
        > Here's my solution:
        > 
        >  <xsl:function name="local:report-map">
        >    <xsl:param name="map" as="map(*)"/>
        >    <xsl:param name="level" as="xs:integer"/>
        > 
        >    <xsl:variable name="indent" as="xs:string?" 
        >      select="(for $i in 1 to $level return '  ') => string-join()"
        >    />
        >    <xsl:text>&#x0a;{$indent}{{</xsl:text>
        >    <xsl:for-each select="map:keys($map) => sort()">
        >      <xsl:variable name="key" select="."/>
        >      <xsl:variable name="value" select="map:get($map, $key)"/>
        >      <xsl:text>&#x0a;{$indent}  "{$key}" : </xsl:text>
        >      <xsl:choose>
        >        <xsl:when test="$value instance of map(*)">
        >          <xsl:text> Map:</xsl:text>
        >          <xsl:sequence select="local:report-map($value, $level + 3)"/>
        >        </xsl:when>
        >        <xsl:when test="$value instance of element()">
        >          <xsl:sequence select="$value"/>
        >        </xsl:when>
        >        <xsl:when test="$value instance of document-node()">
        >          <xsl:text> document-node(): {name($value/*)} - 
"{document-uri($value)}"</xsl:text>
        >        </xsl:when>
        >        <xsl:when test="$value instance of xs:string or $value 
instance of xs:boolean or $value instance of xs:integer">
        >          <xsl:text>"{$value}"</xsl:text>
        >        </xsl:when>
        >        <xsl:otherwise>
        >          <xsl:text>unhandled value</xsl:text>
        >        </xsl:otherwise>
        >      </xsl:choose>
        >    </xsl:for-each>
        >    <xsl:text>&#x0a;{$indent}}}</xsl:text>
        >  </xsl:function>
        > 
        > Which produces output like this:
        > 
        > {
        >   "baseType" : "map"
        >   "doc" :  document-node(): map - 
"file:/Users/ekimber/workspace/paccar/repo/paccar/sample_data/test-project/calibration-data/MX11_NA/calibrationSLMP_MX11_NA.ditamap"
        >   "engineFamilies" : "10.8M01"
        >   "key" : ""
        >   "relpath" : 
"calibration-data/MX11_NA/calibrationSLMP_MX11_NA.ditamap"
        >   "type" : "map"
        > }
        > 
        > I'm happy with the result but wondering if there's not a more 
efficient/cleaner/more elegant way to do the same thing?
        > 
        > Cheers,
        > 
        > E.
        > --
        > Eliot Kimber
        > http://contrext.com
        > 
        > 
        
        
        
    
    
    
--~----------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
EasyUnsubscribe: http://lists.mulberrytech.com/unsub/xsl-list/1167547
or by email: xsl-list-unsub(_at_)lists(_dot_)mulberrytech(_dot_)com
--~--

<Prev in Thread] Current Thread [Next in Thread>