我创建了一个JIRA错误,并通过webhook将后续生成的事件发送到SpringBoot a。这到目前为止工作。在SpringBoot应用程序中,我尝试反序列化收到的JSON。为了实现这一点,我有一个IssueEvent类,它有一个Issue字段:
package my.package.domain.jira.entity;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.atlassian.jira.rest.client.api.domain.ChangelogGroup;
import com.atlassian.jira.rest.client.api.domain.Issue;
import com.atlassian.jira.rest.client.api.domain.User;
/**
* RootObject JSON
*
*/
public class IssueEvent implements Serializable {
/**
*
*/
private static final long serialVersionUID = 4467263405878138432L;
private Long timestamp;
private String webhookEvent;
@JsonProperty("issue_event_type_name")
private String issueEventTypeName;
private User user;
private Issue issue;
private ChangelogGroup changelog;
...
}
这是JIRA在创建错误/问题时生成的JSON:
{
"timestamp": 1516692474575,
"webhookEvent": "jira:issue_created",
"issue_event_type_name": "issue_created",
"user": {
"self": "http://localhost:9090/rest/api/2/user?username=bla",
"name": "bla",
"key": "abla",
"emailAddress": "a.b@xyz.com",
"avatarUrls": {
"48x48": "http://localhost:9090/secure/useravatar?avatarId=10336",
"24x24": "http://localhost:9090/secure/useravatar?size=small&avatarId=10336",
"16x16": "http://localhost:9090/secure/useravatar?size=xsmall&avatarId=10336",
"32x32": "http://localhost:9090/secure/useravatar?size=medium&avatarId=10336"
},
"displayName": "bla",
"active": true,
"timeZone": "Europe/Berlin"
},
"issue": {
"id": "10623",
"self": "http://localhost:9090/rest/api/2/issue/10623",
"key": "COVA-90",
"fields": {
"issuetype": {
"self": "http://localhost:9090/rest/api/2/issuetype/10004",
"id": "10004",
"description": "Ein Problem, das die Funktionen des Produkts beeinträchtigt oder verhindert.",
"iconUrl": "http://localhost:9090/secure/viewavatar?size=xsmall&avatarId=10303&avatarType=issuetype",
"name": "Bug",
"subtask": false,
"avatarId": 10303
},
"components": [],
"timespent": null,
"timeoriginalestimate": null,
"description": "dhmedhmed",
"project": {
"self": "http://localhost:9090/rest/api/2/project/10000",
"id": "10000",
"key": "COVA",
"name": "CoVA",
"avatarUrls": {
"48x48": "http://localhost:9090/secure/projectavatar?avatarId=10324",
"24x24": "http://localhost:9090/secure/projectavatar?size=small&avatarId=10324",
"16x16": "http://localhost:9090/secure/projectavatar?size=xsmall&avatarId=10324",
"32x32": "http://localhost:9090/secure/projectavatar?size=medium&avatarId=10324"
}
},
"fixVersions": [
{
"self": "http://localhost:9090/rest/api/2/version/10000",
"id": "10000",
"name": "Version 1.0",
"archived": false,
"released": true,
"releaseDate": "2017-11-29"
}
],
"aggregatetimespent": null,
"resolution": null,
"timetracking": {},
"customfield_10005": "0|i000gf:",
"attachment": [],
"aggregatetimeestimate": null,
"resolutiondate": null,
"workratio": -1,
"summary": "jrzj",
"lastViewed": null,
"watches": {
"self": "http://localhost:9090/rest/api/2/issue/COVA-90/watchers",
"watchCount": 0,
"isWatching": false
},
"creator": {
"self": "http://localhost:9090/rest/api/2/user?username=bla",
"name": "bla",
"key": "abla",
"emailAddress": "a.b@xyz.com",
"avatarUrls": {
"48x48": "http://localhost:9090/secure/useravatar?avatarId=10336",
"24x24": "http://localhost:9090/secure/useravatar?size=small&avatarId=10336",
"16x16": "http://localhost:9090/secure/useravatar?size=xsmall&avatarId=10336",
"32x32": "http://localhost:9090/secure/useravatar?size=medium&avatarId=10336"
},
"displayName": "bla",
"active": true,
"timeZone": "Europe/Berlin"
},
"subtasks": [],
"created": "2018-01-23T08:27:54.388+0100",
"reporter": {
"self": "http://localhost:9090/rest/api/2/user?username=bla",
"name": "bla",
"key": "abla",
"emailAddress": "a.b@xyz.com",
"avatarUrls": {
"48x48": "http://localhost:9090/secure/useravatar?avatarId=10336",
"24x24": "http://localhost:9090/secure/useravatar?size=small&avatarId=10336",
"16x16": "http://localhost:9090/secure/useravatar?size=xsmall&avatarId=10336",
"32x32": "http://localhost:9090/secure/useravatar?size=medium&avatarId=10336"
},
"displayName": "bla",
"active": true,
"timeZone": "Europe/Berlin"
},
"customfield_10000": null,
"aggregateprogress": {
"progress": 0,
"total": 0
},
"priority": {
"self": "http://localhost:9090/rest/api/2/priority/3",
"iconUrl": "http://localhost:9090/images/icons/priorities/medium.svg",
"name": "Medium",
"id": "3"
},
"labels": [
"Test",
"Bug"
],
"customfield_10004": [
"com.atlassian.greenhopper.service.sprint.Sprint@14d495fe[id=1,rapidViewId=1,state=ACTIVE,name=Sample Sprint 2,startDate=2017-11-30T02:29:06.805+01:00,endDate=2017-12-14T02:49:06.805+01:00,completeDate=<null>,sequence=1,goal=<null>]"
],
"environment": "dghndghmnedghmn",
"timeestimate": null,
"aggregatetimeoriginalestimate": null,
"versions": [
{
"self": "http://localhost:9090/rest/api/2/version/10000",
"id": "10000",
"name": "Version 1.0",
"archived": false,
"released": true,
"releaseDate": "2017-11-29"
}
],
"duedate": null,
"progress": {
"progress": 0,
"total": 0
},
"comment": {
"comments": [],
"maxResults": 0,
"total": 0,
"startAt": 0
},
"issuelinks": [
{
"id": "10421",
"self": "http://localhost:9090/rest/api/2/issueLink/10421",
"type": {
"id": "10000",
"name": "Blocks",
"inward": "is blocked by",
"outward": "blocks",
"self": "http://localhost:9090/rest/api/2/issueLinkType/10000"
},
"outwardIssue": {
"id": "10616",
"key": "COVA-83",
"self": "http://localhost:9090/rest/api/2/issue/10616",
"fields": {
"summary": "fj,t.uoöpuz.ötultl",
"status": {
"self": "http://localhost:9090/rest/api/2/status/10000",
"description": "",
"iconUrl": "http://localhost:9090/",
"name": "Aufgaben",
"id": "10000",
"statusCategory": {
"self": "http://localhost:9090/rest/api/2/statuscategory/2",
"id": 2,
"key": "new",
"colorName": "blue-gray",
"name": "Aufgaben"
}
},
"priority": {
"self": "http://localhost:9090/rest/api/2/priority/3",
"iconUrl": "http://localhost:9090/images/icons/priorities/medium.svg",
"name": "Medium",
"id": "3"
},
"issuetype": {
"self": "http://localhost:9090/rest/api/2/issuetype/10004",
"id": "10004",
"description": "Ein Problem, das die Funktionen des Produkts beeinträchtigt oder verhindert.",
"iconUrl": "http://localhost:9090/secure/viewavatar?size=xsmall&avatarId=10303&avatarType=issuetype",
"name": "Bug",
"subtask": false,
"avatarId": 10303
}
}
}
}
],
"votes": {
"self": "http://localhost:9090/rest/api/2/issue/COVA-90/votes",
"votes": 0,
"hasVoted": false
},
"worklog": {
"startAt": 0,
"maxResults": 20,
"total": 0,
"worklogs": []
},
"assignee": null,
"updated": "2018-01-23T08:27:54.388+0100",
"status": {
"self": "http://localhost:9090/rest/api/2/status/10000",
"description": "",
"iconUrl": "http://localhost:9090/",
"name": "Aufgaben",
"id": "10000",
"statusCategory": {
"self": "http://localhost:9090/rest/api/2/statuscategory/2",
"id": 2,
"key": "new",
"colorName": "blue-gray",
"name": "Aufgaben"
}
}
}
},
"changelog": {
"id": "10742",
"items": [
{
"field": "Link",
"fieldtype": "jira",
"from": null,
"fromString": null,
"to": "COVA-83",
"toString": "This issue blocks COVA-83"
}
]
}
}
由于JIRA类没有无参数的默认构造函数,因此我尝试使用 Mixins 。它适用于User类,但似乎不适用于Issue类:
package my.package.domain.jira.util;
import java.net.URI;
import java.util.Collection;
import java.util.Set;
import org.joda.time.DateTime;
import com.atlassian.jira.rest.client.api.domain.Attachment;
import com.atlassian.jira.rest.client.api.domain.BasicComponent;
import com.atlassian.jira.rest.client.api.domain.BasicPriority;
import com.atlassian.jira.rest.client.api.domain.BasicProject;
import com.atlassian.jira.rest.client.api.domain.BasicVotes;
import com.atlassian.jira.rest.client.api.domain.BasicWatchers;
import com.atlassian.jira.rest.client.api.domain.ChangelogGroup;
import com.atlassian.jira.rest.client.api.domain.Comment;
import com.atlassian.jira.rest.client.api.domain.IssueField;
import com.atlassian.jira.rest.client.api.domain.IssueLink;
import com.atlassian.jira.rest.client.api.domain.IssueType;
import com.atlassian.jira.rest.client.api.domain.Operations;
import com.atlassian.jira.rest.client.api.domain.Resolution;
import com.atlassian.jira.rest.client.api.domain.Status;
import com.atlassian.jira.rest.client.api.domain.Subtask;
import com.atlassian.jira.rest.client.api.domain.TimeTracking;
import com.atlassian.jira.rest.client.api.domain.User;
import com.atlassian.jira.rest.client.api.domain.Version;
import com.atlassian.jira.rest.client.api.domain.Worklog;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
*
*/
public abstract class IssueMixin {
@JsonCreator
public IssueMixin(@JsonProperty("summary") String summary, @JsonProperty("self") URI self, @JsonProperty("key") String key,
@JsonProperty("id") Long id, @JsonProperty("project") BasicProject project, @JsonProperty("issueType") IssueType issueType,
@JsonProperty("status") Status status, @JsonProperty("description") String description, @JsonProperty("priority") BasicPriority priority,
@JsonProperty("resolution") Resolution resolution, @JsonProperty("attachments") Collection<Attachment> attachments, @JsonProperty("reporter") User reporter,
@JsonProperty("assignee") User assignee, @JsonProperty("creationDate") DateTime creationDate, @JsonProperty("updateDate") DateTime updateDate,
@JsonProperty("dueDate") DateTime dueDate, @JsonProperty("affectedVersion") Collection<Version> affectedVersion,
@JsonProperty("fixVersions") Collection<Version> fixVersions, @JsonProperty("components") Collection<BasicComponent> components,
@JsonProperty("timeTracking") TimeTracking timeTracking, @JsonProperty("fields") Collection<IssueField> issueFields,
@JsonProperty("comments") Collection<Comment> comments, @JsonProperty("transitionsUri") URI transitionsUri,
@JsonProperty("issueLinks") Collection<IssueLink> issueLinks, @JsonProperty("votes") BasicVotes votes, @JsonProperty("worklogs") Collection<Worklog> worklogs,
@JsonProperty("watchers") BasicWatchers watchers, @JsonProperty("expandos") Iterable<String> expandos, @JsonProperty("subtasks") Collection<Subtask> subtasks,
@JsonProperty("changelog") Collection<ChangelogGroup> changelog, @JsonProperty("operations") Operations operations, @JsonProperty("labels") Set labels ) {}
}
这个Mixin与包com.atlassian.jira.rest.client.api.domain中的类Issue的构造函数完全匹配。
我得到以下例外:
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
at [Source:
{...JSON from above...}; line: 1, column: 709] (through reference chain: my.package.domain.jira.entity.IssueEvent["issue"]->com.atlassian.jira.rest.client.api.domain.Issue["fields"])
似乎从JIRA生成的JSON与其自己的类Issue不匹配。
Issue类周围的JSON包含更多代码(可能是com.atlassian.jira.event.issue.IssueEvent类)。首先我尝试自己编写这个POJO,但我更喜欢使用JIRA域类......但是如何引用这个类。 Trynig使用jira-api依赖失败,缺少jta 1.0.1依赖。
我该如何解决这个问题?
好。我发现我必须排除jira-api依赖项内部使用的JTA依赖项:
<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-api</artifactId>
<version>${jira.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>jta</groupId>
<artifactId>jta</artifactId>
</exclusion>
</exclusions>
</dependency>
我还不知道如何将JSON解码/反序列化为JIRA域对象......