gpt4 book ai didi

java - 让 Spring/Jackson 智能地反序列化模型子类

转载 作者:行者123 更新时间:2023-11-30 06:49:12 25 4
gpt4 key购买 nike

给定一个像这样的模型层次结构:

// WARNING: This is pseudo-code for giving an example!
public abstract class BaseVehicle {
private String make;
private String model;

// Constructors, getters & setters down here
}

public class Motorcycle extends BaseVehicle {
private int numCylinders;

// Constructors, getters & setters down here
}

public class Car extends BaseVehicle {
// ...etc.
}

并给出以下有效负载类(将发送到 Spring Controller ):

public class Payload {
@JsonIgnore
@JsonProperty(value = "orgId")
private String orgId;

@JsonIgnore
@JsonProperty(value = "isInitialized")
private Boolean isInitialized;

@JsonIgnore
@JsonProperty(value = "vehicle")
private BaseVehicle vehicle;

// Constructors, getters & setters down here
}

我想知道是否可以将 Spring Controller (使用 Jackson 进行 JSON 序列化)配置为仅期望它接收的 Payload 中的 BaseVehicle 实例,但是动态推断实际发送的是哪个 BaseVehicle 子类:

@RequestMapping(value='/payload', method=RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody MyAppResponse onPayload(@RequestBody Payload payload) {
logger.info("Received a payload with a vehicle of type: " + payload.getVehicle().getClass().getName());
}

这样,如果我碰巧发送一个包含 Motorcycle 作为其 vehicle 字段的 Payload JSON,那么当该 logger .info(...) 语句触发,代码看到 vehicleMotorcycle(对于任何其他 BaseVehicle 也是如此)子类)?

这可能吗?如果可能的话,如何实现?

最佳答案

However I'd greatly prefer a solution that allows the JSON to remain as-is.

正如我在上面的评论中提到的,您可以分析有效负载车辆 JSON 对象树,以便进行一些分析以尝试检测有效负载元素类型。

@JsonDeserialize(using = BaseVehicleJsonDeserializer.class)
abstract class BaseVehicle {

@JsonProperty
private String make;

@JsonProperty
private String model;

}

@JsonDeserialize(as = Car.class)
final class Car
extends BaseVehicle {
}

@JsonDeserialize(as = Motorcycle.class)
final class Motorcycle
extends BaseVehicle {

@JsonProperty
private int numCylinders;

}

这里的技巧是 @JsonDeserialize 注释。 BaseVehicleJsonDeserializer 可以按如下方式实现:

final class BaseVehicleJsonDeserializer
extends JsonDeserializer<BaseVehicle> {

@Override
public BaseVehicle deserialize(final JsonParser parser, final DeserializationContext context)
throws IOException {
final TreeNode treeNode = parser.readValueAsTree();
final Class<? extends BaseVehicle> baseVehicleClass = Stream.of(treeNode)
// Check if the tree node is ObjectNode
.filter(tn -> tn instanceof ObjectNode)
// And cast
.map(tn -> (ObjectNode) tn)
// Now "bind" the object node with if the object node can be supported by the resolver
.flatMap(objectNode -> Stream.of(BaseVehicleTypeResolver.cachedBaseVehicleTypeResolvers).filter(resolver -> resolver.matches(objectNode)))
// If found, just get the detected vehicle class
.map(BaseVehicleTypeResolver::getBaseVehicleClass)
// Take the first resolver only
.findFirst()
// Or throw a JSON parsing exception
.orElseThrow(() -> new JsonParseException(parser, "Cannot parse: " + treeNode));
// Convert the JSON tree to the resolved class instance
final ObjectMapper objectMapper = (ObjectMapper) parser.getCodec();
return objectMapper.treeToValue(treeNode, baseVehicleClass);
}

// Known strategies here
private enum BaseVehicleTypeResolver {

CAR_RESOLVER {
@Override
protected Class<? extends BaseVehicle> getBaseVehicleClass() {
return Car.class;
}

@Override
protected boolean matches(final ObjectNode objectNode) {
return !objectNode.has("numCylinders");
}
},

MOTORCYCLE_RESOLVER {
@Override
protected Class<? extends BaseVehicle> getBaseVehicleClass() {
return Motorcycle.class;
}

@Override
protected boolean matches(final ObjectNode objectNode) {
return objectNode.has("numCylinders");
}
};

// Enum.values() returns array clones every time it's invoked
private static final BaseVehicleTypeResolver[] cachedBaseVehicleTypeResolvers = BaseVehicleTypeResolver.values();

protected abstract Class<? extends BaseVehicle> getBaseVehicleClass();

protected abstract boolean matches(ObjectNode objectNode);

}

}

正如您所看到的,这种方法或多或少是脆弱和复杂的,但它试图进行一些分析。现在,如何使用它:

final ObjectMapper mapper = new ObjectMapper();
Stream.of(
"{\"orgId\":\"foo\",\"isInitialized\":true,\"vehicle\":{\"make\":\"foo\",\"model\":\"foo\"}}",
"{\"orgId\":\"bar\",\"isInitialized\":true,\"vehicle\":{\"make\":\"bar\",\"model\":\"bar\",\"numCylinders\":4}}"
)
.map(json -> {
try {
return mapper.readValue(json, Payload.class);
} catch ( final IOException ex ) {
throw new RuntimeException(ex);
}
})
.map(Payload::getVehicle)
.map(BaseVehicle::getClass)
.forEach(System.out::println);

输出:

class q43138817.Car
class q43138817.Motorcycle

关于java - 让 Spring/Jackson 智能地反序列化模型子类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43138817/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com