我使用PTC Arbortext Editor,它最初是在1980年代后期的XML(SGML)之前的时代编写的。 Java应用程序使用 org.custommonkey.xmlunit 来区分XML文件。
diff工具无法解析文件(在Windows上),这些文件期望用分号分隔的各种目录文件位置的绝对路径列表,其中查找catalog
和/或catalog.xml
文件。它们可以使用CATALOG
指令。使用PUBLIC
标识符映射到相对于特定目录文件的路径。
我正在使用此目录信息解析XML,其中可能包含文件实体以及XML包含物。
对于某些用例,我可以设置验证false
并且可行(假设两个文件均有效),但是对于某些文件,我必须读取目录信息以解析XML中的文件实体。
我可以要求用户提供到其顶层目录位置的绝对路径的列表。但是我宁愿选择一个解析器并将其集成到我的代码中。
我正在使用Java 1.8,但是如果这样可以帮助/简化,则不要介意使用10。看来9有了 javax.xml.catalog 的一些简单支持,却不在1.8或10中。
如果可以的话,我可以提供我的解析代码,但我不会被任何一个解析器所困扰。
我的代码如下。为了LSParser
,我从DocumentBuilder
切换到setValidating(false)
。
以下是我想使用的其中一个文件的摘录:
<?xml version="1.0" encoding="UTF-8"?>
<!--Arbortext, Inc., 1988-2016, v.4002-->
<!DOCTYPE Composer PUBLIC "-//Arbortext//DTD Composer 1.0//EN"
"../doctypes/composer/composer.dtd" [
<!ENTITY % stock PUBLIC "-//Arbortext//DTD Fragment - ATI Stock filter list//EN" "../composer/stock.ent">
%stock;
]>
<?Pub Inc?>
<Composer>
<Label>Compose to PDF</Label>
. . .
<Resource>
<Label></Label>
<Documentation></Documentation>&epicGenerator;
&fileSerializer;
&serverProfiler;
&clientProfiler;
&xslTransformer;
&epicSerializer;
&switch;
&errorHandler;
&namespaceFixer;
&atiEventConverter;
&foPropagator;
&extensionHandler;
&ditaPostProcessor;
&ditaStyledElementsTranslator;
&atictFilter;
&applicabilityFilter;
</Resource>
这是我需要参考的目录文件之一的几行:
PUBLIC "-//Arbortext//ENTITIES SAX Event Upstream Loop//EN" "upstreamLoop.ent"
PUBLIC "-//Arbortext//ENTITIES keyRef Resolver//EN" "keyRefResolver.ent"
PUBLIC "-//Arbortext//ENTITIES ATI Change Tracking Filter 1.0//EN" "atictFilter.ent"
PUBLIC "-//Arbortext//ENTITIES Font Filter 1.0//EN" "fontFilter.ent"
PUBLIC "-//Arbortext//ENTITIES Simple Attribute Cascader//EN" "simpleAttrCascader.ent"
我也看过Validate XML using XSD, a Catalog Resolver, and JAXP DOM for XSLT。我觉得这不太可能解决我的问题,但可能是错误的。
我还查看了以下网站:
我已将Java代码,目录结构和XML上传到http://aapro.net/CatalogTest.zip
应该有可能在我的程序中添加一些内容,该内容接受Test / doctypes文件夹(该文件夹,而不是其中的目录文件)的路径,然后CatalogTest.xml文件应使用“验证”选项成功解析。程序提示。其他(昂贵的)SGML / XML感知软件也可以这样做。目录解析器一旦获得了Test / doctypes文件夹的绝对路径,就应该能够遵循Test / doctypes / catalog文件中的CATALOG指令,到达Test / other / forms / catalog文件,再到达Test / other / forms /forms.dtd。解析器应该能够解析Test / other / forms / forms.dtd并将其用于验证Test / CatalogTest.xml。
确实,整个过程应该能够处理此类目录文件或catalog.xml文件,并且应该能够解析DTD或XSD文件以及SGML或XML实例。但是我实际上并不太在乎SGML。在我的工作环境中,仍然只有少数军事环境使用它。
我愿意尝试多个解析器和/或解析器,或者让用户进行选择。
(也在上述zip文件中)
import java.io.File;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
public class ParseXmlWithCatalog {
public static void main(String[] args) {
int validating = JOptionPane.showOptionDialog(null, "Do you want validation?", "Please choose \"Yes\" for validation",
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, JOptionPane.YES_OPTION);
parseDoc(getFile(args), validating == JOptionPane.YES_OPTION);
}
private static boolean parseDoc(File inFile, boolean validate) {
if (inFile == null) {
JOptionPane.showMessageDialog(null, "Failure opening input XML.");
}
try {
/*
System.setProperty(DOMImplementationRegistry.PROPERTY, "org.apache.xerces.dom.DOMImplementationSourceImpl");
DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS");
LSParser builder = impl.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null);
LSParserFilter filter = new InputFilter();
builder.setFilter(filter);
*/
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
if (!validate) {
builderFactory.setValidating(false);
builderFactory.setAttribute("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
}
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document testDoc = builder.parse(inFile.getPath());
System.out.println(testDoc.getFirstChild().getNodeName());
} catch (Exception exc) {
JOptionPane.showMessageDialog(null, "Failure parsing input XML: " + exc.getMessage());
return false;
}
return true;
}
public static File getFile(String[] args) {
if (args.length > 1) {
JOptionPane.showMessageDialog(null, "Too many arguments.");
return null;
}
if (args.length == 1) {
return new File(args[0]);
}
JFileChooser fileChooser = new JFileChooser();
fileChooser.setMultiSelectionEnabled(false);
fileChooser.setDialogTitle("Select 1 XML file");
FileNameExtensionFilter filter = new FileNameExtensionFilter("XML Files", "xml", "ditamap", "dita", "style");
fileChooser.setFileFilter(filter);
int response = fileChooser.showOpenDialog(null);
if (response != JFileChooser.APPROVE_OPTION) {
// aborted
return null;
}
return fileChooser.getSelectedFile();
}
}
答案 0 :(得分:1)
Apache XML Commons Resolver支持OASIS XML目录和较旧的OASIS TR9401目录格式。参见https://xerces.apache.org/xml-commons/components/resolver/。
要在测试项目中启用目录查找,请执行以下操作:
从http://xerces.apache.org/mirrors.cgi#binary下载XML Commons Resolver。
提取resolver.jar并将其添加到您的类路径中。
创建一个名为CatalogManager.properties的文本文件,并将其放在您的类路径中。在此文件中,将路径添加到目录:
catalogs=./doctypes/catalog
还可以通过xml.catalog.files
Java系统属性来指定目录文件的位置。
在ParseXmlWithCatalog.java中,添加一个import
语句并创建一个CatalogResolver
的实例。将该实例设置为解析器的EntityResolver
:
import org.apache.xml.resolver.tools.CatalogResolver;
...
CatalogResolver cr = new CatalogResolver();
builder.setEntityResolver(cr);
答案 1 :(得分:0)
我之所以发布此示例代码,是因为它结合了mzjn所建议的org.apache.xml.resolver.tools.CatalogResolver的使用,并成功地在http://aapro.net/CatalogTest.zip处理了我的示例。就是说,如果我运行它,并用“是”(我要验证)回答第一个提示,并用Test \ doctypes文件夹的绝对路径回答第二个提示,然后浏览到CatalogTest.xml,它将成功遵循Test中的CATALOG指令。 \ doctypes为“ ../other/forms/catalog”,它依次使用以下命令指定DTD的位置:PUBLIC“-// Test // Forms文档类型// EN”“ forms.dtd”,并告诉我我的顶部级别的节点称为“表单”。
在这一点上,我将把这个解决方案整合到我的XML diff程序中。如果我发现需要调整以处理多条目目录路径和/或处理catalog和catalog.xml文件的混合使用,我将发布更新。但这可能要再过几周(或更长时间),我认为这段代码到了有人可能会觉得有用的地步。
import java.io.File;
import java.io.FilenameFilter;
import java.util.Map.Entry;
import java.util.Properties;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.xml.resolver.tools.CatalogResolver;
import org.w3c.dom.Document;
public class ParseXmlWithCatalog {
// Offer end-user the convenience of not having to specify which will be used,
// catalog and/or catalog.xml.
private static FilenameFilter catalogFileFilter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
if (name.equals("catalog") || name.equals("catalog.xml")) {
return true;
} else {
return false;
}
}
};
public static void main(String[] args) {
int validating = JOptionPane.showOptionDialog(null, "Do you want validation?",
"Please choose \"Yes\" for validation", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null,
null, JOptionPane.YES_OPTION);
if (validating == JOptionPane.YES_OPTION) {
String catPath = JOptionPane.showInputDialog(null,
"Please enter semi-colon-separated list of absolute paths to catalog folders, in desired search order; these are the locations of catalog or catalog.xml files, not the filenames.",
"Enter catalog path", JOptionPane.QUESTION_MESSAGE);
String[] catLocs = catPath.split(";");
StringBuilder sb = new StringBuilder();
for (String catLoc : catLocs) {
File[] catFiles = new File(catLoc).listFiles(catalogFileFilter);
for (File catFile : catFiles) {
if (sb.length() > 0) {
sb.append(";");
}
sb.append(catFile.toURI());
}
}
System.setProperty("xml.catalog.files", sb.toString());
System.out.println(
"Using the following top-level catalog files:\n" + System.getProperty("xml.catalog.files"));
System.setProperty("relative-catalogs", "yes");
}
parseDoc(getFile(args), validating == JOptionPane.YES_OPTION);
}
private static boolean parseDoc(File inFile, boolean validate) {
if (inFile == null) {
JOptionPane.showMessageDialog(null, "Failure opening input XML.");
}
try {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
if (!validate) {
builderFactory.setValidating(false);
builderFactory.setAttribute("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
}
DocumentBuilder builder = builderFactory.newDocumentBuilder();
CatalogResolver resolver = new CatalogResolver();
builder.setEntityResolver(resolver);
Document testDoc = builder.parse(inFile.getPath());
JOptionPane.showMessageDialog(null,
"The top level node is \"" + testDoc.getFirstChild().getNodeName() + "\"");
} catch (Exception exc) {
JOptionPane.showMessageDialog(null, "Failure parsing input XML: " + exc.getMessage());
return false;
}
return true;
}
public static File getFile(String[] args) {
if (args.length > 1) {
JOptionPane.showMessageDialog(null, "Too many arguments.");
return null;
}
if (args.length == 1) {
return new File(args[0]);
}
JFileChooser fileChooser = new JFileChooser();
fileChooser.setMultiSelectionEnabled(false);
fileChooser.setDialogTitle("Select 1 XML file");
FileNameExtensionFilter filter = new FileNameExtensionFilter("XML Files", "xml", "ditamap", "dita", "style");
fileChooser.setFileFilter(filter);
int response = fileChooser.showOpenDialog(null);
if (response != JFileChooser.APPROVE_OPTION) {
// aborted
return null;
}
return fileChooser.getSelectedFile();
}
}