【问题标题】:How to convert json into POJO in java using jackson如何使用jackson在java中将json转换为POJO
【发布时间】:2012-08-21 18:54:06
【问题描述】:

我使用的是spring 3.1.2,我需要将一个json对象解析成POJO。 这是我需要解析的json:

{
"Person" : {
    "id" : "2"
 },
"Dog" : {
    "dateOfBirth" : "2012-08-20 00:00:00",
    "price" : "10.00"
    }
}

我需要将这个json对象(由两个对象组合而成)转换成一个POJO,这里是:

public class MyClass{
     public MyClass(){}
     public MyClass(String personsId, TimeStamp dogsDateOfBirth, BigDecimal dogsPrice){
     .... // assign each parameter to the appropriate field
     }
     private String personsId;
     private TimeStamp dogsDateOfBirth;
     private BigDecimal dogsPrice;
     //... Getters and Setters for each field
}

为此,我使用了ObjectMapper mapper = new ObjectMapper(); 现在,由于我有几个 json 对象,我的代码如下所示:

    String json = ... ;// A json with several objects as above
    JsonNode tree = mapper.readTree(json);
    Iterator<JsonNode> iter = tree.path("data").getElements();
    while (iter.hasNext()){
        JsonNode node = iter.next();
        MyClass myClass = mapper.readValue(node, MyClass.class);
        ... // do something with myClass object
    }

当我运行它时 - 我得到以下异常:

org.codehaus.jackson.map.JsonMappingException: No suitable constructor found for type [simple type, class ...MyClass]: can not instantiate from JSON object (need to add/enable type information?)

我尝试创建一个简单的 POJO - Person:

public class Person{
        private String id;          
        public Person(){}
        public Person(String id){
            this.id = id;
         }
         ... // Getter and Setter
    }

并执行以下操作:

Person person = mapper.readValue(node.path("Person"), Person.class);

我得到了这个(相同的)异常:

org.codehaus.jackson.map.JsonMappingException: No suitable constructor found for type [simple type, class ...Person]: can not instantiate from JSON object (need to add/enable type information?)

我尝试阅读一些关于 type information 的信息 - 但不明白它对我有何帮助。

如何将此 json 转换为我的 POJO?

谢谢。

【问题讨论】:

  • 你有没有试过移除你的特殊构造函数而只使用无参数?看起来它以某种方式对您的构造函数感到困惑。
  • 是的,我试过了 - 但我仍然遇到同样的异常。
  • 我试图重现这个问题,但我不能。我使用的是fastxml中的jackson,而不是codehaus。我直接从字符串中读取 json,而不是从节点中读取。以人为例,这很有效。对于另一个它没有,因为你在那里有这种奇怪的物体组合。您可能必须使用中间表示。
  • 嗨 - 我刚刚尝试从字符串 ({"id":"2"}) 进行映射,但它仍然不起作用 - 同样的异常。
  • 以下对我来说很好用:MyClass myClass = new ObjectMapper().readValue("{\"id\" : \"2\" }".getBytes(), MyClass.class) 和 MyClass 一个简单的 bean,有一个字符串字段:id。

标签: java json spring jackson pojo


【解决方案1】:

我所做的是: 我创建了一个包含 Person 对象和 Dog 对象的新类,这些类需要是静态的(我发现它 here )。 以下是课程:

public static class MyNewClass{
    private Person person;
    private Dog dog;
    ... // two constructors and getters and setters

 public static class Person{
     private String id;
     ... // two constructors and getters and setters
 }
 public static class Dog{
     private String dateOfBirth;
     private String price;
     ... // two constructors and getters and setters
  }
}

现在我的代码如下所示:

    JsonNode tree = mapper.readTree(jsonString);
    Iterator<JsonNode> iter = tree.path("data").getElements();
    while (iter.hasNext()){
        JsonNode node = iter.next();
        Person person = mapper.readValue(node.path("Person"), Person.class);
        Dog dog = mapper.readValue(node.path("Dog"), Dog.class);
        MyNewClass myNewClass = new MyNewClass(person , dog);
        ... //Do something with it
    }

我仍然想在不创建这两个对象( Person 和 Dog )的情况下这样做 - 现在已经足够了 - 但如果有人有想法 - 我想在这里!

