【问题标题】:Jackson, howto deserialize fields with custom name?杰克逊,如何反序列化具有自定义名称的字段?
【发布时间】:2017-09-02 22:10:30
【问题描述】:

我尝试将 Jira 问题从 REST API 反序列化为一个对象。这很简单。我挣扎的地方是将 Jira 中的自定义字段映射到属性上。我试过使用自定义反序列化器,但它没有“启动”。

REST 调用中的 Json 如下所示: (部分被剥离)

{
  "expand": "renderedFields,names,schema,...",
  "id": "53899",
  "key": "DPT-12",
  "fields": {
    "issuetype": {
      "id": "10001",
      "name": "Story",
      "subtask": false
    },
    "timespent": null,
    "project": {
      "id": "10823",
      "key": "DPT"
    },
    "fixVersions": [],
    "customfield_10111": null,
    "aggregatetimespent": null,
    "resolution": null,
    "customfield_10112": null,
    "customfield_10700": [
      "entwicklung-w"
    ],
    "customfield_10304": null,
    "resolutiondate": null,
    "lastViewed": "2017-04-04T14:34:19.868+0200",
    "created": "2017-02-02T12:01:31.443+0100",
    "priority": {
      "name": "Schwer",
      "id": "10001"
    },
    "assignee": {
      "displayName": "me :-)"
    },
    "updated": "2017-04-04T14:34:19.710+0200",
    "status": {
      "iconUrl": "https://jira.mobi.ch/",
      "name": "Backlog",
      "statusCategory": {
        "name": "Aufgaben"
      }
    },
    "summary": "Ereignisse in rocket Chat schreiben",
    "creator": {
      "displayName": "me :-)"
    },
    "reporter": {
      "displayName": "me :-)"
    }
  }
}

自定义字段名称在我的应用程序中配置(“customfield_10700”),我想将其映射到属性上:

private Set<String> deploymentEnvironments;

这里是相关的 Dto 和测试类(这里去掉了 getter 和 setter)。

测试:

import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.hamcrest.collection.IsEmptyCollection.empty;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNot.not;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertThat;

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Set;

import com.fasterxml.jackson.databind.module.SimpleModule;
import org.junit.Test;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

public class IssueFieldsWithDeserializerTest {

    @Test
    public void testJiraResponseDeserializer() throws IOException, URISyntaxException {
        // arrange
        String deploymentEnvsKey = "customfield_10700";

        String json = new String(Files.readAllBytes(Paths.get(getClass().getClassLoader().getResource("jira-example-issue-with-customfield-poc.json").toURI())));

        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        SimpleModule module = new SimpleModule();
        module.addDeserializer(Set.class, new CustomFieldDeserializer(deploymentEnvsKey));
        mapper.registerModule(module);


        // act
        IssueResponsePoc issue = mapper.readValue(json, IssueResponsePoc.class);


        // assert
        assertThat("issue is not null", issue, is(not(nullValue())));
        assertThat("fields are not null", issue.getFields(), is(not(nullValue())));
        assertThat("custom field is not null", issue.getFields().getDeploymentEnvironments(), is(not(nullValue())));
        assertThat("custom field is not empty", issue.getFields().getDeploymentEnvironments(), is(not(empty())));
        assertThat("custom field has one value", issue.getFields().getDeploymentEnvironments(), hasSize(1));
    }

}

IssueResponsePoc 类:

import java.io.Serializable;

import com.fasterxml.jackson.annotation.JsonProperty;

public class IssueResponsePoc implements Serializable {

    @JsonProperty private String id;
    @JsonProperty private String key;

    @JsonProperty private IssueFieldsPoc fields;

}

有趣的类:IssueFieldsPoc

import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Set;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

import com.fasterxml.jackson.annotation.JsonProperty;

public class IssueFieldsPoc implements Serializable {

    @JsonProperty private String summary;
    @JsonProperty private IssueType issuetype;
    @JsonProperty private IssueUser creator;
    @JsonProperty private Date created;
    @JsonProperty private IssueUser reporter;
    @JsonProperty private IssuePriority priority;
    @JsonProperty private IssueResolution resolution;
    @JsonProperty private List<String> labels;
    @JsonProperty private Date resolutiondate;
    @JsonProperty private IssueUser assignee;
    @JsonProperty private Date updated;
    @JsonProperty private IssueStatus status;

