0

我使用 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、Catalog Resolver 和 XSLT 的 JAXP DOM。我觉得这不太可能解决我的问题,但可能是错误的。

在线的

我还查看了以下网站:

测试用例

我已将 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。在我的工作环境中,只有少数 milspec 情况仍在使用它。

多种方法?

我愿意尝试多个解析器和/或解析器,或者让用户做出选择。

Java 代码内联

(也在前面提到的 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();

        }

}
4

2 回答 2

1

Apache XML Commons Resolver 支持 OASIS XML 目录和旧的 OASIS TR9401 目录格式。请参阅https://xerces.apache.org/xml-commons/components/resolver/

要在您的测试项目中启用目录查找,请执行以下操作:

  1. 从http://xerces.apache.org/mirrors.cgi#binary下载 XML Commons Resolver 。

  2. 提取 resolver.jar 并将其添加到您的类路径中。

  3. 创建一个名为 CatalogManager.properties 的文本文件并将其放在您的类路径中。在此文件中,添加目录的路径:

    catalogs=./doctypes/catalog
    

    目录文件的位置也可以通过xml.catalog.filesJava 系统属性指定。

  4. 在 ParseXmlWithCatalog.java 中,添加一条import语句并创建CatalogResolver. 将该实例设置为解析器的EntityResolver

    import org.apache.xml.resolver.tools.CatalogResolver;
    ...
    
    CatalogResolver cr = new CatalogResolver();
    builder.setEntityResolver(cr);
    
于 2018-08-10T15:22:28.770 回答
0

我发布此示例代码是因为它包含了 mzjn 建议的 org.apache.xml.resolver.tools.CatalogResolver 的使用,并成功地与我在http://aapro.net/CatalogTest.zip的示例一起使用。也就是说,如果我运行它,并用 Yes(我想要验证)回答第一个提示,第二个用 Test\doctypes 文件夹的绝对路径回答,然后浏览到 CatalogTest.xml,它成功地遵循了 Test 中的 CATALOG 指令\doctypes 到“../other/forms/catalog”,然后指定 DTD 的位置:PUBLIC“-//Test//Forms Document Type//EN”“forms.dtd”,并告诉我我的顶部级节点称为“表格”。

此时,我将把这个解决方案合并到我的 XML 差异程序中。如果我发现需要调整以处理多条目目录路径和/或处理目录和 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();
        }
}
于 2018-08-18T03:25:30.523 回答