【问题标题】:How to provide JACKSON with the namespace-mapper in the code?如何在代码中为 JACKSON 提供命名空间映射器?
【发布时间】:2012-11-03 22:52:51
【问题描述】:

我正在尝试序列化 JAXB 生成的类。使用 Jettison,我能够创建一个哈希映射,将 XML 命名空间映射到任何 JSON 前缀。使用 Jettison,我还可以在序列化中正确区分大小写。对于 JACKSON,它都是小写的。所以,Jettison 似乎能够更好地理解XMLRootElement(name=…)

如何让 JACKSON 更好地理解 XMLRootElement 等 JAXB 注释?

如何为 JACKSON 提供 XML→JSON 命名空间映射器?

【问题讨论】:

  • 您需要包含您尝试使用的 POJO 类。可能还有您期望的 JSON。

标签: json jaxb jackson


【解决方案1】:

注意:我是EclipseLink JAXB (MOXy) 领导,也是JAXB (JSR-222) 专家组的成员。

EclipseLink MOXy 是符合 JAXB (JSR-222) 的实现。在 EclipseLink 2.4.0 中,我们引入了 JSON 绑定。由于 MOXy 是一个 JAXB 实现,您会发现 MOXy 生成的 JSON 输出将与基于相同元数据的 XML 输出非常一致。下面我用一个例子来演示。


域模型

以下是我将用于此答案的域模型。有关在 JAXB 模型中指定命名空间信息的更多信息,请参阅:http://blog.bdoughan.com/2010/08/jaxb-namespaces.html

包裹信息

@XmlSchema(
        namespace="http://www.example.com/A",
        elementFormDefault=XmlNsForm.QUALIFIED,
        xmlns={
                @XmlNs(prefix="a",namespaceURI = "http://www.example.com/A"),
                @XmlNs(prefix="b",namespaceURI = "http://www.example.com/B")
        }
)
package forum13214306;

import javax.xml.bind.annotation.*;

客户

package forum13214306;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

    String firstName;

    @XmlElement(namespace="http://www.example.com/B")
    String lastName;

}

XML 处理

以下是域模型如何对应 XML 表示的示例。

演示

package forum13214306;

import java.io.File;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Customer.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum13214306/input.xml");
        Customer customer = (Customer) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(customer, System.out);
    }

}

input.xml/Output

<?xml version="1.0" encoding="UTF-8"?>
<a:customer xmlns:b="http://www.example.com/B" xmlns:a="http://www.example.com/A">
   <a:firstName>Jane</a:firstName>
   <b:lastName>Doe</b:lastName>
</a:customer>

JSON 处理 - 没有命名空间

命名空间不是 JSON 概念,因此如果可以避免这种情况,我建议不要模拟它们。下面我将证明 MOXy 不需要它们。请注意,这里使用的域模型和 JAXBContext 与用于带有命名空间的 XML 文档完全相同。

jaxb.properties

要将 MOXy 指定为 JSON 提供程序,您需要在与域模型相同的包中包含一个名为 jaxb.properties 的文件,并使用以下条目(请参阅:http://blog.bdoughan.com/search/label/jaxb.properties)。

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

演示

要启用 JSON 绑定,需要在 MarshallerUnmarshaller 上启用 MEDIA_TYPE 属性。

package forum13214306;

import java.io.File;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.eclipse.persistence.jaxb.UnmarshallerProperties;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Customer.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json");
        File json = new File("src/forum13214306/input.json");
        Customer customer = (Customer) unmarshaller.unmarshal(json);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
        marshaller.marshal(customer, System.out);
    }

}

input.json/Output

以下是运行演示代码的输入和输出。请注意 JSON 文档中没有模拟命名空间信息。

{
   "customer" : {
      "firstName" : "Jane",
      "lastName" : "Doe"
   }
}

JSON 处理 - 使用模拟命名空间

演示

如果您真的想在 JSON 文档中模拟命名空间,可以利用 MarshallerUnmarshaller 上的 NAMESPACE_PREFIX_MAPPER 属性来执行此操作。

package forum13214306;

import java.io.File;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.eclipse.persistence.jaxb.UnmarshallerProperties;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Customer.class);

        Map<String, String> namespaceToPrefixMap = new HashMap<String, String>(2);
        namespaceToPrefixMap.put("http://www.example.com/A", "a");
        namespaceToPrefixMap.put("http://www.example.com/B", "b");

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json");
        unmarshaller.setProperty(UnmarshallerProperties.JSON_NAMESPACE_PREFIX_MAPPER, namespaceToPrefixMap);
        File json = new File("src/forum13214306/input.json");
        Customer customer = (Customer) unmarshaller.unmarshal(json);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
        marshaller.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, namespaceToPrefixMap);
        marshaller.marshal(customer, System.out);
    }

}

input.json/Output

{
   "a.customer" : {
      "a.firstName" : "Jane",
      "b.lastName" : "Doe"
   }
}

更多信息

【讨论】:

  • 非常感谢您提供这么多非常有用的信息。不过,我们必须使用 JACKSON!
  • @BehzadPirvali - StaxMan 是杰克逊的领导者,所以你应该看看他的回答:stackoverflow.com/a/13222220/383861
【解决方案2】:

与 XML 不同,JSON 没有名称空间。那么为什么你觉得你需要命名空间映射呢?数据绑定意味着 Java POJO 和数据格式之间的映射,使用格式的特性。对于 XML,这包括名称空间、元素与属性的选择等。使用 JSON 可以消除大部分复杂性,这意味着按原样使用属性名称。

关于 JAXB 注释:Jackson 有自己的一组更匹配的注释,但如果您确实想使用 JAXB 注释作为附加或替代配置源,则需要使用 JAXB Annotation module。 但是对于使用XMLRootElement,JSON就没有必要了:JSON Objects do not have name。

我不知道您所说的“在序列化中正确区分大小写”是什么意思——在什么意义上?问题是什么?您将需要给出 POJO 定义的示例以及预期的 JSON。

最后,请记住,JSON 和 XML 表示法没有必要看起来彼此相似。这些是具有不同逻辑数据模型的不同数据格式,以及自然不同的映射:例如,JSON 在 Arrays 和 Objects 之间具有原生区别;而 XML 必须同时使用 Elements。 XML 具有 JSON 所缺乏的名称空间和属性。这些导致不同的自然映射,并且尝试将两者“统一”会导致其中一个或另一个或两者都有一些不自然的结果。对于 Jettison(以及使用它的框架),它是丑陋的 JSON(“franken-JSON”)。

【讨论】:

  • 非常感谢您提供的信息丰富的 cmets,除了:1) 如何避免没有命名空间的命名冲突? 2) 作为在 *.xsd 文件上运行 JAXB 的一部分,您会自动获得 JAXB 注释。理想情况下,这些注释也应该推动 JSON 的创建,您不同意吗? 3) 那么,为什么不推荐使用 JaxbAnnotationIntrospector 呢?再次感谢
  • (1) 通过避免名称冲突。你会从哪里得到相互冲突的名字? (即实际问题是什么) (2) XSD 是针对 XML 的,而不是针对 JSON 的,所以一般来说它不是最佳定义。但是,既然这就是您所拥有的,并且您正在使用 JAXB 工具,那么是的(可以将其他 XSD 工具与其他产品一起使用)。 (3) JaxbAnnotationIntrospector 绝对不会被弃用,但您可能正在使用构造函数?希望这会有所帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-08
  • 2013-12-28
  • 1970-01-01
  • 2016-09-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多