【问题标题】:Deserialize JSON strings with different formats to instances of the same Java class将不同格式的 JSON 字符串反序列化为同一个 Java 类的实例
【发布时间】:2013-10-17 16:46:35
【问题描述】:

使用 Jackson 将不同格式的 JSON 字符串反序列化为同一 Java 类的实例的最佳方法是什么?

例如,我有从不同来源获取的用户信息:

格式一:

"user" : {
  "name"     : "John",
  "age"      : 21,
  "email"    : "john@mail.com",
  "location" : "NYC"
}

格式2:

{
  "user" : "John",
  "mail" : "john@mail.com",
  "age"  : "21"
}

格式 3:

{
  "who"      : "John",
  "contacts" : "john@mail.com",
  "age"      : 21
}

我想将所有这些字符串反序列化为以下类的实例:

public class User {
  public String name;
  public String email;
  public int age;
}

也许有一种方法可以通过 Map 而不是注释来定义从 JSON 字段到 Java 字段的映射?

【问题讨论】:

    标签: java json jackson deserialization


    【解决方案1】:

    对于第一部分,无论"user" : 是否应该是 JSON 字符串的一部分,您都可以使用 Java 中的普通字符串操作来操作它。

    第二部分,关于将mail映射到email等,可以使用setter

    public class User {
      public String name;
      public String email;
      public int age;
    
      public void setMail(String value) {
         this.email = value;
      }
    }
    

    这里的setMail-方法将被 Jackson 检测到,并将在示例 2 中为 mail 属性调用。您可以为您想要的所有映射添加此类设置方法(联系人、用户、谁) .

    【讨论】:

    • 您建议的方法看起来不错,但我担心如果要呈现更多不同的格式,我将不得不添加数十亿个设置器。 :)
    • @Filipp 你能想出多少种“邮件”、“姓名”和“年龄”的变体?
    • 目前我有大约 5 或 6 种组合。
    【解决方案2】:

    将源数据直接反序列化为单一目标格式会很快变得有点麻烦且容易出错。我建议为不同的源格式定义单独的类,实现一个指定getUser() 方法的接口。这种方法会产生你想要的目标格式。

    这样您就可以在一个类中拥有特定于不同提供程序的所有代码,并且可以在需要时轻松添加新的提供程序。

    【讨论】:

    • 您已经描述了我当前的实现。我同意添加新提供者很容易引用,但与字段名称映射规范相比,它需要更多的工作。
    【解决方案3】:

    我研究了 Jackson 文档并找到了两个解决方案。

    这是我的 Java 类:

    public class User {
        protected String name;
        protected String email;
        protected int age;
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void setEmail(String email) {
            this.email = email;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String toString() {
            return name + ": [ " + email + ", " + age + " ]";
        }
    }
    

    第一个解决方案是创建自定义 PropertyNamingStrategy:

    public class MappingPropertyNamingStrategy extends PropertyNamingStrategy {
    
        Map<String, String> nameMap = new HashMap<String, String>();
    
        public void setMap(Map<String, String> map) {
            nameMap = map;
        }
    
        @Override
        public String nameForGetterMethod(MapperConfig<?> cfg,
                                          AnnotatedMethod method,
                                          String defaultName) {
            return mapName(defaultName);
        }
        @Override
        public String nameForSetterMethod(MapperConfig<?> cfg,
                                          AnnotatedMethod method,
                                          String defaultName) {
            return mapName(defaultName);
        }
        @Override
        public String nameForField(MapperConfig<?> cfg,
                                   AnnotatedField field,
                                   String defaultName) {
            return mapName(defaultName);
        }
    
        protected String mapName(String name) {
            if (nameMap.containsKey(name)) {
                return nameMap.get(name);
            } else {
                return name;
            }
        }
    }
    

    然后您可以定义从用户字段到相应 JSON 字段的映射:

        String json1 = "{ \"user\": { \"name\": \"John\", \"age\": 21, \"email\": \"john@mail.com\", \"location\": \"NYC\" }}";
        String json2 = "{ \"user\": \"John\", \"mail\": \"john@mail.com\", \"age\": \"21\" }";
        String json3 = "{ \"who\": \"John\", \"contacts\": \"john@mail.com\", \"age\": 21 }";
    
        ObjectMapper mapper = new ObjectMapper();
        MappingPropertyNamingStrategy namingStrategy = new MappingPropertyNamingStrategy();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    
        Map<String, User> res = mapper.readValue(json1, new TypeReference<Map<String, User>>() {});
        System.out.println(res.get("user"));
    
    
        mapper = new ObjectMapper();
        mapper.setPropertyNamingStrategy(namingStrategy);
        namingStrategy.setMap(new HashMap<String, String>() {{
                    put("name", "user");
                    put("email", "mail");
                }});
        System.out.println(mapper.readValue(json2, new TypeReference<User>(){}));
    
    
        mapper = new ObjectMapper();
        mapper.setPropertyNamingStrategy(namingStrategy);
        namingStrategy.setMap(new HashMap<String, String>() {{
                    put("name", "who");
                    put("email", "contacts");
                }});
    
        System.out.println(mapper.readValue(json3, new TypeReference<User>(){}));
    

    另一种解决方案是使用 mixins:

    @JsonIgnoreProperties({"location"})
    abstract class FirstFormat {
    }
    
    abstract class SecondFormat {
        @JsonProperty("user")
        public abstract void setName(String name);
        @JsonProperty("mail")
        public abstract void setEmail(String email);
        public abstract void setAge(int age);
    }
    
    abstract class ThirdFormat {
        @JsonProperty("who")
        public abstract void setName(String name);
        @JsonProperty("contacts")
        public abstract void setEmail(String email);
        public abstract void setAge(int age);
    }
    

    然后你只需要将它与 User 类关联起来:

        mapper = new ObjectMapper();
        mapper.addMixInAnnotations(User.class, FirstFormat.class);
        Map<String, User> res = mapper.readValue(json1, new TypeReference<Map<String, User>>() {});
        System.out.println(res.get("user"));        
    
        mapper = new ObjectMapper();
        mapper.addMixInAnnotations(User.class, SecondFormat.class);
        System.out.println(mapper.readValue(json2, new TypeReference<User>(){}));
    
        mapper = new ObjectMapper();
        mapper.addMixInAnnotations(User.class, ThirdFormat.class);
        System.out.println(mapper.readValue(json3, new TypeReference<User>(){}));
    

    我认为,mixins 是最好的解决方案,因为它提供了更多的控制。

    【讨论】:

    • 你所说的“混合”实际上被称为“注释”。总之,挖的好!我挖了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-31
    相关资源
    最近更新 更多