How to deserialize comments from JIRA webhook correctly

626 views Asked by At

I am currently deserializing a webhook URL from JIRA but I am having trouble with deserializing the comments part of the issue in a controller in a MVC application.

Currently in I am deserializing everything correctly except for the comments part of the Json payload.

This is a view of what the comments looks like in the JSON Payload:

'comments':[
{'id':'71980','displayName':'Ciaran','active':true},'created':'2015-06-10T09:30:07.983+0100','updated':'2015-06-10T09:30:07.983+0100'},
{'id':'72026','displayName':'Ciaran ','active':true},'created':'2015-06-10T14:50:34.253+0100','updated':'2015-06-10T14:50:34.253+0100'}]

Using Json2CSharp and then copying the classes I needed and the Comments looked like this:

Then the classes look like this:

Public Class Rootobject
     Public Property expand As String
     Public Property id As String
     Public Property self As String
     Public Property key As String
     Public Property fields As Fields
End Class     

Public Class Fields
     Public Property comment As Comment
End Class

Public Class Comment
     Public Property startAt As Integer
     Public Property maxResults As Integer
     Public Property total As Integer
     Public Property comments As List(Of Comment2)
End Class

Public Class Comment2
     Public Property body As String
     Public Property created As Date
     Public Property updated As Date
End Class

I am unsure about how I would deserialize this properly I tried doing this:

Dim reader As System.IO.StreamReader = New System.IO.StreamReader(HttpContext.Request.InputStream)
Dim rawSendGridJSON As String = reader.ReadToEnd()
Dim issue As Rootobject = JsonConvert.DeserializeObject(Of Rootobject)(rawSendGridJSON)

This works for everything else apart from the comments and I am getting this error message:

Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'class.Comment1' because the type requires a JSON object(e.g.) {"name":"value"}) to deserialize correctly.

How should I deserialize this correctly?

The rootObject Json looks like this :

"expand":"renderedFields,names,schema,transitions,operations,editmeta,changelog,
"id": "41948",
"self": "http://jira:8080/rest/api/latest/issue/41948",
"key": "OP-155",
"fields": {
        "comment": {
        "startAt": 0,
        "maxResults": 9,
        "total": 9,
        "comments": []
                   } 
          }

I can successfully get the ID or key from the rootObject but in the "fields" section getting the comments will give me the error. But I can get information from the "fields" section e.g I can display the summary or created.

The problem with the number of comments e.g there are nine and the way I am deserializing is not correct to handle this.


try this link the problem is with the comment, that contains the json paylaod it is verified

2

There are 2 answers

0
Ňɏssa Pøngjǣrdenlarp On BEST ANSWER

The json provided in the commentary link does not match the classes in the question. For instance, your RootObject class does not match the container at all, but instead is mostly the Issue class except for the "expand" property:

