ジャクソンの例外 - 問題と解決策

ジャクソンの例外–問題と解決策

1. 概要

このチュートリアルでは、the most common Jackson ExceptionsJsonMappingExceptionUnrecognizedPropertyExceptionについて説明します。

最後に、そのようなメソッドエラーがないジャクソンについて簡単に説明します。

参考文献:

ジャクソン–カスタムシリアライザー

カスタムシリアライザーを使用して、Jackson 2でJSON出力を制御します。

ジャクソン注釈の例

ジャクソンの中核は基本的に一連の注釈です。これらをよく理解してください。

Jacksonでのカスタムデシリアライゼーションの開始

Jacksonを使用して、デシリアライズプロセスを完全に制御して、カスタムJSONをJavaエンティティグラフにマッピングします。

2. 「JsonMappingException:インスタンスを構築できません」

2.1. 問題

まず、Jsttepingexception:のインスタンスを構築できませんを見てみましょう。

この例外は、Jackson can’t create an instance of the classの場合にスローされます。これは、クラスがabstractの場合、または単にinterfaceの場合に発生します。

次の例では、abstractタイプAnimalのプロパティanimalを持つクラスZooからインスタンスを逆シリアル化しようとします。

public class Zoo {
    public Animal animal;

    public Zoo() { }
}

abstract class Animal {
    public String name;

    public Animal() { }
}

class Cat extends Animal {
    public int lives;

    public Cat() { }
}

JSONStringをZooインスタンスに逆シリアル化しようとすると、次の例のように「Jsttepingexception:Can Not ConstructOf」がスローされます。

@Test(expected = JsonMappingException.class)
public void givenAbstractClass_whenDeserializing_thenException()
  throws IOException {
    String json = "{"animal":{"name":"lacy"}}";
    ObjectMapper mapper = new ObjectMapper();

    mapper.reader().forType(Zoo.class).readValue(json);
}

full exceptionは次のとおりです。

com.fasterxml.jackson.databind.JsonMappingException:
Can not construct instance of org.example.jackson.exception.Animal,
  problem: abstract types either need to be mapped to concrete types,
  have custom deserializer,
  or be instantiated with additional type information
  at
[Source: {"animal":{"name":"lacy"}}; line: 1, column: 2]
(through reference chain: org.example.jackson.exception.Zoo["animal"])
    at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)

2.2. ソリューション

抽象クラスの@JsonDeserializeという単純なアノテーションで問題を解決できます。

@JsonDeserialize(as = Cat.class)
abstract class Animal {...}

抽象クラスのサブタイプが複数ある場合は、この投稿に示すようにサブタイプ情報を含めることを検討する必要があります:Inheritance with Jackson

3. JsonMappingException:適切なコンストラクターがありません

3.1. 問題

それでは、一般的なJsttepingexception:適切なコンストラクターがありません found for typeを見てみましょう。

この例外は、Jackson can’t access the constructorの場合にスローされます。

次の例では、クラスUserにデフォルトのコンストラクターがありません。

public class User {
    public int id;
    public String name;

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

JSON文字列をユーザーにデシリアライズしようとすると、次の例のように、例外「Jsonmappingexception:適切なコンストラクタが見つかりません」がスローされます。

@Test(expected = JsonMappingException.class)
public void givenNoDefaultConstructor_whenDeserializing_thenException()
  throws IOException {
    String json = "{"id":1,"name":"John"}";
    ObjectMapper mapper = new ObjectMapper();

    mapper.reader().forType(User.class).readValue(json);
}

full exceptionは次のとおりです。

com.fasterxml.jackson.databind.JsonMappingException:
No suitable constructor found for type
[simple type, class org.example.jackson.exception.User]:
 can not instantiate from JSON object (need to add/enable type information?)
 at [Source: {"id":1,"name":"John"}; line: 1, column: 2]
        at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)

3.2. ソリューション

この問題を解決するには、次の例のようにデフォルトのコンストラクタを追加します。

public class User {
    public int id;
    public String name;

