JAXB фрагмент с разными пространствами имен - PullRequest
3 голосов
/ 19 декабря 2011

Я должен маршалировать фрагмент моего корневого XML-объекта:

Header header = ebicsNoPubKeyDigestsRequest.getHeader();
JAXBElement<org.ebics.h003.EbicsNoPubKeyDigestsRequest.Header> jaxbElement =
  new JAXBElement<EbicsNoPubKeyDigestsRequest.Header>(
    new QName("header"), EbicsNoPubKeyDigestsRequest.Header.class, header);
byte[] headerXml = JAXBHelper.marshall(jaxbElement, true);

, но когда я маршал ebicsNoPubKeyDigestsRequest пространства имен не совпадают (во фрагменте заголовка у меня есть: xmlns:ns4="http://www.ebics.org/H003", но в ebicsNoPubKeyDigestsRequest У меня есть xmlns="http://www.ebics.org/H003")

Если я перенаправлю объект заголовка напрямую, без использования JAXBElement, у меня будет No @XmlRootElement error

Как я могу иметь те же пространства имен?Примечание: я уже использую класс NamespacePrefixMapper:

marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapper() {

  @Override
  public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
    if (namespaceUri.equals("http://www.ebics.org/H003")) {
      return "";
    } else if (namespaceUri.equals("http://www.w3.org/2000/09/xmldsig#")) {
      return "ds";
    } else if (namespaceUri.equals("http://www.ebics.org/S001")) {
      return "ns1";
    } else if (namespaceUri.equals("http://www.w3.org/2001/XMLSchema-instance")) {
      return "ns2";
    }
    return "";
  }
});

РЕДАКТИРОВАТЬ: здесь другой пакет-info.java:

@javax.xml.bind.annotation.XmlSchema(namespace = "http://www.ebics.org/H003", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package org.ebics.h003;

@javax.xml.bind.annotation.XmlSchema(namespace = "http://www.ebics.org/S001", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package org.ebics.s001;

@javax.xml.bind.annotation.XmlSchema(namespace = "http://www.w3.org/2000/09/xmldsig#", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package org.w3._2000._09.xmldsig_;

1 Ответ

4 голосов
/ 19 декабря 2011

Не видя вашей фактической схемы XML, файлов и сгенерированных JAXB классов (и их аннотаций), я могу только посоветовать вам попробовать адаптировать следующий пример к вашему сценарию.

Учитывая, что у вас есть сгенерированный JAXB класснапример:

@XmlRootElement(namespace = "http://test.com")
@XmlType(namespace = "http://test.com")
public static final class Test {

  public String data;

  public Test() {}
}

, который находится в пакете test и в нем есть файл package-info.java, например:

@XmlSchema(elementFormDefault = XmlNsForm.QUALIFIED,
           xmlns = @XmlNs(prefix = "", namespaceURI = "http://test.com"))
package test;

import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;

следующий код:

Test test = new Test();
test.data = "Hello, World!";

JAXBContext context = JAXBContext.newInstance(Test.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(test, System.out);

напечатает это:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<test xmlns="http://test.com">
    <data>Hello, World!</data>
</test>

Вероятно, вы, вероятно, можете полностью пропустить реализацию NamespacePrefixMapper.

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

Блейз Дафан (эксперт по связыванию Java XML, вероятно, скрывающийся где-то возле ) опубликовал некоторую информацию по этой проблеме на своемблог, см. его пост для получения дополнительной информации.


На основе информации, предоставленной Баптистом в чате, я предлагаю следующее решение (я думаю, это наиболее безболезненно).

Скомпилируйте ваши файлы схемы с помощью XJC в обычном режиме ($ xjc .).Это создаст package-java.info файлов для каждого из сгенерированных пакетов.Я предполагаю, что эта схема не обновляется каждый день, поэтому вы можете вносить изменения в файлы package-info.java (даже если в этих файлах будут некоторые строчные комментарии, говорящие о том, что вам не следует это делать - в любом случае, делайте это).Если схема обновляется, и вам необходимо перекомпилировать ее, запустите XJC с ключом -npa, который говорит, что он не должен автоматически генерировать эти package-info.java файлы, поэтому (в идеале) вы не можете перезаписать файлы, созданные вручную (если вы используете контроль версий, вы можете / должны включить эти файлы (ручной работы) в репозиторий).

На основе предоставленных файлов схемы создаются четыре пакета, поэтому я включу мою версию измененного package-info.javaфайлы.

@XmlSchema(namespace = "http://www.ebics.org/H000",
           xmlns = @XmlNs(prefix = "ns1", 
                          namespaceURI = "http://www.ebics.org/H000"))
package org.ebics.h000;

@XmlSchema(namespace = "http://www.ebics.org/H003",
           xmlns = @XmlNs(prefix = "", 
                          namespaceURI = "http://www.ebics.org/H003"))
package org.ebics.h003;

@XmlSchema(namespace = "http://www.ebics.org/S001",
           xmlns = @XmlNs(prefix = "ns3", 
                          namespaceURI = "http://www.ebics.org/S001"))
package org.ebics.s001;

@XmlSchema(namespace = "http://www.w3.org/2000/09/xmldsig#",
           xmlns = @XmlNs(prefix = "ns2", 
                          namespaceURI = "http://www.w3.org/2000/09/xmldsig#"))
package org.w3._2000._09.xmldsig;

После этого вы создаете свой JAXBContext следующим образом:

JAXBContext context =
  JAXBContext.newInstance("org.ebics.h003:org.ebics.s001:org.w3._2000._09.xmldsig");

(я заметил, что вы на самом деле не используете h000 package, поэтому я исключил его из списка имен пакетов. Если он включен, то корневой маршалированный XML-тег, вероятно, будет содержать его пространство имен и префикс, даже если он не используется.)

После этого вы отменяете входной XML-код и делаете все, что хотите, с объектом в памяти.

Unmarshaller unmarshaller = context.createUnmarshaller();

EbicsNoPubKeyDigestsRequest ebicsNoPubKeyDigestsRequest =
    (EbicsNoPubKeyDigestsRequest) unmarshaller.unmarshal(stream);

Теперь, если вы хотите маршалировать только тег header, который вложен в ebicsNoPubKeyDigestsRequest у тебя естье, чтобы обернуть его внутри JAXBElement<...>, потому что тип header EbicsNoPubKeyDigestsRequest.Header не аннотирован @XmlRootElement аннотацией.У вас есть два (в этом случае одном ) способа создания этого элемента.

Создайте экземпляр ObjectFactory для соответствующего пакета и используйте его функцию JAXBElement<T> createT(T t),Который оборачивает свой ввод в JAXBElement<...>.К сожалению, однако, для типа поля header (с учетом ваших файлов схемы) XJC не генерирует такой метод, поэтому вы должны сделать это вручную.

В принципе вы почти все сделали правильно, но при созданииJAXBElement<...> вместо его передачи new QName("header") вам нужно создать полностью определенное имя, что означает, что пространство имен также указано.Передача только имени тега XML недостаточна, поскольку JAXB не будет знать, что этот конкретный тег header является частью пространства имен "http://www.ebics.org/H003".Сделайте это следующим образом:

QName qualifiedName = new QName("http://www.ebics.org/H003", "header");
JAXBElement<EbicsNoPubKeyDigestsRequest.Header> header =
    new JAXBElement<EbicsNoPubKeyDigestsRequest.Header>(
        qualifiedName, EbicsNoPubKeyDigestsRequest.Header.class, header);

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

...