1

I have this Saxon C# method that takes in an input XML node, and and a string containing an XSLT 2.0 that I generated in MapForce. This function works fine with one XML input node, but I've recently created a mapping with multiple inputs (i.e. 2 xml files and 1 output). So I need to modify the Saxon code to become aware of another input node. However, the XsltTransformer class only seems to have an "InitialContextNode" property and nothing else to specify additional context nodes. Is anyone familiar with how to get the XsltTransformer class to see other XML nodes? Thanks.

 public static string transform(string inputXML, string transformText) {
    Processor processor = new Processor();
    TextReader stringReader = new StringReader(inputXML);
    XmlTextReader reader2 = new XmlTextReader(stringReader);
    reader2.XmlResolver = null;
    XdmNode input = processor.NewDocumentBuilder().Build(reader2);
    XsltTransformer transformer = processor.NewXsltCompiler().Compile(new StringReader(transformText)).Load();
    transformer.InputXmlResolver = null;
    transformer.InitialContextNode = input;
    Serializer serializer = new Serializer();
    StringBuilder sb = new StringBuilder();
    TextWriter txt = new StringWriter(sb);
    serializer.SetOutputWriter(txt);
    transformer.Run(serializer);
    return sb.ToString();
}

So basically what I need is something like this:

public static XElement transform(string transformText, params XElement[] inputXML) {
    if (string.IsNullOrEmpty(transformText)) return null;
    else {
        Processor processor = new Processor();
        List<XdmNode> nodes = new List<XdmNode>();
        foreach (XElement input in inputXML) {
            TextReader stringReader = new StringReader(input.ToString());
            XmlTextReader reader2 = new XmlTextReader(stringReader);
            reader2.XmlResolver = null;
            nodes.Add(processor.NewDocumentBuilder().Build(reader2));
        }
        XsltTransformer transformer = processor.NewXsltCompiler().Compile(new StringReader(transformText)).Load();
        transformer.InputXmlResolver = null;
        transformer.InitialContextNode = nodes[0]; // How do I get it to see the other nodes?
        Serializer serializer = new Serializer();
        StringBuilder sb = new StringBuilder();
        TextWriter txt = new StringWriter(sb);
        serializer.SetOutputWriter(txt);
        transformer.Run(serializer);
        return XElement.Parse(sb.ToString());
    }
}

XSLT code with 2 inputs for reference: http://pastebin.com/04ZTRe6m

1 Answers1

1

Your stylesheet has a global parameter <xsl:param name="response2" select="'response.xml'"/> which I suppose is the file name or URL of the secondary input document. If your input XML is a file of the name response.xml then you don't have to do anything, if it is a file of a different name then make sure you set the parameter as needed. The stylesheet later on does doc($response2) so it expects a URL.

If you want to change the complete approach and want to pass in the secondary input as an XdmNode then you would need to change the stylesheet code and you would need to set the parameter to the XdmNode in your C# code. The main change in the XSLT then would be to simply use $response2 anywhere the stylesheet does doc($response2).

Unrelated to your question I would like to point out that

    foreach (XElement input in inputXML) {
        TextReader stringReader = new StringReader(input.ToString());
        XmlTextReader reader2 = new XmlTextReader(stringReader);
        reader2.XmlResolver = null;
        nodes.Add(processor.NewDocumentBuilder().Build(reader2));
    }

seems inefficient (as your serialize the XElement to a string to parse the string), the normal way to have an XmlReader over an XElement is simply

    foreach (XElement input in inputXML) {
        using (XmlReader xr = input.CreateReader())
        {
          nodes.Add(processor.NewDocumentBuilder().Build(xr));
        }
    }
Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • Just to add a little to Martin's response, the API for setting a stylesheet parameter would be along the lines `transformer.SetParameter(new QName("response2"), new XdmAtomicValue("some.xml"))`. – Michael Kay Dec 30 '13 at 14:19
  • How do you actually pass the multiple documents into the engine though? InitialContext is still a single object – A X Dec 26 '19 at 02:58
  • @Abr, since XSLT 1 there is the `document` function which is powerful enough to load various documents, in XSLT 2 and in XQuery 1 there is the `collection` function and in XSLT and XQuery 3 there is additionally the `uri-collection` function. Ask separate, new questions on that if the relevant documentation and/or spec section don't help. – Martin Honnen Dec 26 '19 at 07:46