杰克逊在自定义 StdDeserializer 中给出 StackOverflowError 读取值
Jackson gives StackOverflowError reading value in custom StdDeserializer
有 2 个简单的 类 像:
@Setter
@Getter
public class Continent {
private String id;
private String code;
private String name;
}
@Setter
@Getter
public class Country {
private String id;
private String alpha2Code;
private String alpha3Code;
private String name;
private Continent continent;
}
阅读以下 yaml 时:
id: brazil
alpha2_code: BR
alpha3_code: BRA
name: Brazil
continent_id: south-america
我想使用 continent_id
从范围为 List<Continent>
的应用程序中检索 Continent
。
我能想到的最好的事情是使用自定义反序列化器,例如:
public class CountryDeserializer extends StdDeserializer<Country> {
public CountryDeserializer() {
super(Country.class);
}
@Override
public Country deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// This works... the `continentId` is retrieved!
JsonNode node = jp.getCodec().readTree(jp);
String continentId = node.get("continent_id").asText();
// How to access application scoped continents? Use injectable value?
Continent continent = getContinent(continentId);
// Read value for other properties; don't want to read other properties manually!
Country country = jp.getCodec().readValue(jp, Country.class);
// But unfortunately this throws a Whosebug...
country.setContinent(continent);
return country;
}
}
但问题是我希望 Jackson 自动读取其他属性。
我不想手动这样做,好像将来添加 属性 它可能会被遗忘,并且对于具有 20 个属性的其他实体,这变得非常麻烦...
我尝试使用 Country country = jp.getCodec().readValue(jp, Country.class);
但是这会导致堆栈溢出异常,因为它显然进入了自定义反序列化器的循环。
有没有办法使用 Jackson 来解决这个问题,或者在这种情况下是否有另一种更好的方法来获取和设置 Continent
?
请注意,我正在使用一组预定义的域 类 我无法更改。
如果需要,我可以修改对象映射器并添加混合。
我没有使用 CountryDeserializer
,而是使用 ContinentReferenceDeserializer
实现了它。
这样,其他 Country
属性将“自动”反序列化。
看起来像:
public class ContinentReferenceDeserializer extends StdDeserializer<Continent> {
public ContinentReferenceDeserializer() {
super(Continent.class);
}
@Override
public Continent deserialize(JsonParser parser, DeserializationContext context) throws IOException {
String id = parser.getText(); // returns the continent id (`continent_id` in json)
Map<String, Continent> continents = (Map<String, Continent>) context.findInjectableValue("continents", null, null);
return continents.gett(id);
}
}
它被用在 CountryMixIn
中,例如:
public abstract class CountryMixIn {
@JsonProperty("continent_id")
@JsonDeserialize(using = ContinentReferenceDeserializer.class)
abstract Continent getContinent();
}
请注意,如果您不使用 Mix-ins 而是直接注释 domain/dtoa 类,以上内容也可以应用于这些。
ObjectMapper
可以这样设置:
Map<String, Continent> continents = .. // get the continents
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(Country.class, CountryMixIn.class);
mapper.setInjectableValues(new InjectableValues.Std().addValue("continents", continents));
然后可以这样调用:
String json = .. // get the json
Country country = mapper.readValue(json, Country.class);
有 2 个简单的 类 像:
@Setter
@Getter
public class Continent {
private String id;
private String code;
private String name;
}
@Setter
@Getter
public class Country {
private String id;
private String alpha2Code;
private String alpha3Code;
private String name;
private Continent continent;
}
阅读以下 yaml 时:
id: brazil
alpha2_code: BR
alpha3_code: BRA
name: Brazil
continent_id: south-america
我想使用 continent_id
从范围为 List<Continent>
的应用程序中检索 Continent
。
我能想到的最好的事情是使用自定义反序列化器,例如:
public class CountryDeserializer extends StdDeserializer<Country> {
public CountryDeserializer() {
super(Country.class);
}
@Override
public Country deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// This works... the `continentId` is retrieved!
JsonNode node = jp.getCodec().readTree(jp);
String continentId = node.get("continent_id").asText();
// How to access application scoped continents? Use injectable value?
Continent continent = getContinent(continentId);
// Read value for other properties; don't want to read other properties manually!
Country country = jp.getCodec().readValue(jp, Country.class);
// But unfortunately this throws a Whosebug...
country.setContinent(continent);
return country;
}
}
但问题是我希望 Jackson 自动读取其他属性。 我不想手动这样做,好像将来添加 属性 它可能会被遗忘,并且对于具有 20 个属性的其他实体,这变得非常麻烦...
我尝试使用 Country country = jp.getCodec().readValue(jp, Country.class);
但是这会导致堆栈溢出异常,因为它显然进入了自定义反序列化器的循环。
有没有办法使用 Jackson 来解决这个问题,或者在这种情况下是否有另一种更好的方法来获取和设置 Continent
?
请注意,我正在使用一组预定义的域 类 我无法更改。 如果需要,我可以修改对象映射器并添加混合。
我没有使用 CountryDeserializer
,而是使用 ContinentReferenceDeserializer
实现了它。
这样,其他 Country
属性将“自动”反序列化。
看起来像:
public class ContinentReferenceDeserializer extends StdDeserializer<Continent> {
public ContinentReferenceDeserializer() {
super(Continent.class);
}
@Override
public Continent deserialize(JsonParser parser, DeserializationContext context) throws IOException {
String id = parser.getText(); // returns the continent id (`continent_id` in json)
Map<String, Continent> continents = (Map<String, Continent>) context.findInjectableValue("continents", null, null);
return continents.gett(id);
}
}
它被用在 CountryMixIn
中,例如:
public abstract class CountryMixIn {
@JsonProperty("continent_id")
@JsonDeserialize(using = ContinentReferenceDeserializer.class)
abstract Continent getContinent();
}
请注意,如果您不使用 Mix-ins 而是直接注释 domain/dtoa 类,以上内容也可以应用于这些。
ObjectMapper
可以这样设置:
Map<String, Continent> continents = .. // get the continents
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(Country.class, CountryMixIn.class);
mapper.setInjectableValues(new InjectableValues.Std().addValue("continents", continents));
然后可以这样调用:
String json = .. // get the json
Country country = mapper.readValue(json, Country.class);