JAXB: как избежать повторного определения пространства имен для xmlns: xsi - PullRequest
20 голосов
/ 12 февраля 2010

У меня есть настройка JAXB, где я использую @XmlJavaTypeAdapter для замены объектов типа Person объектами типа PersonRef, которые содержат только UUID человека. Это прекрасно работает. Однако сгенерированный XML повторно объявляет одно и то же пространство имен (xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance") при каждом его использовании. Хотя это, как правило, нормально, но это не так.

Как я могу настроить JAXB для объявления xmlns: xsi в самом начале документа? Можно ли вручную добавить объявления пространства имен в корневой элемент?

Вот пример того, чего я хочу достичь:

Ток:

<person uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a">
    <relation type="CHILD"> 
        <to xsi:type="personRef" uuid="56a930c0-5499-467f-8263-c2a9f9ecc5a0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> 
    </relation> 
    <relation type="CHILD"> 
        <to xsi:type="personRef" uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> 
    </relation>
    <!-- SNIP: some more relations -->
</person>

Требуются:

<person uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <relation type="CHILD"> 
        <to xsi:type="personRef" uuid="56a930c0-5499-467f-8263-c2a9f9ecc5a0"/> 
    </relation> 
    <relation type="CHILD"> 
        <to xsi:type="personRef" uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a"/> 
    </relation>
    <!-- SNIP: some more relations -->
</person>

Ответы [ 8 ]

17 голосов
/ 01 марта 2010

Не , что красиво, но вы можете добавить пустое расположение схемы к корневому элементу:

marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "");
9 голосов
/ 22 февраля 2010

Похоже на Настройка сопоставления пространства имен JAXB

Когда вы маршалируете XML-документ с использованием JAXB 1.0, объект Marshaller, JAXB-объект, который управляет процессом маршаллинга, предоставляет объявления пространства имен в результирующем XML-документе. Иногда Marshaller создает множество объявлений пространства имен, которые выглядят избыточными, например:

   <?xml version="1.0"?>
   <root>
      <ns1:element xmlns:ns1="urn:foo"> ... </ns1:element>
      <ns2:element xmlns:ns2="urn:foo"> ... </ns2:element>
      <ns3:element xmlns:ns3="urn:foo"> ... </ns3:element>
   </root>

JAXB 2.0 меняет это поведение. Если вы используете JAXB 2.0 (или более позднюю версию) для упорядочивания XML-документа, Marshaller объявляет все статически известные универсальные идентификаторы ресурсов (URI) пространства имен, то есть те URI, которые используются в качестве имен элементов или атрибутов в аннотациях JAXB.

JAXB может также объявлять дополнительные пространства имен в середине документа XML, например, когда квалифицированное имя (QName), которое используется в качестве значения атрибута или элемента, требует нового URI пространства имен или когда объектная модель документа ( DOM) узел в дереве контента требует нового URI пространства имен. Такое поведение может привести к XML-документу, который имеет много объявлений пространства имен с автоматически сгенерированными префиксами пространства имен.

Проблема заключается в том, что автоматически генерируемые префиксы пространства имен, такие как ns1, ns2 и ns3, не удобны для пользователя - обычно они не помогают людям понять маршаллированный XML.

К счастью, JAXB 2.0 (или более поздняя версия) предоставляет интерфейс поставщика услуг (SPI) с именем com.sun.xml.bind.marshaller.NamespacePrefixMapper, который можно использовать для указания более полезных префиксов пространства имен для маршалинга.

Когда программа JAXBSample выполняет маршалинг XML-документа в первый раз, она делает это без использования NamespacePrefixMapper класса. В результате Marshaller автоматически генерирует префикс пространства имен, в данном случае ns2.

   <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
   <ns2:JustAnElement xmlns:ns2="a">
       <foo>true</foo>
   </ns2:JustAnElement>

Пример конфигурации, избегающей повторения пространства имен:

Второй маршаллинг, выполняемый программой JAXBSample, использует класс NamespacePrefixMapper следующим образом:

   NamespacePrefixMapper m = new PreferredMapper();
               marshal(jc, e, m);

   public static class PreferredMapper extends NamespacePrefixMapper {
           @Override
           public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
               return "mappedNamespace" + namespaceUri;
           }
       }

