REST RestTemplateを使用したAPIを使用しています。APIから取得する応答には、ネストされたオブジェクトが多数含まれています。ここに例として小さなスニペットを示します。
"formularios": [
{
"form_data_id": "123006",
"form_data": {
"form_data_id": "123006",
"form_id": "111",
"efs": {
"1": {},
"2": "{\"t\":\"c\",\"st\":\"m\",\"v\":[{\"id\":\"3675\",\"l\":\"a) Just an example\",\"v\":\"1\"},{\"id\":\"3676\",\"l\":\"b) Another example.\",\"v\":\"2\"}]}"
}
}
私が抱えている問題は、ほとんどの場合、「1」は「2」のように実際にコンテンツを持ち、ジャクソンはそれをオブジェクト「efs」の文字列として解析するだけです。しかし、時々、コードスニペットのように、APIはそれを空に送信し、Jacksonはそれをオブジェクトとして受け取ります。これにより、START_OBJECTについて何かを示すエラーが表示されます(正確なエラーを思い出せませんが、この質問では重要ではありません) )。
そこで、私はカスタムデシリアライザを作成することにしました。そうすれば、jacksonが「1」を読み取ったときに、空のオブジェクトが無視され、null文字列として解析されるだけです。
これが私のカスタムデシリアライザです:
public class CustomDeserializer extends StdDeserializer<Efs> {
public CustomDeserializer(Class<Efs> t) {
super(t);
}
@Override
public Efs deserialize(JsonParser jp, DeserializationContext dc)
throws IOException, JsonProcessingException {
String string1 = null;
String string2 = null;
JsonToken currentToken = null;
while ((currentToken = jp.nextValue()) != null) {
if (currentToken.equals(JsonToken.VALUE_STRING)) {
if (jp.getCurrentName().equals("1")) {
string1 = jp.getValueAsString();
} else {
string2 = jp.getValueAsString();
}
} else {
if (jp.getCurrentName().equals("2")) {
string2 = jp.getValueAsString();
}
}
}
return new Efs(string1, string2);
}
}
そして、これは、APIからの応答を受信するときに使用する方法です。
ObjectMapper mapper = new ObjectMapper();
SimpleModule mod = new SimpleModule("EfsModule");
mod.addDeserializer(Efs.class, new CustomDeserializer(Efs.class));
mapper.registerModule(mod);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
MappingJackson2HttpMessageConverter jsonMessageConverter = new MappingJackson2HttpMessageConverter();
jsonMessageConverter.setObjectMapper(mapper);
messageConverters.add(jsonMessageConverter);
RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(messageConverters);
エラーが発生します:
CustomDeserializer has no default (no arg) constructor
しかし、私は自分が間違っていることやそれを解決する方法を正確には知りません。長い質問に対する助けと謝罪のおかげで、私はできるだけ多くの状況を伝えたかった。
引数のないデフォルトのコンストラクターが必要です。あなたができることは1つを作成することです(または本当に必要ない場合は他のものを置き換えてください):
public class CustomDeserializer extends StdDeserializer<Efs> {
public CustomDeserializer() {
super(Efs.class);
}
...
}
ユーザーが陥る可能性のある罠も1つあります(私の自己のように)。次のように、デシリアライザを内部クラス(静的なネストされたクラスではない)として宣言すると、
_@JsonDeserialize(using = DomainObjectDeserializer.class)
public class DomainObject {
private String key;
public class DomainObjectDeserializer extends StdDeserializer<DomainObject> {
public DomainObjectDeserializer() {
super(DomainObject.class);
}
@Override
public DomainObject deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
// code
}
}
}
_
ジャクソンは Class#getDeclaredConstructor() を引数なしで使用します(メソッドはvararg
を受け入れます)。上記のコードは、JacksonがDomainObjectDeserializer
を作成しようとすると例外をスローします。これは、javac
が、含まれているクラス参照を受け入れるコンストラクターを生成するためです。技術的に言えば、DomainObjectDeserializer
にはデフォルトのコンストラクタはありません。
好奇心のために、DomainObjectDeserializer.class.getDeclaredConstructors()
を実行し、メソッドが、クラス参照を囲むコンストラクター定義を含む単一の要素配列を返すことを確認できます。
DomainObjectDeserializer
はstatic
クラスとして宣言する必要があります。
詳細はこちら answer をご覧ください。