pythonpython-3.xxsltsaxonsaxon-c

Python API for SaxonC-HE fails when creating XDM dict with string values


I am using SaxonC-HE 12.5 to apply an XSL 3.0 transformation in a Python 3.10 application. The XSL stylesheet takes a parameter that is a map of string -> string, as follows:

<xsl:param name="myMapParam" as="map(xs:string, xs:string)">

To build this map, I am using the create_xdm_dict() static method from saxonche to convert a Python dict to an XDM dict, then use this XDM dict as input to the make_map() method in PySaxonProcessor to create a map that I can pass as a parameter into the transformation:

from saxonche import *

with PySaxonProcessor(license=False) as saxonproc:

    dict = {"a": saxonproc.make_string_value("jack"),
            "b": saxonproc.make_string_value("jill")}

    xdm_dict = create_xdm_dict(saxonproc, dict)
    map = saxonproc.make_map(xdm_dict)

In fact, this code is almost exactly like that in the SaxonC documentation, except that the values in the dict are strings, not numbers.

When I attempt this, Saxon fails in the create_xdm_dict() method with a java.lang.NumberFormatException because it looks like it tries to convert the values into numbers (Doubles, to be precise) even though I explicitly asked to make string values:

java.lang.NumberFormatException: For input string: "jack"
    at jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2054)
    at jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
    at java.lang.Double.parseDouble(Double.java:543)
    at net.sf.saxon.type.StringToDouble.stringToNumber(StringToDouble.java:178)
    at net.sf.saxon.s9api.XdmAtomicValue.getLongValue(XdmAtomicValue.java:454)
    at net.sf.saxon.option.cpp.XdmValueForCpp.getLongValue(XdmValueForCpp.java:235)

Any guidance on how to create an XDM map with string values would be much appreciated. Thank you!


Solution

  • The dictionary keys also have to be created as XDM strings with saxon_processor.make_string_value e.g.

    from saxonche import *
    
    with PySaxonProcessor() as saxon_proc:
        print(saxon_proc.version)
    
        python_dic = { 'a' : 'foo', 'b' : 'bar' }
    
        xdm_map = saxon_proc.make_map({saxon_proc.make_string_value(key) : saxon_proc.make_string_value(value) for key, value in python_dic.items()})
    
        print(xdm_map)
    
        xpath_proc = saxon_proc.new_xpath_processor()
    
        xpath_proc.declare_variable('map1')
    
        xpath_proc.set_parameter('map1', xdm_map)
    
        print(xpath_proc.evaluate_single('$map1?a'))
    
        xslt_executable = saxon_proc.new_xslt30_processor().compile_stylesheet(stylesheet_file='sheet1.xsl')
    
        xslt_executable.set_parameter('map1', xdm_map)
    
        print(xslt_executable.call_template_returning_string())
    
    
    

    gives

    SaxonC-HE 12.5 from Saxonica
    map{"a":"foo","b":"bar"}
    foo
    foo
    

    with e.g. sample XSLT being

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="3.0"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      exclude-result-prefixes="#all">
    
      <xsl:param name="map1" as="map(xs:string, xs:string)" required="yes"/>
    
      <xsl:output method="text" indent="yes"/>
    
      <xsl:template match="/" name="xsl:initial-template">
          <xsl:sequence select="$map1?a"/>
      </xsl:template>
    
    </xsl:stylesheet>