Метод getPreferredPrefix() в классе PreferredMapper возвращает предпочтительный префикс, в данном случае mappedNamespacea, который будет объявлен в корневом элементе маршализованного XML.

   <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
   <mappedNamespacea:JustAnElement xmlns:mappedNamespacea="a">
       <foo>true</foo>
   </mappedNamespacea:JustAnElement>
6 голосов
/ 31 мая 2010

Вы можете сделать это с кодом:

marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapper() {
                @Override
                public String[] getPreDeclaredNamespaceUris() {
                    return new String[] { 
                        XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI
                    };
                }

                @Override
                public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
                    if (namespaceUri.equals(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI))
                        return "xsi";
                    if (namespaceUri.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI))
                        return "xs";
                    if (namespaceUri.equals(WellKnownNamespace.XML_MIME_URI))
                        return "xmime";
                    return suggestion;

                }
            });
3 голосов
/ 08 марта 2011

, если вы используете Maven, просто добавьте это в ваш pom:

<dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.2.2</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>

нет необходимости в PreferredMapper, если вы настраиваете свои аннотации, как определено в примере выше. Хотя у меня есть файл package-info.jave, который выглядит следующим образом:

@javax.xml.bind.annotation.XmlSchema(
        namespace = "mylovelynamespace1", 
        xmlns = {
                    @javax.xml.bind.annotation.XmlNs(prefix = "myns1", namespaceURI = "mylovelynamespace1"),
                    @javax.xml.bind.annotation.XmlNs(prefix = "myns2", namespaceURI = "mylovelynamespace2")
                }, 
                elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.mylovelycompanyname.package;
2 голосов
/ 31 марта 2014

Вы можете позволить пространствам имен записываться только один раз. Вам понадобится прокси-класс XMLStreamWriter и package-info.java. Тогда вы будете делать в своем коде:

StringWriter stringWriter = new StringWriter();
XMLStreamWriter writer = new Wrapper((XMLStreamWriter) XMLOutputFactory
                                                               .newInstance().createXMLStreamWriter(stringWriter));
JAXBContext jaxbContext = JAXBContext.newInstance(Collection.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
jaxbMarshaller.marshal(books, writer);
System.out.println(stringWriter.toString());

Прокси-класс (важный метод - writeNamespace):

            class WrapperXMLStreamWriter implements XMLStreamWriter {

                   private final XMLStreamWriter writer;

                   public WrapperXMLStreamWriter(XMLStreamWriter writer) {
                       this.writer = writer;
                   }

                     //keeps track of what namespaces were used so that not to 
                     //write them more than once
                   private List<String> namespaces = new ArrayList<String>();

                   public void init(){
                       namespaces.clear();
                   }

                   public void writeStartElement(String localName) throws XMLStreamException {
                       init();
                       writer.writeStartElement(localName);

                   }

                   public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
                       init();
                       writer.writeStartElement(namespaceURI, localName);
                   }

                   public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
                       init();
                       writer.writeStartElement(prefix, localName, namespaceURI);
                   }

                   public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException {
                       if(namespaces.contains(namespaceURI)){ 
                           return;
                       }
                       namespaces.add(namespaceURI);
                       writer.writeNamespace(prefix, namespaceURI);
                   }

    // .. other delegation method, always the same pattern: writer.method() ...

}

package-info.java:

@XmlSchema(elementFormDefault=XmlNsForm.QUALIFIED, attributeFormDefault=XmlNsForm.UNQUALIFIED ,
        xmlns = { 
        @XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi")})
package your.package;

import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
2 голосов
/ 04 апреля 2013

Это лучший ответ, который я нахожу в Интернете.

Объявления xsi:type, скорее всего, создаются, поскольку объявленный тип JAXBElement не соответствует типу значения.

Если у ObjectFactory есть метод create для правильного JAXBElement, вы должны использовать его, поскольку он должен правильно заполнять как QName, так и информацию о типе; в противном случае я бы попытался установить объявленный тип (второй аргумент конструктора) для JAXBElement в String.class (при условии, что это тип commentTest) вместо CommentType.Comment.

Источник: http://www.java.net/forum/topic/glassfish/metro-and-jaxb/how-do-i-remove-namespace-declarations-child-elements

Владелец: cbrettin

0 голосов
/ 10 июня 2016

Добавьте ваше nsPrefix отображение, выполнив это:

marshaller.setNamespaceMapping("myns","urn:foo");

0 голосов
/ 22 февраля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...