Matthew Simoneau
Matthew Simoneau

Reputation: 6279

Parse an XML fragment stored in a string into nodes in XSLT with SAXON for Java

In my XSLT, I have a variable which contains a string. This string contains a fragment of XML. This is coming in programmatically, but its the equivalent of this:

<xsl:variable name="xmlfrag" select="'<foo>this <bar>is</bar> it</foo>'"/>

Is there a way to parse this XML and add it to the output? We're using SAXON for Java.

Upvotes: 2

Views: 1611

Answers (2)

david.perez
david.perez

Reputation: 7012

The Dimitre solution requires a licensed version of Saxon.

Here is another solution that works also with the free version of Saxon.

I've tried it successfully with Saxon HE 9.5.X.

The inverse process is documented here.

It is about registering a custom extension function with these contents:

import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.lib.ExtensionFunctionCall;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.s9api.DocumentBuilder;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.value.SequenceType;

import javax.xml.transform.stream.StreamSource;

import java.io.StringReader;

@SuppressWarnings("serial")
public class XmlParser extends ExtensionFunctionDefinition {
    @Override
    public StructuredQName getFunctionQName() {
        return new StructuredQName("vis", URI, "my.custom.uri");
    }

    @Override
    public SequenceType[] getArgumentTypes() {
        return new SequenceType[] { SequenceType.SINGLE_STRING };
    }

    @Override
    public SequenceType getResultType(SequenceType[] sequenceTypes) {
        return SequenceType.NODE_SEQUENCE;
    }

    @Override
    public ExtensionFunctionCall makeCallExpression() {
        return new ExtensionFunctionCall() {
            DocumentBuilder constructor;

            @Override
            public Sequence call(XPathContext ctx, Sequence[] secs) throws XPathException {
                // Not synchronized, as worst case it initialize twice
                if (constructor == null)
                    constructor = new Processor(ctx.getConfiguration()).newDocumentBuilder();
                String xml = secs[0].head().getStringValue();
                try {
                    return constructor.build(new StreamSource(new StringReader(xml))).getUnderlyingNode();
                } catch (SaxonApiException e) {
                    final int max = 500;
                    String xml2 = xml.length() > 500 ? xml.substring(0, max)+"..." : xml;
                    throw new XPathException("Error when parsing XML.\n"+xml2, e);
                }
            }
        };
    }
}

You can use this function as follows:

<xs:value-of xmlns:vis="my.custom.uri" select="vis:parse-xml('<a>Test</a>')"/>

Upvotes: 1

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243449

Use the saxon:parse() extension function.

Upvotes: 2

Related Questions