    public User() {
        super();
    }

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

これで、逆シリアル化すると、プロセスは正常に機能します。

@Test
public void givenDefaultConstructor_whenDeserializing_thenCorrect()
  throws IOException {

    String json = "{"id":1,"name":"John"}";
    ObjectMapper mapper = new ObjectMapper();

    User user = mapper.reader()
      .forType(User.class).readValue(json);
    assertEquals("John", user.name);
}

4. JsonMappingException:ルート名が予期したものと一致しません

4.1. 問題

次へ– Jsttepingexception:ルート名が予期したものと一致しないことを見てみましょう。

この例外は、the JSON doesn’t match exactly what Jackson is looking forの場合にスローされます。たとえば、メインのJSONは次の例のようにラップできます。

@Test(expected = JsonMappingException.class)
public void givenWrappedJsonString_whenDeserializing_thenException()
  throws IOException {
    String json = "{"user":{"id":1,"name":"John"}}";

    ObjectMapper mapper = new ObjectMapper();
    mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);

    mapper.reader().forType(User.class).readValue(json);
}

full exceptionは次のとおりです。

com.fasterxml.jackson.databind.JsonMappingException:
Root name 'user' does not match expected ('User') for type
 [simple type, class org.example.jackson.dtos.User]
 at [Source: {"user":{"id":1,"name":"John"}}; line: 1, column: 2]
   at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)

4.2. ソリューション

次の例のように、注釈@JsonRootNameを使用してこの問題を解決できます。

@JsonRootName(value = "user")
public class UserWithRoot {
    public int id;
    public String name;
}

ラップされたJSONをデシリアライズしようとすると、正しく動作します。

@Test
public void
  givenWrappedJsonStringAndConfigureClass_whenDeserializing_thenCorrect()
  throws IOException {

    String json = "{"user":{"id":1,"name":"John"}}";

    ObjectMapper mapper = new ObjectMapper();
    mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);

    UserWithRoot user = mapper.reader()
      .forType(UserWithRoot.class)
      .readValue(json);
    assertEquals("John", user.name);
}

5. JsonMappingException:クラスのシリアライザーが見つかりません

5.1. 問題

では、Jsttepingexception:クラスのシリアライザーが見つかりませんでした。

serialize an instance while its properties and their getters are privateを実行しようとすると、この例外がスローされます。

次の例では、「UserWithPrivateFields」をシリアル化しようとしています。

public class UserWithPrivateFields {
    int id;
    String name;
}

UserWithPrivateFields」のインスタンスをシリアル化しようとすると、次の例のように、例外「Jsttepingexception:クラスのシリアライザーが見つかりません」がスローされます。

@Test(expected = JsonMappingException.class)
public void givenClassWithPrivateFields_whenSerializing_thenException()
  throws IOException {
    UserWithPrivateFields user = new UserWithPrivateFields(1, "John");

    ObjectMapper mapper = new ObjectMapper();
    mapper.writer().writeValueAsString(user);
}

完全な例外は次のとおりです。

com.fasterxml.jackson.databind.JsonMappingException:
No serializer found for class org.example.jackson.exception.UserWithPrivateFields
 and no properties discovered to create BeanSerializer
(to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) )
  at c.f.j.d.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:59)

5.2. ソリューション

次の例のように、ObjectMapperの可視性を構成することでこの問題を解決できます。

@Test
public void givenClassWithPrivateFields_whenConfigureSerializing_thenCorrect()
  throws IOException {

    UserWithPrivateFields user = new UserWithPrivateFields(1, "John");

    ObjectMapper mapper = new ObjectMapper();
    mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

    String result = mapper.writer().writeValueAsString(user);
    assertThat(result, containsString("John"));
}

または、次の例のように、注釈@JsonAutoDetectを使用します。

@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class UserWithPrivateFields { ... }

もちろん、クラスのソースを変更するオプションがある場合、Jacksonが使用するゲッターを追加することもできます。

6. JsonMappingException:のインスタンスを逆シリアル化できません

6.1. 問題

次へ– Jsttepingexception:インスタンスを逆シリアル化できませんを見てみましょう。

この例外は、逆シリアル化中にthe wrong type is usedの場合にスローされます。

次の例では、UserListを逆シリアル化しようとしています。

@Test(expected = JsonMappingException.class)
public void givenJsonOfArray_whenDeserializing_thenException()
  throws JsonProcessingException, IOException {

    String json
      = "[{"id":1,"name":"John"},{"id":2,"name":"Adam"}]";
    ObjectMapper mapper = new ObjectMapper();
    mapper.reader().forType(User.class).readValue(json);
}

full exceptionは次のとおりです。

com.fasterxml.jackson.databind.JsonMappingException:
Can not deserialize instance of
  org.example.jackson.dtos.User out of START_ARRAY token
  at [Source: [{"id":1,"name":"John"},{"id":2,"name":"Adam"}]; line: 1, column: 1]
  at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)

