杰克逊序列化/反序列化:动态属性和字段

时间:2015-07-19 20:04:48

标签: java json spring spring-mvc jackson

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

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

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

    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"
    }
    

2 个答案:

答案 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万个类。