9

我正在使用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(); 了 Now,因为我有几个 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?)

我试图阅读一些有关类型信息的信息- 但无法理解它如何在这里帮助我。

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

谢谢。

4

6 回答 6

6

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

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 )的情况下做到这一点 - 现在已经足够了 - 但如果有人有想法 - 我想在这里!

谢谢。

于 2012-08-27T12:25:09.367 回答
4

问题与此处描述的相同:杰克逊错误:没有合适的构造函数

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

于 2012-08-27T13:40:18.810 回答
1

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

"price" : "10.00"

"price" : 10.00

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

于 2012-08-27T09:38:00.103 回答
1

如果您想将两个 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 版本中(我可能会在今天发布新版本,因为我是作者 :))要使其正常工作,您必须注释您的 getter(如果您也添加 setter使用@JsonProperty(the_name_from_json) 进行序列化。请注意,如果您希望 Genson 只能使用字段,则 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;
    }
}
于 2012-08-27T12:01:03.600 回答
1

注意: 我是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);
    }

}

输入.xml/输出

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

{
   "Person" : {
      "id" : "2"
   },
   "Dog" : {
      "dateOfBirth" : "2012-08-20T00:00:00.0",
      "price" : "10.00"
   }
}
于 2012-08-27T13:25:02.163 回答
1

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

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

node.get(0).get(1).get("nodename").asText();
于 2014-01-01T01:40:50.467 回答