[英]Jackson Serialization / Deserialization: Dynamic properties and fields
我使用Spring MVC来驱动当前正在使用的应用程序的API。 API响应的序列化是通过Jackson的ObjectMapper
完成的。 我面临以下情况,我们正在扩展一些对象以支持UserDefinedFields(UDF) ,这在下面的抽象UserDefinedResponse
。 作为SaaS解决方案,多个客户端具有不同的配置,这些配置存储在数据库中用于其自定义字段。
这个问题的目标是能够使用其UDF数据响应每个客户端。 这将需要
customString1
, customString2
,...动态重命名为其相应的UDF标签 抽象响应示例
public abstract class UserDefinedResponse {
public String customString1;
public String customString2;
public String customString3;
public String customString4;
}
对于扩展UserDefinedResponse
对象的产品的响应
public class Product extends UserDefinedResponse {
public long id;
public String name;
public float price;
}
最后,假设有一位客户
customString1
= "supplier"
customString2
= "warehouse"
为该客户序列化Product
应该会导致类似以下情况:
{
"id" : 1234,
"name" : "MacBook Air",
"price" : 1299,
"supplier" : "Apple",
"warehouse" : "New York warehouse"
}
我认为您可以借助一些Jackson注释来做您需要的事情:
public abstract class UserDefinedResponse {
@JsonIgnore
public String customString1;
@JsonIgnore
public String customString2;
@JsonIgnore
public String customString3;
@JsonIgnore
public String customString4;
@JsonIgnore // Remove if clientId must be serialized
public String clientId;
private Map<String, Object> dynamicProperties = new HashMap<>();
@JsonAnyGetter
public Map<String, Object> getDynamicProperties() {
Mapper.fillDynamicProperties(this, this.dynamicProperties);
return this.dynamicProperties;
}
@JsonAnySetter
public void setDynamicProperty(String name, Object value) {
this.dynamicProperties.put(name, value);
Mapper.setDynamicProperty(this.dynamicProperties, name, this);
}
}
首先,使用@JsonIgnore
注释基类的所有属性,因为这些属性不会成为响应的一部分。 然后,使用@JsonAnyGetter
注释展平dynamicProperties
贴图,该贴图将保存动态属性。 最后, @JsonAnySetter
批注将由Jackson用于反序列化。
缺少的部分是Mapper
实用程序类:
public abstract class Mapper<T extends UserDefinedResponse> {
private static final Map<Class<T>, Map<String, Mapper<T>>> MAPPERS = new HashMap<>();
static {
// Mappers for Products
Map<String, Mapper<Product>> productMappers = new HashMap<>();
productMappers.put("CLIENT_1", new ProductMapperClient1());
productMappers.put("CLIENT_2", new ProductMapperClient2());
// etc for rest of clients
MAPPERS.put(Product.class, productMappers);
// Mappers for Providers
Map<String, Mapper<Provider>> providerMappers = new HashMap<>();
providerMappers.put("CLIENT_1", new ProviderMapperClient1());
providerMappers.put("CLIENT_2", new ProviderMapperClient2());
// etc for rest of clients
MAPPERS.put(Provider.class, providerMappers);
// etc for rest of entities
// (each entity needs to add specific mappers for every client)
}
protected Mapper() {
}
public static void fillDynamicProperties(T response, Map<String, Object> dynamicProperties) {
// Get mapper for entity and client
Mapper<T> mapper = MAPPERS.get(response.getClass()).get(response.clientId);
// Perform entity -> map mapping
mapper.mapFromEntity(response, dynamicProperties);
}
public static void setDynamicProperty(Map<String, Object> dynamicProperties, String name, T response) {
// Get mapper for entity and client
Mapper<T> mapper = MAPPERS.get(response.getClass()).get(response.clientId);
// Perform map -> entity mapping
mapper.mapToEntity(dynamicProperties, name, response);
}
protected abstract void mapFromEntity(T response, Map<String, Object> dynamicProperties);
protected abstract void mapToEntity(Map<String, Object> dynamicProperties, String name, T response);
}
对于产品实体和客户CLIENT_1:
public class ProductMapperClient1 extends Mapper<Product> {
@Override
protected void mapFromEntity(Product response, Map<String, Object> dynamicProperties) {
// Actual mapping from Product and CLIENT_1 to map
dynamicProperties.put("supplier", response.customString1);
dynamicProperties.put("warehouse", response.customString2);
}
@Override
protected void mapToEntity(Map<String, Object> dynamicProperties, String name, Product response) {
// Actual mapping from map and CLIENT_1 to Product
String property = (String) dynamicProperties.get(name);
if ("supplier".equals(name)) {
response.customString1 = property;
} else if ("warehouse".equals(name)) {
response.customString2 = property;
}
}
}
想法是每个(实体,客户端)对都有一个特定的映射器。 如果您有许多实体和/或客户端,则可以考虑动态填充映射器的地图,也许从某些配置文件中读取并使用反射来读取实体的属性。
您是否考虑过返回Map <>作为响应? 还是响应的一部分,例如response.getUDF()。get(“ customStringX”))? 这应该为将来节省一些麻烦,例如:1000万并发用户意味着您的VM中有1000万个类。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.