Средство распознавания каталога Java для нескольких файлов каталога (а также catalog.xml) и DTD (а также XSD) и указатель на дополнительную информацию, а не только на API?
Я работаю с PTC Arbortext Editor, который был изначально написан в дни до XML (SGML) в конце 1980-х годов. Приложение 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"
Ресурсы
Переполнение стека
Я также рассмотрел валидацию XML с использованием XSD, резолвера каталогов и JAXP DOM для XSLT. Я чувствую, что это вряд ли решит мою проблему, но может ошибаться.
онлайн
Я также просмотрел следующие веб-сайты:
- http://magicmonster.com/kb/prg/java/xml/dom/xml_resolver.html
- http://magicmonster.com/kb/prg/java/xml/resolvers.html
- http://openjdk.java.net/jeps/268
- https://blog.xml.rocks/early-review-of-java-9-xml-catalogs/
- https://commons.apache.org/proper/commons-configuration/javadocs/v1.10/apidocs/org/apache/commons/configuration/resolver/CatalogResolver.html
- https://commons.apache.org/proper/commons-configuration/javadocs/v1.10/apidocs/org/apache/commons/configuration/resolver/CatalogResolver.html
- https://docs.oracle.com/javase/10/docs/api/javax/xml/catalog/CatalogManager.html
- https://docs.oracle.com/javase/9/docs/api/javax/xml/catalog/CatalogManager.html
- https://norman.walsh.name/2007/02/06/xmlresolver
Прецедент
Я загрузил код Java, структуру каталогов и XML на http://aapro.net/CatalogTest.zip
Должна быть возможность добавить что-то в мою программу, которая принимает путь к папке Test/ doctypes (папка, а не файл каталога в ней), а затем файл CatalogTest.xml должен успешно проанализировать с опцией "Validate", которую программа запрашивает. за. Другое (дорогое) программное обеспечение, поддерживающее SGML/XML, может это сделать. Средство распознавания каталога, получивший абсолютный путь к папке Test/ doctypes, должно иметь возможность следовать директиве CATALOG в файле Test/doctypes/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; Есть только несколько ситуаций, которые используют это в моей рабочей среде.
Несколько методов?
Я хотел бы попробовать более одного распознавателя и / или анализатора, либо позволить пользователю сделать выбор.
Встроенный код Java
(Также в вышеупомянутом почтовом файле)
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();
}
}
2 ответа
Средство Apache XML Commons Resolver поддерживает как каталоги OASIS XML, так и более старый формат каталогов OASIS TR9401. См. https://xerces.apache.org/xml-commons/components/resolver/.
Чтобы включить поиск по каталогу в вашем тестовом проекте, сделайте следующее:
Загрузите XML Commons Resolver с http://xerces.apache.org/mirrors.cgi.
Извлеките 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);
Я публикую этот пример кода, потому что он включает в себя использование org.apache.xml.resolver.tools.CatalogResolver, как это предложено mzjn, и успешно работает с моим примером на http://aapro.net/CatalogTest.zip. То есть, если я запустил его и отвечаю на первое приглашение Да (я хочу проверить), а на второе - с абсолютным путем к папке Test \ doctypes, а затем просматриваю CatalogTest.xml, оно успешно следует директиве CATALOG в Test. \doctypes to "../other/forms/catalog", который, в свою очередь, указывает местоположение DTD с помощью: PUBLIC "-//Test// Формирует тип документа //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();
}
}