Вопрос по java, serialization, transformer, xml – Установка пространств имен и префиксов в документе Java DOM

9

Я пытаюсь преобразовать ResultSet в файл XML. Я впервые использовал этот пример для сериализации.

import  org.w3c.dom.bootstrap.DOMImplementationRegistry;
import  org.w3c.dom.Document;
import  org.w3c.dom.ls.DOMImplementationLS;
import  org.w3c.dom.ls.LSSerializer;

...

DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();

DOMImplementationLS impl = 
    (DOMImplementationLS)registry.getDOMImplementation("LS");

...     

LSSerializer writer = impl.createLSSerializer();
String str = writer.writeToString(document);

После того, как я сделал эту работу, я попытался проверить свой XML-файл, было несколько предупреждений. Один о том, что у вас нет доктайпа Поэтому я попробовал другой способ реализовать это. Я наткнулся на класс Трансформер. Этот класс позволяет мне установить кодировку, тип документа и т. Д.

Предыдущая реализация поддерживает автоматическое исправление пространства имен. Следующего нет.

private static Document toDocument(ResultSet rs) throws Exception {   
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(true);
    DocumentBuilder builder = factory.newDocumentBuilder();
    Document doc = builder.newDocument();

    URL namespaceURL = new URL("http://www.w3.org/2001/XMLSchema-instance");
    String namespace = "xmlns:xsi="+namespaceURL.toString();

    Element messages = doc.createElementNS(namespace, "messages");
    doc.appendChild(messages);

    ResultSetMetaData rsmd = rs.getMetaData();
    int colCount = rsmd.getColumnCount();

    String attributeValue = "true";
    String attribute = "xsi:nil";

    rs.beforeFirst();

    while(rs.next()) {
        amountOfRecords = 0;
        Element message = doc.createElement("message");
        messages.appendChild(message);

        for(int i = 1; i <= colCount; i++) {

            Object value = rs.getObject(i);
            String columnName = rsmd.getColumnName(i);

            Element messageNode = doc.createElement(columnName);

            if(value != null) {
                messageNode.appendChild(doc.createTextNode(value.toString()));
            } else {
                messageNode.setAttribute(attribute, attributeValue);
            }
            message.appendChild(messageNode);
        }
        amountOfRecords++;
    }
    logger.info("Amount of records archived: " + amountOfRecords);

    TransformerFactory tff = TransformerFactory.newInstance();
    Transformer tf = tff.newTransformer();
    tf.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
    tf.setOutputProperty(OutputKeys.INDENT, "yes");

    BufferedWriter bf = createFile();
    StreamResult sr = new StreamResult(bf);
    DOMSource source = new DOMSource(doc);
    tf.transform(source, sr);

    return doc;
}

Пока я тестировал предыдущую реализацию, я получил TransformationException: Namespace для префикса 'xsi'; не был объявлен. Как вы можете видеть, я пытался добавить пространство имен с префиксом xsi к корневому элементу моего документа. После тестирования я все еще получил Исключение. Как правильно установить пространства имен и их префиксы?

Изменить: Другая проблема, с которой я столкнулся в первой реализации, заключается в том, что последний элемент в документе XML не имеет последних трех закрывающих тегов.

Ваш Ответ

3   ответа
7

что установка префикса xmlns с помощью setAttribute неверна. Если вы когда-нибудь захотите, например, подписать вашу DOM, вы должны использовать setAttributeNS: element.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:PREFIX", "http://example/namespace");

@JBert Если это все еще актуально (2015): у нас были проблемы с использованием подписи XML из-за кода, использующего setAttribute вместо setAttributeNS. Подпись не может быть подтверждена.
Какой-то конкретный пример, в котором вы заметили разницу? Когда я преобразовываю DOM в файл, использование setAttribute или setAttributeNS дает тот же результат. Заметили ли вы разницу в DOM времени выполнения, которое исправляет преобразование?
Не имеет значения, откуда взято значение. Имеет значение только то, что вы используете setAttributeNS, если вы используете пространства имен.
Это может работать, но тогда у вас есть жестко запрограммированное пространство имен. Если это не ссылка на стандартную схему, мне действительно нравятся эти вещи в файле конфигурации.
Спасибо за ваш ответ. ТакsetAttributesNS похоже, правильно манипулирует DOM в памяти, чтобы подготовить его к подписанию, тогда какsetAttribute дает правильную структуру только после сериализации (может быть, это сериализатор, который исправляет вещи). Затем я смог увидеть, как небольшие различия в DOM в памяти могут дать разные результаты канонизации XML. Верификатор, очевидно, считывает сериализованный XML, поэтому, если подпись была сгенерирована из DOM с «неверным» заявить, что эти различия в канонизации действительно могут привести к созданию недействительной подписи.
6

Вы только что объявили корневой узел в пространстве имен, две совершенно разные вещи. При создании DOM вам необходимо ссылаться на пространство имен на каждом соответствующем узле. Другими словами, когда вы добавляете свой атрибут, вам нужно определить его пространство имен (например, setAttributeNS).

Примечание: хотя пространства имен XML выглядят как URL-адреса, на самом деле они не являются таковыми. Здесь нет необходимости использовать класс URL.

Спасибо, это работает сейчас. Узнал что-то новое сегодня, как и каждый день. TrashCan
30

овать:

rootNode.createElementNS("http://example/namespace", "PREFIX:aNodeName");

Таким образом, вы можете заменить & quot; PREFIX & quot; со своим собственным префиксом и замените & quot; имя_узла & quot; с именем вашего узла. Чтобы каждый узел не имел собственного объявления пространства имен, вы можете определить пространства имен как атрибуты вашего корневого узла следующим образом:

rootNode.setAttribute("xmlns:PREFIX", "http://example/namespace");

Пожалуйста, не забудьте установить:

documentBuilderFactory.setNamespaceAware(true)

В противном случае у вас не будет осведомленности о пространстве имен.

Обратите внимание, что естьsetPrefix(prefix); метод, который позволяет динамически устанавливать префикс.
+1, хотя мне не нужно было звонитьsetNamespaceAware, Документация для этой функции предполагает, что она связана с синтаксическим анализом.

Похожие вопросы