JACKSON对Java Generics的支持?

时间:2012-11-27 19:52:46

标签: json jackson jettison

目前,我正在开发一个基于模式的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)

Google搜索解决方案,建议您必须向序列化表单添加更多元数据或注册反序列化程序。 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);

2 个答案:

答案 0 :(得分:4)

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

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

JAVA模型

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

<强> RestResponse

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

以下是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)是否可以通过camal来表示JSON变量:所以AccountNumber    - &GT; accountNumber仅适用于JSON?

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

  

2)Jettison已经获得了命名空间映射,Moxy也有吗?

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

  

3)我想,Moxy优于Jettison的优势是什么   性能

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


更新#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。

  

此时我想向Intuit推荐MOXy   JAXB + JSON。

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

答案 1 :(得分:1)

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

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