使用Jackson将JSON反序列化为ArrayList<POJO>

124

我有一个Java类MyPojo,我希望从JSON反序列化它。我配置了一个特殊的MixIn类MyPojoDeMixIn来帮助我进行反序列化。 MyPojo仅具有intString实例变量,以及正确的getter和setter。 MyPojoDeMixIn看起来像这样:

public abstract class MyPojoDeMixIn {
  MyPojoDeMixIn(
      @JsonProperty("JsonName1") int prop1,
      @JsonProperty("JsonName2") int prop2,
      @JsonProperty("JsonName3") String prop3) {}
}

在我的测试客户端中,我做了以下操作,但编译时出现了与类型不匹配相关的JsonMappingException错误。

ObjectMapper m = new ObjectMapper();
m.getDeserializationConfig().addMixInAnnotations(MyPojo.class,MyPojoDeMixIn.class);
try { ArrayList<MyPojo> arrayOfPojo = m.readValue(response, MyPojo.class); }
catch (Exception e) { System.out.println(e) }
我知道我可以通过创建一个仅包含 ArrayList<MyPojo> 的 "Response" 对象来缓解这个问题,但是这样我就必须为我想要返回的每种类型都创建这些有点无用的对象。
我还在网上查看了JacksonInFiveMinutes,但是对于关于Map<A,B>以及它与我的问题的关系的内容理解起来非常困难。如果你没能感受到,我完全是一个新手Java开发者,我来自Obj-C背景。他们特别提到:
引用:

除了绑定POJO和“简单”类型之外,还有一种变体:绑定到通用(类型化)容器。这种情况需要特殊处理,因为所谓的类型擦除(Java用于以相当向后兼容的方式实现泛型的方法)会阻止您使用诸如Collection.class(不编译)之类的东西。

所以如果您想将数据绑定到Map中,您需要使用:

Map<String,User> result = mapper.readValue(src, new TypeReference<Map<String,User>>() { });

我如何直接反序列化为 ArrayList


请参见 https://dev59.com/zWw05IYBdhLWcg3wcRQj。 - Raedwald
5个回答

175
您可以使用TypeReference包装器直接反序列化为列表。以下是一个示例方法:
public static <T> T fromJSON(final TypeReference<T> type,
      final String jsonPacket) {
   T data = null;

   try {
      data = new ObjectMapper().readValue(jsonPacket, type);
   } catch (Exception e) {
      // Handle the problem
   }
   return data;
}

并且被这样使用:

final String json = "";
Set<POJO> properties = fromJSON(new TypeReference<Set<POJO>>() {}, json);

TypeReference Javadoc


1
嗯,这是相关的。但这是生产环境中工作代码的片段。忘记你的mixin,只需使用我展示的代码(当然用你实际的bean类名替换POJO)。 - Perception
1
目视检查你的数组,确保你至少得到了一个列表。如果需要的话,可以添加回混入(mixin),这应该能与TypeReference一起工作,以便将所有内容整齐地反序列化。 - Perception
3
@JsonProperty并不像人们所说的那样邪恶。由于当前领域中标准化的状态(最小化),很难摆脱特定供应商的注释。 - Perception
有没有使用jackson 2.x api将json对象反序列化为列表的示例? - Andy Dufresne
@DevNG的答案使用Arrays.asList将数组或映射对象转换更好,比这个更好。 - robjwilkins
显示剩余6条评论

126

另一种方法是使用数组作为类型,例如:

ObjectMapper objectMapper = new ObjectMapper();
MyPojo[] pojos = objectMapper.readValue(json, MyPojo[].class);

这样可以避免使用Type对象所带来的所有麻烦,如果您真的需要一个列表,您总是可以通过以下方式将数组转换为列表:

List<MyPojo> pojoList = Arrays.asList(pojos);

我个人认为这样更易读。

如果要将其转换为实际列表(可修改,请参阅Arrays.asList()的限制),只需执行以下操作:

List<MyPojo> mcList = new ArrayList<>(Arrays.asList(pojos));

1
优雅的确很重要,但是由于MyPojo[].class的存在,我无法将其泛型化,而我又不想将其作为参数传递。 - Adrian Redgers
我认为使用TypeFactory,如下一个答案所述:https://dev59.com/tWkw5IYBdhLWcg3wiq-s#42458104 是指定类型的Jackson方式。 - Jmini

54

这个变量看起来更简单、更优雅。

//import com.fasterxml.jackson.core.JsonProcessingException;
//import com.fasterxml.jackson.databind.ObjectMapper;
//import com.fasterxml.jackson.databind.type.CollectionType;
//import com.fasterxml.jackson.databind.type.TypeFactory;

//import java.util.List;


CollectionType typeReference =
    TypeFactory.defaultInstance().constructCollectionType(List.class, Dto.class);
List<Dto> resultDto = objectMapper.readValue(content, typeReference);

1
感谢明确的“CollectionType”参考。 - Erwin Wessels

6

这对我有效。

@Test
public void cloneTest() {
    List<Part> parts = new ArrayList<Part>();
    Part part1 = new Part(1);
    parts.add(part1);
    Part part2 = new Part(2);
    parts.add(part2);
    try {
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonStr = objectMapper.writeValueAsString(parts);

        List<Part> cloneParts = objectMapper.readValue(jsonStr, new TypeReference<ArrayList<Part>>() {});
    } catch (Exception e) {
        //fail("failed.");
        e.printStackTrace();
    }

    //TODO: Assert: compare both list values.
}

4

我也遇到了同样的问题。我有一个需要转换为ArrayList的json。

账户看起来像这样。

Account{
  Person p ;
  Related r ;

}

Person{
    String Name ;
    Address a ;
}

以上所有类都已正确注释。

我尝试使用TypeReference>() {},但它不起作用。

它给了我ArrayList,但ArrayList有一个包含一些最终值的LinkedHashMap的链式哈希映射。

我的代码如下:

public T unmarshal(String responseXML,String c)
{
    ObjectMapper mapper = new ObjectMapper();

    AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();

    mapper.getDeserializationConfig().withAnnotationIntrospector(introspector);

    mapper.getSerializationConfig().withAnnotationIntrospector(introspector);
    try
    {
      this.targetclass = (T) mapper.readValue(responseXML,  new TypeReference<ArrayList<T>>() {});
    }
    catch (JsonParseException e)
    {
      e.printStackTrace();
    }
    catch (JsonMappingException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }

    return this.targetclass;
}

我最终解决了这个问题。我可以将Json字符串中的List直接转换为ArrayList,方法如下:

JsonMarshallerUnmarshaller<T>{

     T targetClass ;

     public ArrayList<T> unmarshal(String jsonString)
     {
        ObjectMapper mapper = new ObjectMapper();

        AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();

        mapper.getDeserializationConfig().withAnnotationIntrospector(introspector);

        mapper.getSerializationConfig().withAnnotationIntrospector(introspector);
        JavaType type = mapper.getTypeFactory().
                    constructCollectionType(ArrayList.class, targetclass.getClass()) ;
        try
        {
        Class c1 = this.targetclass.getClass() ;
        Class c2 = this.targetclass1.getClass() ;
            ArrayList<T> temp = (ArrayList<T>) mapper.readValue(jsonString,  type);
        return temp ;
        }
       catch (JsonParseException e)
       {
        e.printStackTrace();
       }
       catch (JsonMappingException e) {
           e.printStackTrace();
       } catch (IOException e) {
          e.printStackTrace();
       }

     return null ;
    }  

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接