Я использую API спецификации JAXP в сочетании с API Saxon-HE, основной целью которого было разработать приложение, которое преобразует файлы XML с помощью настраиваемых таблиц стилей XSLT, возможность переопределить сгенерированные выходные документы .. я пропущу подробности, потому что я создал пример проекта, чтобы проиллюстрировать возникшую проблему:JAXP saxon-he: XML-файл StreamSource не освобождает доступ к файлам после ошибки синтаксического анализа
использования: в случае ошибок преобразования, перемещение файла XML в другой каталог (может быть каталог ошибок) вызывает исключение доступа.
Когда я создаю экземпляр StreamSource на основе экземпляра файла (указывая на файл XML), если возникает некоторая ошибка синтаксического анализа, перемещение файла приводит к «Процесс не может получить доступ к файлу, потому что он используется другим процессом». Исключение.
Вот основные одноклассны приложение, которое я написал, чтобы проиллюстрировать этот вопрос:
package com.sample.xslt.application;
import net.sf.saxon.Configuration;
import net.sf.saxon.lib.FeatureKeys;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;
public class XsltApplicationSample {
public static void main(String[] args) throws Exception {
if (args.length != 2) {
throw new RuntimeException("Two arguments are expected : <xslFilePath> <inputFilePath>");
}
String xslFilePath = args[0];
String xmlFilePath = args[1];
TransformerFactory factory = TransformerFactory.newInstance();
factory.setAttribute(FeatureKeys.ALLOW_MULTITHREADING, Boolean.TRUE);
factory.setAttribute(FeatureKeys.RECOVERY_POLICY,
new Integer(Configuration.RECOVER_WITH_WARNINGS));
Source xslSource = new StreamSource(new File(xslFilePath));
Source xmlSource = new StreamSource(new File(xmlFilePath));
Transformer transformer = factory.newTransformer(xslSource);
try {
transformer.transform(xmlSource, new DOMResult());
} catch (TransformerException e) {
System.out.println(e.getMessage());
}
// move input file to tmp directory (for example, could be configured error dir)
File srcFile = Paths.get(xmlFilePath).toFile();
File tempDir = new File(System.getProperty("java.io.tmpdir"));
Path destFilePath = new File(tempDir, srcFile.getName()).toPath();
try {
Files.move(srcFile.toPath(), destFilePath, StandardCopyOption.REPLACE_EXISTING);
} catch (SecurityException | IOException e) {
System.out.println(e.getMessage());
}
}
}
Teh настроен XSLT содержимого файл преобразование должны быть действительными для воспроизведения. Если входной файл xml пуст, он создаст ошибку преобразования/разбора, но ошибка файла доступа не произойдет.
Пример входного файла для воспроизведения:
<root>
<elem>
</root>
Пример STDOUT:
JAXP: find factoryId =javax.xml.transform.TransformerFactory
JAXP: find factoryId =javax.xml.parsers.SAXParserFactory
JAXP: loaded from fallback value: com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
JAXP: created new instance of class com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl using ClassLoader: null
JAXP: find factoryId =javax.xml.parsers.SAXParserFactory
JAXP: loaded from fallback value: com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
JAXP: created new instance of class com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl using ClassLoader: null
JAXP: find factoryId =javax.xml.parsers.DocumentBuilderFactory
JAXP: loaded from fallback value: com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
JAXP: created new instance of class com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl using ClassLoader: null
JAXP: find factoryId =javax.xml.parsers.SAXParserFactory
JAXP: loaded from fallback value: com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
JAXP: created new instance of class com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl using ClassLoader: null
Error on line 3 column 3 of input_err.xml:
SXXP0003: Error reported by XML parser: The element type "elem" must be terminated by the
matching end-tag "</elem>".
org.xml.sax.SAXParseException; systemId: file:/C:/<path>/input_err.xml; lineNumber: 3; columnNumber: 3; The element type "elem" must be terminated by the matching end-tag "</elem>".
C:\<path>\input_err.xml -> C:\<path>\AppData\Local\Temp\input_err.xml: The process cannot access the file because it is being used by another process.
используемую командную строку (я использую Eclipse):
java ... -Djaxp.debug=1 -Dfile.encoding=UTF-8 -classpath <...> com.sample.xslt.application.XsltApplicationSample C:\<path>\transform.xsl C:\<path>\input_err.xml
использованный pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sample</groupId>
<artifactId>XsltExampleProject</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>XsltExampleProject</name>
<description>XSLT example project</description>
<dependencies>
<dependency>
<groupId>net.sf.saxon</groupId>
<artifactId>Saxon-HE</artifactId>
<version>9.7.0-7</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
Обходной я использовал для загрузки содержимого XML входного файла в памяти как строки, см следующее:
String xmlContent = FileUtils.readFileToString(new File(xmlFilePath), StandardCharsets.UTF_8);
Source xslSource = new StreamSource(new File(xslFilePath));
Source xmlSource = new StreamSource(new StringReader(xmlContent));
ли я что-то пропустил при инициализации трансформатор? По умолчанию разрешен SAX Parser следует переопределить на другой API, рекомендованный Saxon? Я думаю, что синтаксический анализатор Xerces используется в соответствии с протоколом отладки, но полностью ли он совместим с реализацией трансформатора, предоставленной Saxon? Я немного запутался в этом ...
Спасибо за помощь!
Я проведу дополнительные проверки, но я уверен, что в этой ситуации Saxon передает исходный код в XML-парсер в качестве URI, поэтому поток открывается парсером XML и поэтому должен быть закрыт синтаксическим анализатором XML ; Саксон никогда не видит поток и поэтому не имеет возможности закрыть его. Попробуйте использовать Apache Xerces вместо JDK-версии Xerces; версия Apache почти всегда более надежна. Кроме того, передайте FileInputStream, а затем закройте его в блоке finally {}. –
Я добавил зависимость xercesImpl версии 2.11.0 в pom.xml, и ошибка с ней не возникает. Итак, из версии, поставляемой с JDK, я использую: java-версию «1.8.0_92»/Java (TM) SE Runtime Environment (build 1.8.0_92-b14)/Java HotSpot (TM) 64-разрядная серверная VM (сборка 25.92) -b14, смешанный режим) – PacDroid