将 HashMap 转换为包含列表属性的对象

Convert HashMap to Object containing list attributes

是否可以将这个HashMap转换为对应的Object?也许使用 Jackson Object Mapper、Gson,甚至是 Mapstruct。这里的技巧是如何映射 List 属性,在我的 INPUT 中有一个数字作为属性名称的后缀:

Map<String, String> map = new HashMap<String, String>() {{
   put("fooName", "foo name");
   put("bars1.barName", "bar at position 0 name");
   put("bars1.barValue", "bar at position 0 value");
   put("bars2.barName", "bar at position 1 name");
   put("bars2.barValue", "bar at position 1 value");
}};
      
public class Foo {
    
    String fooName;
    List<Bar> bars;
// getters/setters    
}
    
public class Bar {
    
    String barName;
    String barValue;
// getters/setters
}

PS: 此输入来自外部 API 调用,我无法修改源。

您可以使用中间哈希图进行转换。尝试下面的示例转换器。 备注:您可能希望为 Foo 和 Bar 使用适当的构造函数或使用 setter(为简单起见,我使用 public 字段)。而且没有错误处理。

static Foo convertMap(Map<String, String> map){
    Foo foo = new Foo();
    foo.fooName = map.remove("fooName");

    Map<String, Bar> barsMap = new HashMap<String, Bar>();

    for(String key : map.keySet()){
      String[] split = key.split("\.");
      String name = split[0];
      String attribute = split[1];

      Bar bar = null;
      if(barsMap.containsKey(name)){
        bar = barsMap.get(name);
      }
      else{
        bar = new Bar();
        barsMap.put(name, bar);
      }

      switch(attribute){
        case "barName": bar.barName = map.get(key); break;
        case "barValue": bar.barValue = map.get(key); break;
        default: break;
      }
    }

    foo.bars = new ArrayList(barsMap.values());

    return foo;
  }

您可以使用 @JsonAnySetter 注释以及 Bar 实例的映射:

public class Foo {

    String fooName;
    private Map<Integer, Bar> map = new TreeMap<>();

    // getters/setters    
    public String getFooName() {
        return fooName;
    }
    public void setFooName(String fooName) {
        this.fooName = fooName;
    }
    public List<Bar> getBars() {
        return map.values().stream().collect(Collectors.toList());
    }

    @JsonAnySetter
    public void bars(String key, String value) {
        String[] ids = key.split("\.");
        if (ids[0].startsWith("bars")) {
            Integer barKey = Integer.parseInt(ids[0].substring("bars".length())) - 1;
            String field = ids[1];
            Bar bar = map.computeIfAbsent(barKey, k -> new Bar());
            if ("barName".equals(field)) {
                bar.setBarName(value);
            } else if ("barValue".equals(field)) {
                bar.setBarValue(value);
            }
            map.put(barKey, bar);               
        }  // else handle other properties
    }
}

测试:

ObjectMapper m = new ObjectMapper();

Map<String, String> map = new HashMap<String, String>() {{
    put("fooName", "foo name");
    put("bars1.barName", "bar at position 0 name");
    put("bars1.barValue", "bar at position 0 value");
    put("bars2.barName", "bar at position 1 name");
    put("bars2.barValue", "bar at position 1 value");
}};

String json = m.writerWithDefaultPrettyPrinter().writeValueAsString(map);

Foo foo = m.readValue(json, Foo.class);

System.out.println("reserialized = " + m.writerWithDefaultPrettyPrinter().writeValueAsString(foo));

输出:

reserialized = {
  "fooName" : "foo name",
  "bars" : [ {
    "barName" : "bar at position 0 name",
    "barValue" : "bar at position 0 value"
  }, {
    "barName" : "bar at position 1 name",
    "barValue" : "bar at position 1 value"
  } ]
}