谢谢。

【讨论】:

  • 你应该声明你的类在内部类:) 这会自动为你的构造函数添加一个引用父类的值。这就是它找不到构造函数的原因。
【解决方案2】:

问题与此处描述的相同:Jackson error: no suitable constructor

您尝试实例化的类不是静态的。因此,它有一个隐藏的构造函数参数。这导致杰克逊失败了。

【讨论】:

    【解决方案3】:

    尝试更改 MyClass 的构造函数以接受所有字段的String,因为所有字段都是 JSON 数据中的字符串。顺便说一句,JSON 中的时间戳没有标准表示,因此无论如何您都需要对日期字段进行转换。对于“价格”字段,您可以尝试更改

    "price" : "10.00"
    

    "price" : 10.00
    

    在 JSON 数据中;这应该允许它被读取为 BigDecimal。

    【讨论】:

    • 谢谢,我确实错过了 - 但它仍然无法工作 - 看看当我只要求 Person 对象时发生了什么 - 这个类只有一个字段是字符串。
    【解决方案4】:

    如果你想将你的两个 json 对象组合成一个 java 对象,这里是 Genson 库http://code.google.com/p/genson/ 的解决方案。 以下代码可以缩短并使用 Gensons 标准转换器,但作为示例不太清楚。这里的优点是您直接使用流式 API,因此速度非常快。

    class MyClassConverter implements Deserializer<MyClass> {
        @Override
        public MyClass deserialize(ObjectReader reader, Context ctx)
                throws TransformationException, IOException {
            reader.beginObject();
            MyClass myClass = new MyClass();
            for (; reader.hasNext();) {
                reader.next();
                if ("Person".equals(reader.name())) {
                    readPerson(reader, myClass);
                } else if ("Dog".equals(reader.name())) {
                    readDog(reader, myClass);
                }
            }
            reader.endObject();
            return myClass;
        }
    
        private void readPerson(ObjectReader reader, MyClass myClass) throws IOException {
            reader.beginObject();
            for (; reader.hasNext();) {
                reader.next();
                if ("id".equals(reader.name()))
                    myClass.setPersonsId(reader.valueAsString());
            }
            reader.endObject();
        }
    
        private void readDog(ObjectReader reader, MyClass myClass) throws IOException {
            reader.beginObject();
            for (; reader.hasNext();) {
                reader.next();
                if ("dateOfBirth".equals(reader.name()))
                    myClass.setDogsDateOfBirth(Timestamp.valueOf(reader.valueAsString()));
                else if ("price".equals(reader.name()))
                    myClass.setDogsPrice(new BigDecimal(reader.valueAsString()));
            }
            reader.endObject();
        }
    }
    

    如果您想要其他将 Person 和 Dog 作为分离对象的示例,您可以在 Gensons 用户组上询问。

    希望这会有所帮助!

    编辑 这是另一个更短更好的版本,但未包含在已发布的 0.91 版本中(我可能会在今天发布新版本,因为我是作者 :)) 要使其工作,您必须使用 @JsonProperty(the_name_from_json) 注释您的 getter(如果您还进行序列化,则为 setter)。请注意,Genson 不需要任何 getter/setter,如果您希望它只能使用字段(默认情况下,如果可用,则使用 getter/setter,否则字段)。

    Genson genson = new Genson.Builder().withDeserializerFactory(new MyClassConverterFactory()).create();
    MyClass myClass = genson.deserialize(json, MyClass.class);
    
    public static class MyClassConverterFactory implements Factory<Deserializer<MyClass>> {
        @SuppressWarnings("unchecked")
        @Override
        public Deserializer<MyClass> create(Type type, Genson genson) {
            BeanDescriptor<MyClass> myClassDescriptor = (BeanDescriptor<MyClass>) genson.getBeanDescriptorFactory().provide(MyClass.class, genson);
            return new MyClassConverter(myClassDescriptor);
        }
    }
    
    public static class MyClassConverter implements Deserializer<MyClass> {
        BeanDescriptor<MyClass> myClassDescriptor;
        public MyClassConverter(BeanDescriptor<MyClass> myClassDescriptor) {
            this.myClassDescriptor = myClassDescriptor;
        }
    
        @Override
        public MyClass deserialize(ObjectReader reader, Context ctx)
                throws TransformationException, IOException {
            reader.beginObject();
            MyClass myClass = new MyClass();
            for (; reader.hasNext();) {
                reader.next();
                if ("Person".equals(reader.name())) {
                    myClassDescriptor.deserialize(myClass, reader, ctx);
                } else if ("Dog".equals(reader.name())) {
                    myClassDescriptor.deserialize(myClass, reader, ctx);
                }
            }
            reader.endObject();
            return myClass;
        }
    }
    

    【讨论】:

      【解决方案5】:

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

      您可以利用 MOXy 中基于路径的映射来支持您的用例。

      我的班级

      @XmlPath 注解用于指定基于路径的映射:

      package forum12139380;
      
      import java.math.BigDecimal;
      import java.sql.Timestamp;
      import org.eclipse.persistence.oxm.annotations.XmlPath;
      import javax.xml.bind.annotation.*;
      
      @XmlAccessorType(XmlAccessType.FIELD)
      public class MyClass {
      
          public MyClass() {
          }
      
          public MyClass(String personsId, Timestamp dogsDateOfBirth,
                  BigDecimal dogsPrice) {
              this.personsId = personsId;
              this.dogsDateOfBirth = dogsDateOfBirth;
              this.dogsPrice = dogsPrice;
          }
      
          @XmlPath("Person/id/text()")
          private String personsId;
      
          @XmlPath("Dog/dateOfBirth/text()")
          private Timestamp dogsDateOfBirth;
      
          @XmlPath("Dog/price/text()")
          @XmlSchemaType(name="string")
          private BigDecimal dogsPrice;
      
          // ... Getters and Setters for each field
      
      }
      

      jaxb.properties

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

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

      演示

      下面的代码会将 JSON 转换为对象,然后再转换回 JSON。

      package forum12139380;
      
      import java.util.*;
      import javax.xml.bind.*;
      import javax.xml.transform.stream.StreamSource;
      import org.eclipse.persistence.jaxb.JAXBContextProperties;
      
      public class Demo {
      
          public static void main(String[] args) throws Exception {
              Map<String, Object> properties = new HashMap<String, Object>(2);
              properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
              properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
              JAXBContext jc = JAXBContext.newInstance(new Class[] {MyClass.class}, properties);
      
              Unmarshaller unmarshaller = jc.createUnmarshaller();
              StreamSource json = new StreamSource("src/forum12139380/input.json");
              MyClass myClass = (MyClass) unmarshaller.unmarshal(json, MyClass.class).getValue();
      
              Marshaller marshaller = jc.createMarshaller();
              marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
              marshaller.marshal(myClass, System.out);
          }
      
      }
      

      input.xml/Output

      下面是运行演示代码的输入和输出。在我的示例中,我使用 MOXy 的默认代表 Timestamp。您可以使用XmlAdapter 轻松控制此表示(请参阅:jaxb unmarshal timestamp)。

      {
         "Person" : {
            "id" : "2"
         },
         "Dog" : {
            "dateOfBirth" : "2012-08-20T00:00:00.0",
            "price" : "10.00"
         }
      }
      

      【讨论】:

        【解决方案6】:

        所有这些答案都意味着使用 POJO 是唯一的方法。显然,在 Web 项目中,例如 Spring Web 服务项目,拥有 POJO 很重要,但仅用于单元测试,您不能只使用通用的 Jackson JsonNode 对象吗?

        你不能简单地使用 Jackson ObjectMapper 反序列化成 JsonNode 而不使用 POJO 类吗? JsonNode 的实例本身不是合法的杰克逊 POJO 吗?我确信它确实如此,因为一旦你有一个 JsonNode 实例,你就可以在 json 中获取对象,例如:

        node.get(0).get(1).get("nodename").asText();
        

        【讨论】:

          猜你喜欢
          • 2020-09-07
          • 1970-01-01
          • 1970-01-01
          • 2020-12-10
          • 1970-01-01
          • 1970-01-01
          • 2014-01-31
          • 1970-01-01
          • 2022-07-24
          相关资源
          最近更新 更多