web-dev-qa-db-ja.com

Javaでjacksonを使用してjsonをPOJOに変換する方法

Spring 3.1.2を使用しており、jsonオブジェクトをPOJOに解析する必要があります。これは私が解析する必要があるjsonです:

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

このjsonオブジェクト(2つのオブジェクトから結合されたもの)を1つの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?)
_

タイプ情報 -について読んでみましたが、ここでどのように役立つのか理解できませんでした。

このjsonをPOJOに変換するにはどうすればよいですか?

ありがとう。

9
Noam

私がしたことはこれでした: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
    }

私はまだこれらの2つのオブジェクト(PersonとDog)を作成せずにそれをやりたいです-それは今のところ十分です-しかし誰かがアイデアを持っているなら-私はここに行きたいです!

ありがとう。

6
Noam

問題はここで説明したものと同じです: ジャクソンエラー:適切なコンストラクターがありません

インスタンス化しようとしているクラスは静的ではありません。そのため、コンストラクターパラメーターが非表示になっています。これがジャクソンの失敗の原因です。

4
Joeri Hendrickx

2つのjsonオブジェクトを1つに結合したい場合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ユーザーグループで質問できます。

お役に立てれば!

[〜#〜] edit [〜#〜]これは、より短くてより良い別のバージョンですが、リリースされたバージョン0.91には含まれていません(おそらくリリースします私が作者である今日の新しいバージョン:))それを機能させるには、@ JsonProperty(the_name_from_json)でゲッター(およびシリアル化も行う場合はセッター)に注釈を付ける必要があります。 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;
    }
}
1
eugen

注:私は EclipseLink JAXB(MOXy) リードおよび JAXB(JSR-222) エキスパートグループのメンバー。

MOXyのパスベースのマッピングを活用して、ユースケースをサポートできます。

MyClass

@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タイムスタンプ )。

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

これらすべての回答は、POJOを使用することがそれを行う唯一の方法であることを意味します。明らかに、Spring WebサービスプロジェクトのようなWebプロジェクトでは、POJOを使用することが重要ですが、単体テストのためだけに、汎用のJackson JsonNodeオブジェクトを使用することはできませんか?

POJOクラスを使用せずに、Jackson ObjectMapperを使用してJsonNodeに単純に逆シリアル化することはできませんか? JsonNodeのインスタンスは、それ自体が合法的なJackson POJOとして適格ではありませんか? JsonNodeインスタンスを取得すると、次のようにjson内のオブジェクトを取得できるためです。

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

すべてのフィールドはJSONデータの文字列であるため、MyClassのコンストラクターを変更して、すべてのフィールドでStringを受け入れるようにしてください。ところで、JSONにはTimeStampsの標準表現がないため、とにかく日付フィールドの変換を行う必要があります。 「価格」フィールドについては、変更してみてください

"price" : "10.00"

"price" : 10.00

jSONデータ内。これにより、BigDecimalとして読み取ることができます。

1