繁体   English   中英

Jackson序列化/反序列化:动态属性和字段

[英]Jackson Serialization / Deserialization: Dynamic properties and fields

我使用Spring MVC来驱动当前正在使用的应用程序的API。 API响应的序列化是通过Jackson的ObjectMapper完成的。 我面临以下情况,我们正在扩展一些对象以支持UserDefinedFields(UDF) ,这在下面的抽象UserDefinedResponse 作为SaaS解决方案,多个客户端具有不同的配置,这些配置存储在数据库中用于其自定义字段。

这个问题的目标是能够使用其UDF数据响应每个客户端。 这将需要

  1. 将字段customString1customString2 ,...动态重命名为其相应的UDF标签
  2. 删除未定义的UDF字段(示例客户端仅使用4个字段中的2个。

抽象响应示例

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM