代码之家  ›  专栏  ›  技术社区  ›  Paul Wagland

jackson和反序列化当你不知道json标记名的时候?

  •  1
  • Paul Wagland  · 技术社区  · 6 年前

    我想使用jackson将我的json从jira反序列化为一组pojo。我有很多我想要的工作漂亮,现在我只需要解码自定义字段值。

    我的输入json看起来像:

    {
      "expand": "renderedFields,names,schema,operations,editmeta,changelog,versionedRepresentations",
      "id": "104144",
      "self": "https://jira.internal.net/rest/api/2/issue/104144",
      "key": "PRJ-524",
      "fields": {
        "summary": "Redo unit tests to load from existing project",
        "components": [],
        "customfield_10240": {
          "self": "https://jira.internal.net/rest/api/2/customFieldOption/10158",
          "value": "Normal",
          "id": "10158"
        }
    }
    

    我可以轻松地加载摘要和组件,因为我提前知道这些json元素的名称,并且可以在我的pojo中定义它们:

    @JsonIgnoreProperties({ "expand", "self", "id", })
    public class JiraJson
    {
      private JiraFields fields;
      private String key;
    
      public JiraFields getFields()
      {
        return fields;
      }
    
      public String getKey()
      {
        return key;
      }
    
      public void setFields(JiraFields newFields)
      {
        fields = newFields;
      }
    
      public void setKey(String newKey)
      {
        key = newKey;
      }
    }
    

    同样地 JiraFields 以下内容:

    @JsonIgnoreProperties({ "issuetype", "priority", "status" })
    public class JiraFields
    {
      private List<JiraComponent> components;
      private String summary;
    
      public List<JiraComponent> getComponents()
      {
        return components;
      }
    
      public String getSummary()
      {
        return summary;
      }
    
      public void setComponents(List<JiraComponent> newComponents)
      {
        components = newComponents;
      }
    
      public void setSummary(String newSummary)
      {
        summary = newSummary;
      }
    }
    

    然而,这个领域 custom_10240 实际上,根据运行的是哪个jira系统而有所不同 自定义 ,另一个是 custom_10345 ,所以我不能硬编码到pojo中。使用另一个调用,可以在运行时,在反序列化开始之前知道字段的名称是什么,但这在编译时是不可能的。

    假设我想绘制 value 将字段转换为 String 吉拉菲尔德 打电话 Importance ,我该怎么做呢?或者更简单的是,如何映射这个 重要性 到一个 JiraCustomField 上课?

    2 回复  |  直到 6 年前
        1
  •  1
  •   Sharon Ben Asher    6 年前

    可以使用注释为 @JsonAnySetter 接受所有未定义(且不被忽略)的属性。如果是json对象(如问题中的自定义字段),jackson将传递一个 Map 包含所有对象属性的 地图 嵌套对象的值)。现在可以在运行时提取所需的任何属性:

    @JsonIgnoreProperties({ "issuetype", "priority", "status" })
    public class JiraFields
    {
        private List<JiraComponent> components;
        private String summary;
        private String importance;
    
        // getter/setter omitted for brevity
    
        @JsonAnySetter
        public void setCustomField(String name, Object value) {
            System.out.println(name);  // will print "customfield_10240"
            if (value instanceof Map) {  // just to make sure we got a Json Object
                Map<String, Object> customfieldMap = (Map<String, Object>)value;
                if (customfieldMap.containsKey("value")) {  // check if object contains "value" property
                    setImportance(customfieldMap.get("value").toString());
                }
            }
        }
    }
    
        2
  •  1
  •   Paul Wagland    6 年前

    经过进一步搜索,我终于找到了 JsonAlias annotation 是的。这仍然是在编译时定义的,但是我有一些可以进一步搜索的东西!

    进一步搜索,我发现 PropertyNamingStrategy ,它允许您重命名setter/字段所需的json字段名。这样做的好处是通过一个方法完成,并且类可以在运行时构造。

    下面是我用来执行此映射的类:

    import java.util.Map;
    import java.util.stream.Collectors;
    
    import com.fasterxml.jackson.databind.PropertyNamingStrategy;
    import com.fasterxml.jackson.databind.cfg.MapperConfig;
    import com.fasterxml.jackson.databind.introspect.AnnotatedField;
    import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
    
    public final class CustomFieldNamingStrategy
      extends PropertyNamingStrategy
    {
      private static final long serialVersionUID = 8263960285216239177L;
      private final Map<String, String> fieldRemapping;
      private final Map<String, String> reverseRemapping;
    
      public CustomFieldNamingStrategy(Map<String, String> newFieldRemappings)
      {
        fieldRemapping = newFieldRemappings;
        reverseRemapping = fieldRemapping.entrySet()//
                                         .stream()//
                                         .collect(Collectors.toMap(Map.Entry::getValue,
                                                                   Map.Entry::getKey));
      }
    
      @Override
      public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName)
      {
        if (field.getDeclaringClass().getName().equals(JiraFields.class.getName()))
        {
          return reverseRemapping.getOrDefault(defaultName, defaultName);
        }
        return defaultName;
      }
    
      @Override
      public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method,
                                        String defaultName)
      {
        if (method.getDeclaringClass().getName().equals(JiraFields.class.getName()))
        {
          return reverseRemapping.getOrDefault(defaultName, defaultName);
        }
        return defaultName;
      }
    
      @Override
      public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method,
                                        String defaultName)
      {
        if (method.getDeclaringClass().getName().equals(JiraFields.class.getName()))
        {
          return reverseRemapping.getOrDefault(defaultName, defaultName);
        }
        return defaultName;
      }
    }