我使用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"
}
答案 0 :(得分:3)
我认为你可以借助杰克逊的一些注释来做你需要的事情:
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
注释意在被杰克逊用于反序列化。
缺少的部分是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;
}
}
}
这个想法是每个(实体,客户)对都有一个特定的映射器。如果您有许多实体和/或客户端,那么您可以考虑动态填充映射器的映射,也可以从某个配置文件中读取并使用反射来读取实体的属性。
答案 1 :(得分:0)
您是否考虑过返回地图&lt;&gt;作为回应?或者响应的一部分,比如response.getUDF()。get(“customStringX”))?这可以为您节省一些可能的麻烦,例如:1000万并发用户意味着您的VM中有1000万个类。