Как я могу игнорировать проверку DTD, но сохранить Doctype при записи XML-файла? - PullRequest
3 голосов
/ 24 февраля 2009

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

  • жестко закодированные ссылки на информацию о схеме / доктрине в любом месте моего кода. Информация о типе документа находится в исходном документе, я хочу сохранить именно эту информацию о типе документа и не предоставлять ее снова из моего кода. Если документ не имеет DocType, я не буду его добавлять. Меня не волнует форма или содержимое этих файлов, за исключением нескольких моих узлов.
  • пользовательские EntityResolvers или StreamFilters для пропуска или иного манипулирования исходной информацией (Жаль, что информация о пространстве имен кажется каким-то недоступной из файла документа, в котором она объявлена, но я могу управлять с помощью более уродливых XPaths)
  • Проверка DTD. У меня нет ссылочных DTD, я не хочу их включать, и манипулирование Node вполне возможно, не зная о них.

Цель состоит в том, чтобы исходный файл был полностью неизменным, за исключением измененных узлов, которые извлекаются через XPath. Я хотел бы уйти со стандартным материалом javax.xml.

Мой прогресс до сих пор:

    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

    factory.setAttribute("http://xml.org/sax/features/namespaces", true);
    factory.setAttribute("http://xml.org/sax/features/validation", false);
    factory.setAttribute("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
    factory.setAttribute("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

    factory.setNamespaceAware(true);
    factory.setIgnoringElementContentWhitespace(false);
    factory.setIgnoringComments(false);
    factory.setValidating(false);
    DocumentBuilder builder = factory.newDocumentBuilder();
    Document document = builder.parse(new InputSource(inStream));

Это успешно загружает источник XML в org.w3c.dom.Document, игнорируя проверку DTD. Я могу сделать свои замены, а затем я использую

    Source source = new DOMSource(document);
    Result result = new StreamResult(getOutputStream(getPath()));

    // Write the DOM document to the file
    Transformer xformer = TransformerFactory.newInstance().newTransformer();
    xformer.transform(source, result);

чтобы написать обратно. Что почти идеально. Но тег Doctype пропал, что бы я ни делал. Во время отладки я обнаружил, что в объекте Document после синтаксического анализа есть объект DeferredDoctypeImpl [log4j: configuration: null], но он почему-то неверный, пустой или игнорируется. Файл, на котором я тестировал, начинается примерно так (но он одинаков для других типов файлов):

<? Xml version = "1.0" encoding = "UTF-8"?>

<! DOCTYPE log4j: настройка системы "log4j.dtd">

[...]

Я думаю, что есть много (простых?) Способов вовлечения хаков или добавления дополнительных JAR-файлов в проект. Но я бы хотел иметь его с теми инструментами, которые я уже использую.

Ответы [ 3 ]

2 голосов
/ 24 февраля 2009

Извините, получил прямо сейчас, используя XMLSerializer вместо Transformer ...

0 голосов
/ 16 ноября 2017

Я попытался использовать библиотеку LSSerializer и не смог ничего с ней поделать с точки зрения сохранения Doctype. Это решение, которое Стефан, вероятно, использовал Примечание: это в Scala, но использует библиотеку Java, поэтому просто конвертируйте ваш код

import com.sun.org.apache.xml.internal.serialize.{OutputFormat, XMLSerializer}
 def transformXML(root: Element, file: String): Unit = {
    val doc = root.getOwnerDocument
    val format = new OutputFormat(doc)
    format.setIndenting(true)
    val writer = new OutputStreamWriter(new FileOutputStream(new File(file)))
    val serializer = new XMLSerializer(writer, format)
    serializer.serialize(doc)

  }
0 голосов
/ 01 марта 2011

Вот как вы можете сделать это, используя LSSerializer, найденный в JDK:

    private void writeDocument(Document doc, String filename)
            throws IOException {
        Writer writer = null;
        try {
            /*
             * Could extract "ls" to an instance attribute, so it can be reused.
             */
            DOMImplementationLS ls = (DOMImplementationLS) 
                    DOMImplementationRegistry.newInstance().
                            getDOMImplementation("LS");
            writer = new OutputStreamWriter(new FileOutputStream(filename));
            LSOutput lsout = ls.createLSOutput();
            lsout.setCharacterStream(writer);
            /*
             * If "doc" has been constructed by parsing an XML document, we
             * should keep its encoding when serializing it; if it has been
             * constructed in memory, its encoding has to be decided by the
             * client code.
             */
            lsout.setEncoding(doc.getXmlEncoding());
            LSSerializer serializer = ls.createLSSerializer();
            serializer.write(doc, lsout);
        } catch (Exception e) {
            throw new IOException(e);
        } finally {
            if (writer != null) writer.close();
        }
    }

Необходимый импорт:

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import org.w3c.dom.Document;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSSerializer;

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

...