Китайские символы во входном XML приводят к тому, что преобразование XSLT выбрасывает недопустимые ссылки на символы в выходном XML - PullRequest
0 голосов
/ 05 ноября 2019

Я пытаюсь выяснить, почему мое простое XSLT-преобразование, которое должно преобразовать XML в XML, похоже, не в состоянии этого достичь.

Преобразование просто копирует все:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:output method="xml" encoding="utf-8" />
    <xsl:template match="*|@*">
        <xsl:copy>
            <xsl:apply-templates select="*|@*|text()" />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

При входном XML-файле, например ниже:

<?xml version="1.0" encoding="utf-8"?>
<foo xmlns="uri:foo">
  <name>丕????</name>
</foo>

результат будет следующим:

<?xml version="1.0" encoding="utf-8"?>
<foo xmlns="uri:foo">
  <name>丕&#55360;&#56326;&#55360;&#56325;&#55360;&#56333;&#55360;&#56384;</name>
</foo>

Все инструменты, которые я использую, зависят от(Java) Apache Xalan 2.7.1 XSLT-процессор, включая Eclipse (Mars) с плагином XSL Developer Tools, в котором я создал этот образец.

Последний плагин утверждает, что входной XML правильно сформирован, но выходной XMLнет (ссылка на символ � является недопустимым символом XML).

Почему мой процессор XSLT генерирует недопустимый XML и как я могу предотвратить это?

Фактический код соответствует следующему (вам нужно Xalan в вашем пути к классам):

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;

public class XSLTTest {

    private final TransformerFactory xalanTransFact;

    public XSLTTest() {
        xalanTransFact = new org.apache.xalan.processor.TransformerFactoryImpl();
    }

    public Templates createCustomTransformation(
            File transformation
    ) throws TransformerException, IOException {
        InputStreamReader readerTransformation = null;
        try {
            readerTransformation = new InputStreamReader(
                    new FileInputStream(transformation), StandardCharsets.UTF_8);  
            Templates transformer = xalanTransFact.newTemplates(
                    new StreamSource(readerTransformation)
            );
            return transformer;
        } catch (TransformerException | IOException ex) {
            throw ex;
        } finally {
            try {
                if (readerTransformation != null) {
                    readerTransformation.close();
                }
            } catch (IOException ex) {} 
        }
    }

    public File applyCustomTransformation(
            Transformer transformer, Reader transformeeReader, Path out, 
            boolean indent
    ) throws TransformerException, IOException {
        Writer writer = null;
        try {

            File file = out.toFile();
            writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);

            if (indent) {
                transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                transformer.setOutputProperty(
                        "{http://xml.apache.org/xslt}indent-amount",
                        String.valueOf(2));
            }
            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
            transformer.setOutputProperty(OutputKeys.ENCODING, "utf-8");

            transformer.transform(
                    new StreamSource(transformeeReader),
                    new StreamResult(writer));

            return file;

        } catch (TransformerException | IOException ex) {
            throw ex;
        } finally {      
            try {
                if (writer != null) {
                    writer.close();
                }
            } catch (IOException ex) {}
        }
    }

    private void saveToFile(File selectedFile, String content)
            throws FileNotFoundException, IOException {
        Writer writer = null;
        try {
            writer = new OutputStreamWriter(
                    new FileOutputStream(selectedFile), StandardCharsets.UTF_8);
            writer.write(content);
            writer.flush();
        }
        catch (FileNotFoundException ex) {
            throw ex;
        } catch (IOException ex) {
            throw ex;
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException ex) {
                }
            }
        }
    }

    public static void main(String[] args) throws IOException, TransformerException {
        String xslText = "" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n" +
"    version=\"1.0\">\n" +
"    <xsl:output method=\"xml\" encoding=\"utf-8\" />\n" +
"    <xsl:template match=\"*|@*\">\n" +
"        <xsl:copy>\n" +
"            <xsl:apply-templates select=\"*|@*|text()\" />\n" +
"        </xsl:copy>\n" +
"    </xsl:template>\n" +
"</xsl:stylesheet>";

        String xmlToParse = "" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<foo xmlns=\"uri:foo\">\n" +
"  <name>丕????</name>\n" +
"</foo>";

        XSLTTest test = new XSLTTest();

        Path xsl = Files.createTempFile("test", ".xsl");
        test.saveToFile(xsl.toFile(), xslText);        
        Templates templates = test.createCustomTransformation(xsl.toFile());
        Transformer transformer = templates.newTransformer();

        Path xml = Files.createTempFile("test-out", ".xml");
        StringReader reader = new StringReader(xmlToParse);
        test.applyCustomTransformation(transformer, reader, xml, true);

        System.out.println("Result is at: " + xml.toString());
    }
}

По причинам, я не могу переключиться на другойXSLT-процессор.

1 Ответ

0 голосов
/ 06 ноября 2019

Как писал @VGR в комментарии, это является проявлением ошибки https://issues.apache.org/jira/browse/XALANJ-2419.

Комментарий к их JIRA предлагает обходной путь - используйте UTF-16 в качестве выходной кодировки для преобразования вместо UTF-8, поскольку ошибка влияет только на последнее.

Так что в моем примере строку

transformer.setOutputProperty(OutputKeys.ENCODING, "utf-8");

необходимо заменить на

// workaround for https://issues.apache.org/jira/browse/XALANJ-2419
transformer.setOutputProperty(OutputKeys.ENCODING, "utf-16");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");            
writer.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");

, в то время как все остальное остаетсятот же самый. Фактические файлы по-прежнему записываются как UTF-8, но преобразование будет обрабатываться как UTF-16 внутри.

...