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

Java Jackson序列化/反序列化:动态属性和字段,java,json,spring,spring-mvc,jackson,Java,Json,Spring,Spring Mvc,Jackson,我使用SpringMVC来驱动当前正在使用的应用程序的API。API响应的序列化是通过Jackson的ObjectMapper完成的。我面临以下情况,我们正在扩展许多对象以支持UserDefinedFields(UDF),如下面的摘要UserDefinedResponse所示。作为一个SaaS解决方案,多个客户端具有不同的配置,这些配置存储在数据库中,用于其自定义字段 这个问题的目标是能够用他们的UDF数据对每个客户做出响应。这需要 动态重命名字段customString1,customStri

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

这个问题的目标是能够用他们的UDF数据对每个客户做出响应。这需要

  • 动态重命名字段
    customString1
    customString2
    。。。到相应的UDF标签
  • 删除未定义的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
      =
      “供应商”
    • customString2
      =
      “仓库”
    为该客户序列化
    产品
    ,应产生类似的结果:

    {
       "id" : 1234,
       "name" : "MacBook Air",
       "price" : 1299,
       "supplier" : "Apple",
       "warehouse" : "New York warehouse"
    }
    

    您考虑过将Map作为响应返回吗?或者是响应的一部分,比如response.getUDF().get(“customStringX”)?这将为您在将来节省一些可能的麻烦,例如:1000万并发用户意味着您的VM中有1000万个类。

    我认为您可以借助一些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);
        }
    }
    
    对于产品实体和客户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;
            }
        }
    }
    
    公共类ProductMapPerClient 1扩展了映射器{
    @凌驾
    受保护的void mapFromEntity(产品响应、映射动态属性){
    //从产品和客户_1到映射的实际映射
    dynamicProperties.put(“供应商”,response.customString1);
    dynamicProperties.put(“仓库”,response.customString2);
    }
    @凌驾
    受保护的void映射实体(映射动态属性、字符串名称、产品响应){
    //从地图和客户_1到产品的实际映射
    字符串属性=(字符串)dynamicProperties.get(名称);
    如果(“供应商”。等于(名称)){
    response.customString1=属性;
    }否则,如果(“仓库”。等于(名称)){
    response.customString2=属性;
    }
    }
    }
    

    其思想是每个(实体、客户机)对都有一个特定的映射器。如果您有许多实体和/或客户端,那么您可以考虑动态地填充映射器的映射,或者从一些配置文件中读取并使用反射来读取实体的属性。但是,当某些API响应在代码的不同部分被重用时,使用该类将强制并标准化响应。至于多个类,我看不出它们是如何链接到并发用户的。我会在运行时更改根据请求和客户机返回的字段。JVM将类加载到内存中,以便它们消耗堆空间-Classloader也是一个对象。我假设您的用例将基于“动态”生成的类。在最坏的情况下,它可能会导致内存泄漏和性能错误-以下是关于它的文章。感谢Federico的回答。我会试试看,今天再给你回复。工作很有魅力。解决方案在@JsonAnyGetter中
    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;
            }
        }
    }