4

目前,我正在开发一个基于模式的 restFul 项目。因此,我们使用 JAXB 进行 XSD-->JAVA 转换。我有一堂课如下:

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = {
     "systemResponse"
    })
    @XmlRootElement(name = "RestResponse")

public class RestResponse implements Serializable {

     private final static long serialVersionUID = 1L;
     @XmlElementRef(name = "SystemResponse", namespace = "http://www.intuit.com/psd/cdm/v1", type = JAXBElement.class)
   protected JAXBElement<? extends CdmComplexBase> systemResponse;

...
}

以下是它的序列化方式:

{"systemResponse":{"name":"{http://www.intuit.com/psd/cdm/v1}Transactions","declaredType":"com.intuit.psd.cdm.v1.Transactions","scope":"javax.xml.bind.JAXBElement$GlobalScope","value":{"requestId":null,"requestName":null,"isEncrypted":null,"totalCount":null,"pageSize":null,"genDuration":null,"genDateTime":null,"transaction":[{"id":null,"externalKey":[],"metaData":null,"accountNumber":"12345678798","transactionNumber":null,"transactionReference":null,"batchID":null,"batchCycleDate":null,"paymentType":null,"paymentMethod":null,"transactionType":null,"cardType":null,"amount":null,"transactionDate":null,"authCode":null,"customerTransactionID":null,"ccnumberFirstSix":null,"ccnumberLastFour":null,"etctype":null,"posentryType":null}]},"nil":false,"globalScope":true,"typeSubstituted":false}}

尝试反序列化时,出现以下异常:

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class javax.xml.bind.JAXBElement<com.intuit.psd.cdm.v1.CdmComplexBase>]: can not instantiate from JSON object (need to add/enable type information?)
 at [Source: java.io.StringReader@725d9aa7; line: 1, column: 20] (through reference chain: com.intuit.psd.cdm.v1.RestResponse["systemResponse"])
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObjectUsingNonDefault(BeanDeserializer.java:400)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:289)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:375)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:308)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2796)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1942)
    at com.bp.samples.json.HelperJackson.testMarshalUnmarshal(HelperJackson.java:122)
    at com.bp.samples.json.JSONToJavaTest.main(JSONToJavaTest.java:42)

谷歌搜索解决方案,建议您必须向序列化表单添加更多元数据或注册反序列化器。Jettison 正在使用 JAXB 注释来成功序列化和反序列化。另外,我也可以从序列化中删除命名空间“http://www.intuit.com/psd/cdm/v1”。有没有办法对 JACKSON 做同样的事情?

这是我用来配置 JACKSON 的代码:

    ObjectMapper mapper = new ObjectMapper();
    AnnotationIntrospector introspectorPrimary = new JacksonAnnotationIntrospector();
    AnnotationIntrospector introspectorSecondary = new JaxbAnnotationIntrospector();
    AnnotationIntrospector pair = new AnnotationIntrospector.Pair(introspectorPrimary, introspectorSecondary);
    mapper.getSerializationConfig().with(pair);
    mapper.getDeserializationConfig().with(pair);
4

2 回答 2

5

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

Jackson 不是符合 JAXB (JSR-222) 的实现,它仅支持其 JSON 绑定实现中的 JAXB 注释子集。对于 JAXB 生成的模型,您可能对原生支持 JSON 绑定的 EclipseLink JAXB (MOXy) 感兴趣。

JAVA模型

以下是我从您的问题中推断出的部分 Java 模型。

休息响应

package forum13591952;

import java.io.Serializable;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = { "systemResponse" })
@XmlRootElement(name = "RestResponse")
public class RestResponse implements Serializable {

    private final static long serialVersionUID = 1L;

    @XmlElementRef(name = "SystemResponse", namespace = "http://www.intuit.com/psd/cdm/v1", type = JAXBElement.class)
    protected JAXBElement<? extends CdmComplexBase> systemResponse;

}

CdmComplexBase

以下是您的CdmComplexBase课程的简化版本。

package forum13591952;

import javax.xml.bind.annotation.XmlSeeAlso;

@XmlSeeAlso({Transactions.class})
public class CdmComplexBase {

}

交易

以下是您的Transactions课程的简化版本。

package forum13591952;

public class Transactions extends CdmComplexBase {

    private long accountNumber;

    public long getAccountNumber() {
        return accountNumber;
    }

    public void setAccountNumber(long accountNumber) {
        this.accountNumber = accountNumber;
    }

}

对象工厂

以下是您的ObjectFactory课程的简化版本。它指定了@XmlElementDecl与您的使用一起使用的注释@XmlElementRef

package forum13591952;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;

@XmlRegistry
public class ObjectFactory {

    @XmlElementDecl(name="SystemResponse", namespace="http://www.intuit.com/psd/cdm/v1")
    public JAXBElement<CdmComplexBase> createCdmComplexBase(CdmComplexBase value) {
        return new JAXBElement<CdmComplexBase>(new QName("SystemResponse"), CdmComplexBase.class, value);
    }

