Bug #4815


Conversion of XDM maps to JS objects

Added by Debbie Lockett over 3 years ago. Updated almost 2 years ago.

IXSL extensions
Start date:
Due date:
% Done:


Estimated time:
Applies to JS Branch:
Fix Committed on JS Branch:
Fixed in JS Release:
SEF Generated with:
Contact person:
Additional contact persons:


On the Slack general channel, Pieter Lamers asked the following:

I'm trying to get scrollIntoView to work smoothly. Now, ixsl:call($target, 'scrollIntoView',[]) works, but I don't know how to pass the ScrollIntoViewOptions into the array. [...] This is what I would typically want to achieve: element.scrollIntoView({behavior: "smooth", block: "start", inline: "nearest"});

Intuitively, users expect to be able to supply a map(*) in the array for the 3rd argument to ixsl:call; but this will not work as expected, because the XDM to JS conversion does not automatically convert XDM maps to JavaScript object literals. (See!xdm/conversions). Instead if an XDM map is supplied here, it will be converted to a "wrapped XDM map object", which the scrollIntoView JavaScript method simply ignores.

Martin Honnen suggested a couple of work arounds:

you might want to set up a Javascript variable var options = {behavior: "smooth", block: "start", inline: "nearest"}; and pass it to the XSLT with e.g. SaxonJS.transform({ stylesheetLocation: 'sheet1.sef.json', sourceLocation: 'sample1.xml', stylesheetParams: { options: options } } ) In your XSLT you then use ixsl:call(., 'scrollIntoView', [ $options]) and declare <xsl:param name="options" required="true"/>

Or construct the scroll options inside XSLT with e.g. <xsl:param name="scrollOptions" as="xs:string" expand-text="no">{ "behavior" : "smooth", "block" : "start", "inline": "nearest" }</xsl:param> and ixsl:call(., 'scrollIntoView', [ ixsl:window() => ixsl:call('JSON.parse', [ $scrollOptions ]) ]). Seems a bit cumbersome, not sure whether there is a simple way to convert an XdmMap into a JavaScript object inside of Saxon-JS.

Indeed currently our recommended approach is that if you really want/need to work with JavaScript objects, then create them in the JavaScript space, since converting from XDM maps is not possible. But either: (a) this needs to be made clearer in the documentation; or (b) as Martin alludes, perhaps we should actually be providing a way to ask for (certain) XDM maps to be converted to JavaScript objects from the XSLT (e.g. with a new IXSL function).

Actions #1

Updated by Debbie Lockett over 3 years ago

I'm sure we have considered whether such an extension - which converts XDM maps to JavaScript object literals - would be useful (or was necessary) before; but perhaps not formally. One difficulty with this of course is that IXSL extensions need to be defined in the XJ and XX compilers; so this would only be available with later versions of Saxon 10.

Certainly we have done a lot of work on XDM to JS, and JS to XDM, conversion! See for instance Bug #3545: Inconsistencies using map functions on JS objects, and in particular #3545#note-10 and #3545#note-13:

Re Note 10:

"Still want to think about what happens when an XDM map is supplied in the array of arguments within an ixsl:call(). Currently this will be wrapped as an XDMValue, but it seems reasonable that really the JavaScript function called would expect a JS object."

We have stuck with XDM maps always being converted to wrapped XDMValue objects when they are passed to JS functions. It may not be obvious that the converted JS object will not just be a literal object (this should be made clear in the documentation), but since there is not a direct correspondence between XDM maps and JS objects, I think it's reasonable. If a user actually wants to work with JS objects, then it will be best to create and amend these in the JavaScript space, by calling JS global functions from the XSLT. The alternative is to work with XDM maps but provide your own conversion to JS (using a JS global function). But working with XDM maps on the XSLT side and using our internal conversion will no longer work.

Actions #2

Updated by Community Admin about 3 years ago

  • Applies to JS Branch 2 added
  • Applies to JS Branch deleted (2.0, Trunk)
Actions #3

Updated by Martynas Jusevicius over 2 years ago

The suggested workarounds in the JS space aren't great if the map entries depend on conditionals.

Is there any drawback (besides having to convert to/from string) to the following fn:serialize/JSON.parse based approach?

<xsl:variable name="options" select="map{ 'behavior': 'smooth', 'block': 'start', 'inline': 'nearest' }" as="map(xs:string, xs:string)"/>
<xsl:variable name="options-obj" select="ixsl:call(ixsl:window(), 'JSON.parse', [ $options => serialize(map { 'method': 'json' }) ])"/>
<xsl:value-of select="ixsl:call(ixsl:window(), 'JSON.stringify', [ $options-obj ])"/>

Seems to work for me. Not exactly round-trip, but this does not seem to be required for this use case.

Actions #4

Updated by Norm Tovey-Walsh almost 2 years ago

  • Sprint/Milestone set to SaxonJS 3.0

Please register to edit this issue

Also available in: Atom PDF Tracking page