web-dev-qa-db-ja.com

JacksonでJSONをデシリアライズする-なぜJsonMappingExceptionが「適切なコンストラクターなし」なのか?

Jacksonを使用してJSON文字列の逆シリアル化に問題があります(ただし、オブジェクトをJSONにシリアル化するのに問題はありません)。

以下に、使用するクラスを示します。問題は、JSON文字列(他の場所でシリアル化され、Webサービス経由で取得されたProtocolContainer)を受け取り、それを逆シリアル化するときに発生します。

JSON文字列:

{"DataPacketJSONString":null、 "DataPacketType": "MyPackage.DataPackets.LoginRequestReply"、 "MessageId":6604、 "SenderUsername":null、 "SubPacket":{"__ type": "LoginRequestReply:#MyPackage.DataPackets"、 "理由 ":"パスまたはユーザー名が間違っています "、"成功 ":false、"ユーザー名 ":"ユーザー1 "}}

私はこのようにデシリアライズしようとします:

ProtocolContainer ret = ProtocolContainer.Create(jsonString);

また、ProtocolContainerで実行されるコードは次のとおりです。例外:

org.codehaus.jackson.map.JsonMappingException:型[単純型、クラスMyPackage.ProtocolContainer]に適したコンストラクターが見つかりません:[Source:Java.io.]でJSONオブジェクトからインスタンス化できません(型情報を追加/有効にする必要がありますか?)。 StringReader @ 4059dcb0;行:1、列:2]

ProtocolContainer.Java-「サブパケット」をカプセル化するコンテナクラス:

import Java.io.IOException;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;

import MyPackage.DataPackets.*;

public class ProtocolContainer 
{
    public String SenderUsername;
    public String DataPacketType;
    public long MessageId;
    public String DataPacketJSONString;
    public DataPacket SubPacket;

    public ProtocolContainer(DataPacket dp)
    {
        DataPacketType = dp.getClass().toString().substring(6);
        SubPacket = dp;
    }

    public String toJSON()
    {
        try {
            if (SubPacket != null)
                this.DataPacketJSONString = ProtocolContainer.mapper.writeValueAsString(SubPacket);

            return ProtocolContainer.mapper.writeValueAsString(this);
        } catch (JsonGenerationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (JsonMappingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    public static ObjectMapper mapper = new ObjectMapper();

    public static ProtocolContainer Create(String jsonString)
    {
        ProtocolContainer pc = null;
        try {
            pc = mapper.readValue(jsonString, ProtocolContainer.class); // error here!
        } catch (JsonParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (JsonMappingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();  // Exception when deserializing
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


        try 
        {
            if (pc != null && pc.DataPacketType == "LoginRequest")
                pc.SubPacket = mapper.readValue(jsonString, LoginRequest.class);
    }
        catch (JsonParseException e) 
        {
            e.printStackTrace();
        }
        catch (JsonMappingException e) 
        {
            e.printStackTrace();
        }
        catch (IOException e) 
        {
            e.printStackTrace();
        }
        return pc;
    }
}

DataPacket.Java-すべてのデータパケットのスーパークラス

public class DataPacket 
{

}

LoginRequestReply.Java-a DataPacket

package MyPackage.DataPackets;

import MyPackage.DataPacket;

public class LoginRequestReply extends DataPacket
{
    public boolean LoginOK;
    public int UserId;
}
23
Ted

エラーメッセージにすべてが示されています。ProtocolContainerにはデフォルトのコンストラクタがないため、ジャクソンはそのインスタンスを作成できません。 (ProtocolContainerを作成する現在の唯一の方法は、DataPacketを渡すことです。)

31
Ola Herrdahl

この場合、コンストラクターに_@JsonCreator_注釈を追加できます。それを行うには2つの方法があります。

  • その注釈のみを追加する場合、一致するJSON全体が最初に唯一の引数の型( `DataPacket ')にバインドされます。あなたはそれをしたくないと思います。
  • 引数の前に_@JsonProperty_アノテーションも追加すると、その名前に一致するJSONプロパティがコンストラクターに渡されます(Javaバイトコードにはメソッドまたはコンストラクターの引数の名前が含まれないため、アノテーションは必須です) )-@JsonProperty("SubPacket")が欲しいと思う

これは、コンストラクターに必要な情報がJSONから来る場合に機能します。そうでない場合は、引数なしの代替コンストラクタを追加する必要があります。

ところで、この場合、エラーメッセージは間違って聞こえます。 JSON文字列の場合、JSONデータが期待値と一致する場合にのみ指定する必要があります。

15
StaxMan

サムルール:マッピングクラスとして使用した各クラスのデフォルトコンストラクターを追加します。あなたはこれを逃し、問題が発生します!

デフォルトのコンストラクタを追加するだけで機能します。

2
Badal

Lombok!を使用した場合の別の可能性理由はわかりません。

@Getter
@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public Car implements Serializable {
   Map<String, Object> basicInfo;
   CarEnums.TypeEnum type;
   List<Maintenance> maintenances;
   public void addMaintenance(Maintenance m) {
      // initialize if null
      maintenances.add(m);
   }

   // must be static or jackson throws "inner class cannot be static" exception.  Yes you see it right. 
   public static class Maintenance { 
      private Long id;
      public class Maintenance(Long id) { // constructor causes the exception
         this.id = id;
      }
   }
   ...
}

Lombokコンストラクターアノテーションが外部クラス、内部クラスで使用されている場合、すべてのargsコンストラクターを手動で記述しても、コンストラクターが見つからないことを訴えます。独自のコードを作成する代わりにMaintenance@AllArgsConstructorを使用すると、jacksonは正常にデシリアライズします。今日も同じ経験を得たので、@AllArgsConstructorを追加すると解決します。

1
Tiina

私は問題に直面していましたが、答えはどれもうまくいきませんでした。スローされる例外は非常に一般的なものであり、n個の原因でスローされるようです。したがって、1つの修正がすべてのユーザーに有効とは限りません。私の場合:jsonレスポンスがあり、クレジットカードは複雑なタイプですが、オプションです。クレジットカードデータがない場合、応答で空の文字列を取得していました。

"クレジットカード":""

しかし、クレジットカードは私たちにとって複雑なタイプです。

<xs:element name="CC" minOccurs="0">
                                <xs:complexType>
                                    <xs:sequence>
                                        <xs:element name="aaa" type="xs:string" minOccurs="0"/>
                                        <xs:element name="bbb" type="xs:string" minOccurs="0"/>
                                        <xs:element name="ccc" type="xs:string" minOccurs="0"/>
                                    </xs:sequence>
                                </xs:complexType>
                            </xs:element>

クレジットカードデータがない場合、jsonの応答には次のようなものがあるはずです。

"クレジットカード":{}

「クレジットカード」ではなく: ""

この問題は修正されました。

1
vks