我想使用Jackson将来自Jira的JSON反序列化为一组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
的{{1}}上的JiraFields
,该怎么做?或更简单,如何将此Importance
映射到Importance
类上?
答案 0 :(得分:1)
您可以使用带有@JsonAnySetter
注释的方法,该方法接受所有未定义(且不会忽略)的属性。对于Json对象(如问题中的自定义字段),Jackson传递了一个Map
,其中包含所有Object属性(对于嵌套对象,它甚至可能包含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());
}
}
}
}
答案 1 :(得分:1)
进一步搜索后,我终于找到了JsonAlias annotation。这仍然是在编译时定义的,但是我可以进一步搜索!
进一步搜索后,我发现了PropertyNamingStrategy,它使您可以重命名setter / field所期望的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;
}
}