1. 概要

このチュートリアルでは、最も一般的なJackson Exceptions JsonMappingExceptionおよびUnrecognizedPropertyExceptionについて説明します。

最後に、Jacksonの「そのような方法はありません」エラーについて簡単に説明します。

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

2.1. 問題

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

この例外は、 Jacksonがクラスのインスタンスを作成できない場合にスローされます。これは、クラスが 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() { }
}

JSON String をZooインスタンスに逆シリアル化しようとすると、JsonMappingExceptionがスローされます。次のインスタンスを構築できません:

@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);
}

これは完全な例外です:

com.fasterxml.jackson.databind.JsonMappingException: 
Can not construct instance of org.baeldung.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.baeldung.jackson.exception.Zoo["animal"])
	at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)

2.2. ソリューション

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

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

抽象クラスのサブタイプが複数ある場合は、記事ジャクソンの継承に示されているように、サブタイプ情報を含めることを検討する必要があることに注意してください。

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

3.1. 問題

次に、一般的なJsonMappingExceptionを見てみましょう。typeに適したコンストラクターが見つかりません。

この例外は、Jacksonがコンストラクターにアクセスできない場合にスローされます。

次の例では、クラス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);
}

そしてこれは完全な例外です:

com.fasterxml.jackson.databind.JsonMappingException: 
No suitable constructor found for type 
[simple type, class org.baeldung.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. 問題

次に、JsonMappingExceptionを見てみましょう:ルート名が期待値と一致しません。

この例外は、JSONがJacksonが探しているものと正確に一致しない場合にスローされます。

たとえば、メインの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);
}

これは完全な例外です:

com.fasterxml.jackson.databind.JsonMappingException:
Root name 'user' does not match expected ('User') for type
 [simple type, class org.baeldung.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. 問題

次に、JsonMappingException:クラスのシリアライザーが見つかりませんを見てみましょう。

この例外は、プロパティとそのゲッターがプライベートであるときにインスタンスをシリアル化しようとするとスローされます。

UserWithPrivateFieldsをシリアル化してみます。

public class UserWithPrivateFields {
    int id;
    String name;
}

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

@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.baeldung.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. 問題

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

この例外は、逆シリアル化中に間違ったタイプが使用された場合にスローされます。

この例では、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);
}

そして、これが完全な例外です。

com.fasterxml.jackson.databind.JsonMappingException:
Can not deserialize instance of 
  org.baeldung.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. ソリューション

タイプをから変更することでこの問題を解決できますユーザーリスト

@Test
public void givenJsonOfArray_whenDeserializing_thenCorrect() 
  throws JsonProcessingException, IOException {
 
    String json
      = "[{"id":1,"name":"John"},{"id":2,"name":"Adam"}]";
   
    ObjectMapper mapper = new ObjectMapper();
    List<User> users = mapper.reader()
      .forType(new TypeReference<List<User>>() {})
      .readValue(json);

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

7. UnrecognizedPropertyException

7.1. 問題

次に、UnrecognizedPropertyExceptionを見てみましょう。

この例外は、逆シリアル化中に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);
}

これは完全な例外です:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "checked" (class org.baeldung.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.baeldung.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:予期しない文字(”’(コード39))について説明します。

この例外は、逆シリアル化されるJSON文字列に二重引用符ではなく一重引用符が含まれている場合にスローされます。

一重引用符を含む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);
}

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

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. ソリューション

ObjectMapper を一重引用符で囲むように構成することで、これを解決できます。

@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. ジャクソンNoSuchMethodError

最後に、Jacksonの「そのような方法はありません」エラーについて簡単に説明しましょう。

java .lang.NoSuchMethodError 例外がスローされた場合、通常、クラスパスに複数の(互換性のない)バージョンのJacksonjarがあることが原因です。

これは完全な例外です:

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. 結論

この記事では、最も一般的なジャクソンの問題(例外とエラー)について深く掘り下げ、考えられる原因とそれぞれの解決策を調べました。

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