{
"timestamp": 1437140952183,
"webhookEvent": "jira:issue_updated",
"user": {
    ...
  ,
    "displayName": "Ciaran",
    "active": true,
    "timeZone": "Europe/London"
},
"issue": {...
 },
"comment": {...
}...

Based on the jsfiddle json, the root (which can be renamed) should defined as:

Public Class CiaranObj
    Public Property timestamp As Long
    Public Property webhookEvent As String
    Public Property user As Actor
    Public Property issue As Issue
    Public Property comment As CommentItem
End Class

Important Note: The jsfiddle sample provided may or may not be complete. There are a number of undefined Types in the various classes. Fields for instance has several Types with nulls resulting in:

Property customfield_10070 As Object

Object may not be the best Type when data is present. Likewise, TimeTracking is wholly undefined.

Apparently, you are not interested in anything but the comments. If true, you could just parse the json. If you look at the formatted json in something like jsonlint.com, you can see the relationships. So, to just get the comments:

Dim jstr =  ' from whereever

' extract comments to reduce typing and typos
Dim comments = myJ("issue")("fields")("comment")
' get the comment count
Dim CommentCount = Convert.ToInt32(comments("maxResults"))
'print them
For n As Int32 = 0 To CommentCount - 1
    Console.WriteLine("ID: {0}, Created: {1} upd Author: {2}",
                      comments("comments")(n)("id").ToString,
                      comments("comments")(n)("created").ToString,
                      comments("comments")(n)("author")("displayName").ToString)
Next

Output:

ID: 72430, Created: 6/16/2015 8:48:52 AM upd Author: Ciaran ID: 72431, Created: 6/16/2015 9:02:16 AM upd Author: Ciaran ID: 72661, Created: 6/18/2015 9:58:12 AM upd Author: Ciaran

As you may have noticed in the formatted JsonLint display, the comments are actually buried somewhat deeper than the root. Perhaps there was some other processing similar to the above, but it isnt mentioned.

To deserialize to an object, you need to use classes. You can drop any properties you do not care about, but you cant migrate properties from one class to another. Using any of the robots -- json2csharp, jsonutils.com1 which creates VB classes or even Visual Studio 2012 or later (Edit - Paste Special - Paste as Json Classes -- to auto-create the classes, you may want to take a moment to "normalize" the classes.

  • The User class repeats over and over for User, Assignee, Creator etc. I reduced it to one Type (Class) named Actor.
  • AvatarUrls also repeats over and over as AvatarUrls1 etc. You only need one Type.
  • There is a Comment Type in Fields
  • Comment also includes a collection of comment items, I renamed these CommentItem
  • CommentItem is also used in the root object, but the robots will call it Comment2 This is what you show in the first json snippet
  • The comments property can be defined as an array or a List<T> as shown

Keep in mind, you can remove any property you dont care about (but you cannot move or simply rename them). Which I did:

Public Class CiaranObj
    Public Property timestamp As Long
    Public Property webhookEvent As String
    Public Property user As Actor
    Public Property issue As Issue
    Public Property comment As CommentItem
End Class

Public Class Actor
    Public Property self As String
    Public Property name As String
    Public Property key As String
    Public Property emailAddress As String
    Public Property avatarUrls As Avatarurls
    Public Property displayName As String
    Public Property active As Boolean
    Public Property timeZone As String
End Class

Public Class Avatarurls
    ' provide property mapping for illgal names
    <JsonProperty("48x48")>
    Public Property _48x48 As String
    <JsonProperty("24x24")>
    Public Property _24x24 As String
    <JsonProperty("16x16")>
    Public Property _16x16 As String
    <JsonProperty("32x32")>
    Public Property _32x32 As String
End Class

Public Class Issue
    Public Property id As String
    Public Property self As String
    Public Property key As String
    Public Property fields As Fields
End Class

Public Class Fields
    'Public Property issuetype As Issuetype         ' not shown
    ' ...
    Public Property summary As String
    Public Property creator As Actor

    Public Property reporter As Actor
    Public Property progress As Progress        ' not shown
    Public Property comment As Comment
End Class

Public Class Comment
    Public Property startAt As Integer
    Public Property maxResults As Integer
    Public Property total As Integer
    Public Property comments As CommentItem()
    ' or:
    'Public Property comments As List(of CommentItem)
End Class

Public Class CommentItem
    Public Property self As String
    Public Property id As String
    Public Property author As Actor
    Public Property body As String
    Public Property updateAuthor As Actor
    Public Property created As DateTime
    Public Property updated As DateTime
End Class

Whew! Now, you can deserialize to a CiaranObj object:

Dim jstr =  ...from whereever
Dim complexJ = JsonConvert.DeserializeObject(Of CiaranObj)(jstr)

' reduce typing
Dim c As Comment = complexJ.issue.fields.comment

For n As Int32 = 0 To c.maxResults - 1
    Console.WriteLine("ID: {0}, Created: {1} upd Author: {2}",
                      c.comments(n).id.ToString,
                      c.comments(n).created.ToString,
                      c.comments(n).author.displayName)
Next

It prints the same info.

Again, this works fine on the jsfiddle provided in the comments. It has clearly been sanitized and in the course of that, other changes may have been wrought (the missing "expand" for instance).

1 jsonutils.com is pretty cool in that it can create VB classes, but it actually runs out of steam on this json: the classes generated are incomplete. This is apparently due to the size.

0
Biel Simon On

Since the provided "JSON" string is not valid, and assuming you can't do anything about it, you can always process it in a quick way using the String.Split(regex) method and clearing non escaped control characters like [, {, ', " with the replace method: String.Replace("[", "") for example.

So you could create a CommentInfo class that contains all the info related with a comment and make the function return a filled instance of the class.