Как вставить схему размещения в документе XML через DOM - PullRequest
6 голосов
/ 02 февраля 2011

Я создаю XML-документ с JAXP и ищу способ вставить схему размещения На данный момент мое приложение выдает:

<?xml version="1.0" encoding="UTF-8"?>
<root>
...
</root>

Но мне нужно:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="namespaceURL" 
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="namespaceURL pathToMySchema.xsd">
...
</root>

Мой код:

StreamResult result = new StreamResult(writer);
Document doc = getDocument();

Transformer trans = transfac.newTransformer();
trans.setOutputProperty(OutputKeys.INDENT, "yes");
trans.setOutputProperty(OutputKeys.METHOD, "xml");
trans.setOutputProperty(OutputKeys.VERSION, "1.0");
trans.setOutputProperty(OutputKeys.ENCODING, "UTF-8");

DOMSource source = new DOMSource(depl.getAsElement(doc));
trans.transform(source, result);

Спасибо за ваше время,
Kasten

Ответы [ 3 ]

6 голосов
/ 10 февраля 2011

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

<root xmlns="namespaceURL">
    <child xmlns=""/>
    ...
</root>

Обратите внимание на появление пустого пространства имен по умолчанию xmlns="" на дочерних элементах.Что на самом деле нужно сделать, так это изменить пространство имен каждого узла или создать новый документ с требуемым пространством имен по умолчанию и скопировать содержимое, имена элементов и атрибутов и т. Д. Старого документа в новый.Это можно сделать, рекурсивно просматривая исходный документ.Я слышал, что реализация Java DOM может быть трудоемкой.Одним из коротких путей может быть чтение документа с помощью DOM, не поддерживающего пространство имен, а затем добавление в качестве атрибута нового пространства имен по умолчанию.Другое решение состоит в том, чтобы изменить пространство имен с помощью преобразования XSLT, что кажется вполне подходящим в этом случае, поскольку вы фактически уже генерируете выходные данные с помощью преобразования XSLT.

Используйте эту таблицу стилей XSLT, чтобы добавить новое пространство имен по умолчанию ирасположение схемы к корневому элементу.Эта таблица стилей сохраняет старые пространства имен, но добавляет все элементы в новое пространство имен по умолчанию, если они ранее находились в пространстве без имен.

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

    <!-- Template to add a default namespace to a document -->
    <!-- Elements without a namespace are "moved" to default namespace -->
    <!-- Elements with a namespace are copied as such -->

  <!-- string for default namespace uri and schema location -->
  <xsl:variable name="ns" select="'namespaceURL'"/>
  <xsl:variable name="schemaLoc" select="'namespaceURL pathToMySchema.xsd'"/>

    <!-- template for root element -->
    <!-- adds default namespace and schema location -->
  <xsl:template match="/*" priority="1">
    <xsl:element name="{local-name()}" namespace="{$ns}">
      <xsl:attribute name="xsi:schemaLocation"
        namespace="http://www.w3.org/2001/XMLSchema-instance">
        <xsl:value-of select="$schemaLoc"/>
        </xsl:attribute>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:element>
  </xsl:template>

    <!--template for elements without a namespace -->
  <xsl:template match="*[namespace-uri() = '']">
    <xsl:element name="{local-name()}" namespace="{$ns}">
      <xsl:apply-templates select="@* | node()"/>
    </xsl:element>
  </xsl:template>

    <!--template for elements with a namespace -->
  <xsl:template match="*[not(namespace-uri() = '')]">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

    <!--template to copy attributes, text, PIs and comments -->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Вместо создания преобразователя с

Transformer trans = transfac.newTransformer();

(который создает итаблица стилей, которая выполняет преобразование тождества), создайте источник ввода XSLT и передайте его в качестве параметра newTransformer()

javax.xml.transform.Source xsltSource = new javax.xml.transform.stream.StreamSource(xsltFile);
Transformer trans = transFact.newTransformer(xsltSource);

, где xsltFile - это объект File, указывающий на этот файл XSLT.

Установите выходные свойства по своему усмотрению и вызовите transform(), как в вашем примере кода.Результат должен быть тем, что вы хотели, но я не проверял это в Java .Данный XSLT-файл протестирован для некоторых тривиальных случаев, и в конце этого ответа приведен пример ввода и вывода.

Некоторые незначительные замечания:

  1. Исходный объект документа не являетсяизменены в этом процессе.Новое пространство имен по умолчанию появляется только в выходных данных метода transform().
  2. Префикс пространства имен для пространства имен экземпляра схемы обычно равен xsi:, а не xs:, как в примере кода (xs: isиспользуется в определениях схемы (а также xsd:)).

Пример ввода и вывода для таблицы стилей XSLT, показанной выше

Ввод:

<root>
    <child>text</child>
    <child attribute="attr-value"/>
    <?pi-target pi-content?>
    <nsx:ns-child xmlns:nsx="ns1x">
        <no-ns-child>text</no-ns-child>
        <!-- comment -->
        <nsx:ns-child nsx:ns-attribute="nsx-attr-value">text</nsx:ns-child>
    </nsx:ns-child>
    <defns-child xmlns="default-ns">
        <def-child attr="val">text</def-child>
        <child xmlns=""/>
    </defns-child>
    <child>text</child>
</root>

Вывод:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="namespaceURL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="namespaceURL pathToMySchema.xsd">
    <child>text</child>
    <child attribute="attr-value"/>
    <?pi-target pi-content?>
    <nsx:ns-child xmlns:nsx="ns1x">
        <no-ns-child>text</no-ns-child>
        <!-- comment -->
        <nsx:ns-child nsx:ns-attribute="nsx-attr-value">text</nsx:ns-child>
    </nsx:ns-child>
    <defns-child xmlns="default-ns">
        <def-child attr="val">text</def-child>
        <child xmlns="namespaceURL"/>
    </defns-child>
    <child>text</child>
</root>
4 голосов
/ 31 июля 2012

Вы можете добавить пространства имен в корне при создании документа.

String NS_URL = "namespaceURL";

doc = builder.newDocument();
Element root = doc.createElementNS(NS_URL, "root");
root.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance", 
    "xs:schemaLocation", NS_URL + " pathToMySchema.xsd");
doc.appendChild(root);

Затем для каждого элемента, добавленного в документ, вместо createElement () используйте createElementNS ()

doc.createElementNS(NS_URL, name);

Это приводит к тому, что вы искали.

<root 
    xmlns="namespaceURL"
    xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" 
    xs:schemaLocation="namespaceURL pathToMySchema.xsd"
    >
1 голос
/ 08 февраля 2011

Вот как дать подсказку парсеру для решения вашей проблемы: http://bytes.com/topic/java/answers/16892-xerces-how-perfrom-schema-validations-without-using-xsi-schemalocation

Это выглядит так:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setValidating(true);
dbf.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
"http://www.w3.org/2001/XMLSchema");
dbf.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLocation",
"http://www.example.com/Report.xsd");

Вот пример проверки с некоторым источникомкод.Это может помочь вам.http://www.ibm.com/developerworks/xml/library/x-tipvalschm/

(Если все ухудшается, вы всегда можете искать и заменять. Я знаю, что это не идеальное решение, но javax.xml.transform.OutputKeys, похоже, не имеетчлен, связанный с атрибутом схемы.)

...