6.2. ソリューション

次の例のように、タイプをUserからList<User>に変更することで、この問題を解決できます。

@Test
public void givenJsonOfArray_whenDeserializing_thenCorrect()
  throws JsonProcessingException, IOException {

    String json
      = "[{"id":1,"name":"John"},{"id":2,"name":"Adam"}]";

    ObjectMapper mapper = new ObjectMapper();
    List users = mapper.reader()
      .forType(new TypeReference>() {})
      .readValue(json);

    assertEquals(2, users.size());
}

7. UnrecognizedPropertyException

7.1. 問題

では、UnrecognizedPropertyExceptionを見てみましょう。

この例外は、逆シリアル化中にunknown property in the JSON文字列がある場合にスローされます。

次の例では、追加のプロパティ「checked」を使用してJSON文字列を逆シリアル化しようとしています。

@Test(expected = UnrecognizedPropertyException.class)
public void givenJsonStringWithExtra_whenDeserializing_thenException()
  throws IOException {

    String json = "{"id":1,"name":"John", "checked":true}";

    ObjectMapper mapper = new ObjectMapper();
    mapper.reader().forType(User.class).readValue(json);
}

full exceptionは次のとおりです。

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "checked" (class org.example.jackson.dtos.User),
 not marked as ignorable (2 known properties: "id", "name"])
 at [Source: {"id":1,"name":"John", "checked":true}; line: 1, column: 38]
 (through reference chain: org.example.jackson.dtos.User["checked"])
  at c.f.j.d.exc.UnrecognizedPropertyException.from(
    UnrecognizedPropertyException.java:51)

7.2. ソリューション

次の例のように、ObjectMapperを構成することで、この問題を解決できます。

@Test
public void givenJsonStringWithExtra_whenConfigureDeserializing_thenCorrect()
  throws IOException {

    String json = "{"id":1,"name":"John", "checked":true}";

    ObjectMapper mapper = new ObjectMapper();
    mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

    User user = mapper.reader().forType(User.class).readValue(json);
    assertEquals("John", user.name);
}

または、注釈@JsonIgnorePropertiesを使用できます。

@JsonIgnoreProperties(ignoreUnknown = true)
public class User {...}

8. JsonParseException:予期しない文字(” '(コード39))

8.1. 問題

次へ–JsonParseException: Unexpected character (”' (code 39))について説明しましょう。

この例外は、二重引用符ではなくthe JSON String to be deserialized contains single quotesの場合にスローされます。

次の例では、一重引用符を含むJSON文字列の逆シリアル化を試みます。

@Test(expected = JsonParseException.class)
public void givenStringWithSingleQuotes_whenDeserializing_thenException()
  throws JsonProcessingException, IOException {

    String json = "{'id':1,'name':'John'}";
    ObjectMapper mapper = new ObjectMapper();

    mapper.reader()
      .forType(User.class).readValue(json);
}

full exceptionは次のとおりです。

com.fasterxml.jackson.core.JsonParseException:
Unexpected character (''' (code 39)):
  was expecting double-quote to start field name
  at [Source: {'id':1,'name':'John'}; line: 1, column: 3]
  at c.f.j.core.JsonParser._constructError(JsonParser.java:1419)

8.2. ソリューション

We can solve this by configuring the ObjectMapper to allow single quotes:

@Test
public void
  givenStringWithSingleQuotes_whenConfigureDeserializing_thenCorrect()
  throws JsonProcessingException, IOException {

    String json = "{'id':1,'name':'John'}";

    JsonFactory factory = new JsonFactory();
    factory.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
    ObjectMapper mapper = new ObjectMapper(factory);

    User user = mapper.reader().forType(User.class)
      .readValue(json);

    assertEquals("John", user.name);
}

9. Jackson NoSuchMethodError

Finally – let’s quickly discuss the Jackson “No such method” errors.

When java.lang.NoSuchMethodError Exception is thrown, it is usually because you have multiple (and incompatible) versions of Jackson jars on your classpath.

full exceptionは次のとおりです。

java.lang.NoSuchMethodError:
com.fasterxml.jackson.core.JsonParser.getValueAsString()Ljava/lang/String;
 at c.f.j.d.deser.std.StringDeserializer.deserialize(StringDeserializer.java:24)

10. 結論

In this article, we did a deep dive in the most common Jackson problems – exceptions and errors, looking at the potential causes and at the solutions for each one.

これらすべての例とコードスニペットcan be found on Githubの実装–これはMavenベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。