Java @JsonTypeResolver是使用多个属性进行解析的唯一选项吗?
我有以下格式的传入JSON数据Java @JsonTypeResolver是使用多个属性进行解析的唯一选项吗?,java,json,jackson,Java,Json,Jackson,我有以下格式的传入JSON数据 { "header": { "schema_id": { "namespace": "omh", "name": "physical-activity", }, }, "body": { "activity_name": "walking", "distance": { "value": 1.5,
{
"header": {
"schema_id": {
"namespace": "omh",
"name": "physical-activity",
},
},
"body": {
"activity_name": "walking",
"distance": {
"value": 1.5,
"unit": "mi"
},
}
}
以及相应的Java类
public class DataPoint<T extends Measure> {
private DataPointHeader header;
private T body;
我希望Jackson根据JSON文档中的schema\u id
将body
解析为PhysicalActivity
类型,例如伪代码
if schema_id.namespace == 'omh' && schema_id.name == 'physical-activity'
then return PhysicalActivity.class
我曾尝试使用@JsonTypeIdResolver
执行此操作,但如果我尝试使用@JsonTypeInfo
导航到header.schema\u id.name
,例如
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
property = "header.schema_id.name")
@JsonTypeIdResolver(DataPointTypeIdResolver.class)
public abstract class Measure {
我得到一个缺少的属性:“header.schema\u id.name”
错误。即使我可以,我也不认为我可以对名称空间
和名称
属性做出决定
除了用
@jsontypesolver
从头开始构建之外,还有什么明智的方法可以做到这一点吗?在Jackson源代码中,似乎有很多假设类型ID是字符串,所以我怀疑JSontypesolver是一种方法。。。当然,这似乎并不简单
至少当您只有“header”和“body”属性时,完整的自定义反序列化器并不难:
public static class DataPointDeserializer extends StdDeserializer<DataPoint<?>> implements ResolvableDeserializer {
private JsonDeserializer<Object> headerDeserializer;
private Map<SchemaId, JsonDeserializer<Object>> activityDeserializers;
public DataPointDeserializer() {
super(DataPoint.class);
}
@Override
public void resolve(DeserializationContext ctxt) throws JsonMappingException {
headerDeserializer = ctxt.findRootValueDeserializer(ctxt.getTypeFactory().constructType(
DataPointHeader.class));
activityDeserializers = new HashMap<>();
activityDeserializers.put(new SchemaId("omh", "physical-activity"),
ctxt.findRootValueDeserializer(ctxt.getTypeFactory().constructType(PhysicalActivity.class)));
}
@Override
public DataPoint<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException,
JsonProcessingException {
String fieldName = p.nextFieldName();
if (fieldName == null)
throw ctxt.wrongTokenException(p, JsonToken.FIELD_NAME, "expected 'header' and 'body' fields");
if (fieldName.equals("header")) {
p.nextToken();
DataPointHeader header = (DataPointHeader) headerDeserializer.deserialize(p, ctxt);
JsonDeserializer<Object> bodyDeserializer = activityDeserializers.get(header.schemaId);
if (bodyDeserializer == null) throw ctxt.mappingException("No mapping for schema: " + header.schemaId);
fieldName = p.nextFieldName();
if (fieldName == null)
throw ctxt.wrongTokenException(p, JsonToken.FIELD_NAME, "expected 'body' field after header");
p.nextToken();
Measure body = (Measure) bodyDeserializer.deserialize(p, ctxt);
DataPoint<Measure> dataPoint = new DataPoint<>();
dataPoint.header = header;
dataPoint.body = body;
return dataPoint;
}
else if (fieldName.equals("body")) {
p.nextToken();
try (TokenBuffer tb = new TokenBuffer(p)) {
tb.copyCurrentStructure(p);
fieldName = p.nextFieldName();
if (fieldName == null)
throw ctxt.wrongTokenException(p, JsonToken.FIELD_NAME, "expected 'header' field after body");
if (!fieldName.equals("header"))
throw ctxt.weirdStringException(fieldName, DataPoint.class, "Unexpected field name");
p.nextToken();
DataPointHeader header = (DataPointHeader) headerDeserializer.deserialize(p, ctxt);
JsonDeserializer<Object> bodyDeserializer = activityDeserializers.get(header.schemaId);
if (bodyDeserializer == null)
throw ctxt.mappingException("No mapping for schema: " + header.schemaId);
JsonParser bodyParser = tb.asParser();
bodyParser.nextToken();
Measure body = (Measure) bodyDeserializer.deserialize(bodyParser, ctxt);
DataPoint<Measure> dataPoint = new DataPoint<>();
dataPoint.header = header;
dataPoint.body = body;
return dataPoint;
}
}
else throw ctxt.weirdStringException(fieldName, DataPoint.class, "Unexpected field name");
}
}
public静态类DataPointDeserializer扩展StdDeserializer反序列化(JsonParser p,DeserializationContext ctxt)抛出IOException,
JsonProcessingException{
字符串fieldName=p.nextFieldName();
if(fieldName==null)
抛出ctxt.ErrorTokenException(p,JsonToken.FIELD_NAME,“应为'header'和'body'字段”);
if(fieldName.equals(“标头”)){
p、 nextToken();
DataPointHeader=(DataPointHeader)headerDeserializer.deserialize(p,ctxt);
JsonDeserializer bodyDeserializer=activityDeserializers.get(header.schemaId);
if(bodyDeserializer==null)抛出ctxt.mappingException(“架构没有映射:”+header.schemaId);
fieldName=p.nextFieldName();
if(fieldName==null)
抛出ctxt.ErrorTokenException(p,JsonToken.FIELD_NAME,“头后应为'body'字段”);
p、 nextToken();
度量体=(度量)体反序列化器。反序列化(p,ctxt);
数据点数据点=新数据点();
dataPoint.header=头;
dataPoint.body=body;
返回数据点;
}
else if(fieldName.equals(“body”)){
p、 nextToken();
try(TokenBuffer tb=新的TokenBuffer(p)){
tb.copyCurrentStructure(p);
fieldName=p.nextFieldName();
if(fieldName==null)
抛出ctxt.ErrorTokenException(p,JsonToken.FIELD_NAME,“正文后应为'header'字段”);
如果(!fieldName.equals(“标头”))
抛出ctxt.weirdStringException(fieldName,DataPoint.class,“意外字段名”);
p、 nextToken();
DataPointHeader=(DataPointHeader)headerDeserializer.deserialize(p,ctxt);
JsonDeserializer bodyDeserializer=activityDeserializers.get(header.schemaId);
if(bodyDeserializer==null)
抛出ctxt.mappingException(“架构:+header.schemaId没有映射”);
JsonParser-bodyParser=tb.aspaser();
bodyParser.nextToken();
Measure body=(Measure)bodyDeserializer.deserialize(bodyParser,ctxt);
数据点数据点=新数据点();
dataPoint.header=头;
dataPoint.body=body;
返回数据点;
}
}
else抛出ctxt.weirdStringException(fieldName,DataPoint.class,“意外字段名”);
}
}
否,无法使用路径表达式匹配属性。这需要访问完整的JSON(子树)
对于Jackson 2.5,类型ID是标量值(通常是字符串)的要求略有放宽,因此这可能得到支持。有关这一问题的更多背景信息:
但我认为这还不足以让你使用标准的Jackson类型id分辨率
你可以考虑的是一个创造者的方法,比如:
abstract class Measure {
// either constructor, or static method:
@JsonCreator
public static Measure construct(
@JsonProperty("header") HeadOb header, // or JsonNode, Map etc
@JsonProperty("body") JsonNode body) {
// extract type info, build actual instance from body
}
}
或者,可能是
Converter
,您使用一个中间包装器来绑定头,而body只绑定到JsonNode
或Map
,然后从那里进行构造。谢谢!我会仔细检查并发布反馈。我刚刚意识到它不会在字段之后检查和使用最终对象,我只在独立文档上测试了它
abstract class Measure {
// either constructor, or static method:
@JsonCreator
public static Measure construct(
@JsonProperty("header") HeadOb header, // or JsonNode, Map etc
@JsonProperty("body") JsonNode body) {
// extract type info, build actual instance from body
}
}