Джексон и десериализация, когда ты заранее не знаешь имя тега JSON? - PullRequest
0 голосов
/ 27 июня 2018

Я хочу использовать Джексона, чтобы десериализовать мой 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_10240, на другой - custom_10345, поэтому я не могу жестко закодировать это в POJO. Используя другой вызов, можно узнать во время выполнения, до начала десериализации, как называется поле, но это невозможно во время компиляции.

Предполагая, что я хочу отобразить поле value в String на JiraFields с именем Importance, как мне это сделать? Или, может быть, проще, как отобразить этот Importance на JiraCustomField класс?

Ответы [ 2 ]

0 голосов
/ 29 июня 2018

После поиска я наконец нашел аннотацию JsonAlias ​​. Это все еще определяется во время компиляции, но у меня было кое-что, что я мог бы искать дальше!

Дальнейший поиск, и я нашел PropertyNamingStrategy , который позволяет вам переименовать то, какое имя поля 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;
  }
}
0 голосов
/ 27 июня 2018

Вы можете использовать метод, помеченный @JsonAnySetter, который принимает все свойства, которые не определены (и не проигнорированы). в случае объекта Json (например, настраиваемого поля в вопросе) Джексон передает Map, который содержит все свойства объекта (он может даже содержать значения 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());
            }
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...