Saxon C# XSLT 2.0 with multiple input xml files

966 views Asked by At

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

There are 1 answers

3
Martin Honnen On

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));
        }
    }