java - 具有不同 namespace 的 JAXB 片段

标签 java jaxb marshalling

我必须整理我的根 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 时,命名空间不一样(在 header 片段中我有:xmlns:ns4="http://www.ebics.org/H003" 但在 ebicsNoPubKeyDigestsRequest 我有 xmlns="http://www.ebics.org/H003")

如果我直接编码 header 对象,而不使用 JAXBElement,则会出现 No @XmlRootElement 错误

我怎样才能拥有相同的命名空间? 注意:我已经使用了 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 "";
  }
});

编辑: 这里是不同的 package-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_;

最佳答案

在没有看到您实际的 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来自任何注释的元素,并查看输出如何变化。

Blaise Doughan (一位 Java XML 绑定(bind)专家,可能潜伏在附近 的某个地方)在他的博客上发布了一些关于此问题的信息,请参阅 his post了解更多信息。


根据 Baptiste 在聊天中提供的意见,我提出了以下解决方案(我认为这是最轻松的)。

正常使用 XJC 编译模式文件 ($ xjc .)。这将生成 package-java.info每个生成的包的文件。我假设此架构不会每天更新,因此您可以安全地对 package-info.java 进行修改文件(即使这些文件中会有一些行注释告诉你不要这样做——无论如何都要这样做)。如果架构得到更新并且您必须重新编译它,请使用 -npa 运行 XJC开关,告诉它不要生成那些 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 包,所以我从包名称列表中省略了它。如果包含它,那么编码的 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")您必须创建一个完全限定的名称,这意味着也指定了 namespace 。仅传递 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实例化解决了你的问题,但也许它会。但是我认为它不会,您必须手动管理您的前缀才能获得漂亮一致的结果。

关于java - 具有不同 namespace 的 JAXB 片段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8560383/

相关文章:

java - 如何将 OpenSSL 的模数结果转换为 Java BigInteger?

java - 基于 DFA 的 Java 正则表达式引擎与捕获

java - 如何使用Java遍历类及其成员类的对象

c# - 从 C++ 中的 char* 获取 C# 中的 byte[]

c# - 如何在 C# 中固定指向托管对象的指针?

java - struts 2中将java bean转换为json数据

java - Maven - 部署大型 war 文件

java - 无法创建具有特定类的 JAXB 实例

jaxb - 编码时可选的 JAXB xml 属性

json - 在 Go 中使用匿名成员展平编码的 JSON 结构