    @JsonDeserialize private Set<String> deploymentEnvironments;
    // @JsonDeserialize(using = CustomFieldDeserializer.class) private Set<String> deploymentEnvironments;

    public Set<String> getDeploymentEnvironments() {
        return deploymentEnvironments;
    }

    public void setDeploymentEnvironments(Set<String> deploymentEnvironments) {
        this.deploymentEnvironments = deploymentEnvironments;
    }
}

我的反序列化器:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;

public class CustomFieldDeserializer extends StdDeserializer<Set<String>> {

    private final String customFieldName;

    public CustomFieldDeserializer(String customFieldName) {
        super((Class<?>) null);
        this.customFieldName = customFieldName;
    }

    @Override
    public Set<String> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        System.out.println("deserializer started!");
        return null;
    }

    @Override
    public Collection<Object> getKnownPropertyNames() {
        return Collections.singletonList(customFieldName);
    }
}

我尝试注册一个自定义反序列化器,但它没有启动,我怀疑它被忽略了,因为杰克逊无法识别字段名称。添加“getKnownPropertyNames”方法没有帮助。由于我需要将 jira 自定义字段名称(我从配置中读取)放在某处,因此我尝试将其放入反序列化器中。使用杰克逊注解@JsonDeserialize。

我还尝试将它包装到另一个类中,而不是直接使用 Set 来获得更强的输入。也没有运气。

我也尝试在注解中配置反序列化器,但这需要默认构造函数,我无法再配置 jira 自定义字段名称。

目前的解决方案使用@JsonAnySetter注解:

@JsonAnySetter
public void setCustomProperty(String name, Object value) {
    if(StringUtils.startsWith(name, "customfield_")) {
        this.customFields.put(name, value);
    }
}

但我更喜欢在反序列化器中使用该逻辑。

有没有办法帮助 jackson 何时为这个动态属性名称启动反序列化器(因为它知道属性名称)?

更新: 将模块注册到映射器。 正如答案中所建议的那样,将确切的属性名称添加到字段中:

@JsonProperty("customfield_10700")
@JsonDeserialize
private Set<String> deploymentEnvironments;

将允许反序列化程序启动。但如上所述,这是一个可配置的值,我不能(或者我不想)直接放入映射代码中。

【问题讨论】:

  • 看起来您没有将持有 CustomFieldDeserializer module 注册到 IssueFieldsWithDeserializerTest 中的 ObjectMapper 实例。我认为您应该在尝试读取值之前致电mapper.registerModule(module);
  • 谢谢。那当然是非常真实的。我添加了代码来注册模块。反序列化器仍然没有运气。似乎属性名称确实具有相当的相关性。我不知道如何告诉设置解串器告诉杰克逊它负责哪些字段。

标签: java json jackson


【解决方案1】:

我认为您的问题可以通过将@JsonProperty("customfield_10700") 设置为字段deploymentEnvironments 来解决,如下所示。在这种情况下,您不需要自定义反序列化器。

public class IssueFieldsPoc implements Serializable {

    @JsonProperty private String summary;
    @JsonProperty private Date created;
    @JsonProperty private List<String> labels;
    @JsonProperty private Date resolutiondate;

    @JsonProperty private Date updated;

    @JsonProperty("customfield_10700")
    private Set<String> deploymentEnvironments;

【讨论】:

  • 当然可以,但是该属性的值是可配置的,并且从后端更改为后端。所以“customfield_10700”是在运行时从配置中读取的,不能作为常量使用。
【解决方案2】:

好吧,如果我理解正确的话,你需要将json转换为java对象。

如果您希望该类忽略未知属性,则需要将 @JsonIgnoreProperties(ignoreUnknown = true) 添加到必须忽略的类中(仅限 IssueResponsePocIssueFieldsPoc 也一样)。

@JsonProperty(value = &lt;name_of_property_in_json&gt;) 将允许您在 java 类中为您的字段使用任何名称。

如果您通过带有相应注释(@JsonProperty@JsonIgnore 等)的 java 类重复嵌套级别的 json,则不需要整个使用反序列化器。

如果你想在你的类中处理未知字段,你可以使用@JsonAnySetter来达到这个目的

【讨论】:

  • 不,我认为你的问题错了。这已经是我问题的一部分了。
猜你喜欢
  • 2023-04-07
  • 2020-11-30
  • 1970-01-01
  • 2017-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多