    @XmlElementDecl(name="Transactions", namespace="http://www.intuit.com/psd/cdm/v1", substitutionHeadName="SystemResponse", substitutionHeadNamespace="http://www.intuit.com/psd/cdm/v1")
    public JAXBElement<Transactions> createTransactions(Transactions value) {
        return new JAXBElement<Transactions>(new QName("Transactions"), Transactions.class, value);
    }

}

jaxb.properties

要将 MOXy 指定为您的 JAXB 提供程序,您需要包含一个jaxb.properties在与域模型相同的包中调用的文件,其中包含以下条目:

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

演示

下面的演示代码会将对象编组为 XML 和 JSON

package forum13591952;

import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.MarshallerProperties;

public class Demo {

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

        ObjectFactory objectFactory = new ObjectFactory();

        RestResponse response = new RestResponse();
        Transactions transactions = new Transactions();
        transactions.setAccountNumber(12345678798L);
        response.systemResponse = objectFactory.createTransactions(transactions);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        // Marshal to XML
        marshaller.marshal(response, System.out);

        // Marshal to JSON
        marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
        marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);
        marshaller.marshal(response, System.out);
    }

}

输出

下面是运行演示代码的输出。请注意 JSON 表示与 XML 表示非常相似。

<?xml version="1.0" encoding="UTF-8"?>
<RestResponse xmlns:ns0="http://www.intuit.com/psd/cdm/v1">
   <ns0:Transactions>
      <accountNumber>12345678798</accountNumber>
   </ns0:Transactions>
</RestResponse>
{
   "Transactions" : {
      "accountNumber" : 12345678798
   }
}

了解更多信息


更新#1

以下是您的第一组后续问题的答案:

1)有没有办法对JSON变量进行camal case:所以AccountNumber->仅用于JSON的accountNumber?

您可以使用 MOXy 的外部映射文档来自定义 JSON 表示。有关完整代码示例,请参阅下面的链接答案。

2) Jettison 有命名空间映射,Moxy 也有吗?

默认情况下,MOXy 不要求您在 JSON 消息中模拟命名空间限定以匹配您的 XML 结构。在我们的 XML 绑定中,我们基于限定名称进行匹配,而在我们的 JSON 绑定中,我们基于相同元数据的本地名称进行匹配。我们还支持 Jettison 风格的命名空间。下面的链接答案包含如何使用 MOXy 完成此操作的完整示例。

3) Moxy 与 Jettison 相比有什么优势,我猜是性能?

Jettison 是一个将 JSON 转换为 StAX 事件/从 StAX 事件转换的库,以便它可以与 XML 绑定库一起使用以生成/使用 JSON(请参阅:http ://blog.bdoughan.com/2011/04/jaxb-and-json -通过-jettison.html)。因此,Jettison 在以下项目中存在 MOXy 没有的问题。

  • Jettison 在将大小为 1 的列表表示为 JSON 数组时存在问题。
  • XML 属性需要一个特殊的指示符
  • 很难指定是否要将字符串“1”表示为 JSON 中的“1”或 1。
  • XML 命名空间需要特殊的表示(参见:http ://blog.bdoughan.com/2011/04/jaxb-and-json-via-jettison-namespace.html )。

更新#2

以下是您第二组后续问题的答案:

1) 你如何解决 Jettison 的列表问题?

Jettison 仅根据收到的事件将 StAX 事件转换为 JSON 或从 JSON 转换。这意味着为了识别一个集合,它需要接收 2 个同名的 startElement 事件,因此大小为 1 的列表不表示为 JSON 数组。由于 MOXy 提供原生 JSON 绑定,它知道数据何时来自列表。

2) 那么,您是从 JAXB 类直接转到 JSON 吗?

是的,MOXy 将 Java 对象(带有 JAXB 和 MOXy 注释)直接转换为 JSON 或从 JSON 转换。

3) 是否有更详细的问题 3 的论文或链接?

我们没有将 MOXy 与 Jettison 进行比较的文件。我们确实检查了人们在使用 Jettison 时遇到的痛点,并确保我们消除了这些痛点。尽管 MOXy 不需要像 Jettison 那样在 JSON 中模拟属性和命名空间等 XML 概念,但我们提供了启用此行为的设置,以使人们更容易从 Jettison 过渡到 MOXy。

在这一点上,我想在做 JAXB+JSON 时为 Intuit 推荐 MOXy。

谢谢您的支持。我在 Stack Overflow 上非常活跃,但如果您将问题发布到EclipseLink 论坛,您将能够获得整个团队的支持。

于 2012-11-28T16:02:38.477 回答
1

JAXBElement 是一种特定于 XML 的数据类型(基本上类似于 DOM 树),Jackson 不知道如何处理它。Jettison 之所以能够处理这个问题,是因为它建立在 XML API 之上,并且只在输出端(或输入的最低部分)转换为 JSON。

但是你为什么在那里使用 JAXBElement 呢?它是一种后备方案,用于无法真正进行数据绑定的情况;有点像将数据映射到 JavaMap左右。你能用真正的 POJO 代替吗?那会很好用。

于 2012-11-28T03:48:51.350 回答