5

Is it possible to create JSON values in Groovy using the default JsonBuilder library to exclude all the null values of an object? Such as what Jackson does in Java by annotating classes to exclude null values.

An example would be:

{
   "userId": "25",
   "givenName": "John",
   "familyName": null,
   "created": 1360080426303
}

Which should be printed as:

{
   "userId": "25",
   "givenName": "John",
   "created": 1360080426303
}
Peymankh
  • 1,976
  • 25
  • 30

4 Answers4

8

Not sure if it's OK for you as my method works on a Map with List properties:

def map = [a:"a",b:"b",c:null,d:["a1","b1","c1",null,[d1:"d1",d2:null]]]

def denull(obj) {
  if(obj instanceof Map) {
    obj.collectEntries {k, v ->
      if(v) [(k): denull(v)] else [:]
    }
  } else if(obj instanceof List) {
    obj.collect { denull(it) }.findAll { it != null }
  } else {
    obj
  }
}

println map
println denull(map)

yields:

[a:a, b:b, c:null, d:[a1, b1, c1, null, [d1:d1, d2:null]]]
[a:a, b:b, d:[a1, b1, c1, [d1:d1]]]

After filter null values out, you then can render the Map as JSON.

chanwit
  • 3,174
  • 2
  • 23
  • 20
3

If you are using Groovy >2.5.0, you can use JsonGenerator. The example below is taken from Groovy's Documentation as of July 2018.

class Person {
    String name
    String title
    int age
    String password
    Date dob
    URL favoriteUrl
}

Person person = new Person(name: 'John', title: null, age: 21, password: 'secret',
                            dob: Date.parse('yyyy-MM-dd', '1984-12-15'),
                            favoriteUrl: new URL('http://groovy-lang.org/'))

def generator = new JsonGenerator.Options()
    .excludeNulls()
    .dateFormat('yyyy@MM')
    .excludeFieldsByName('age', 'password')
    .excludeFieldsByType(URL)
    .build()

assert generator.toJson(person) == '{"dob":"1984@12","name":"John"}'
krismath
  • 1,879
  • 2
  • 23
  • 41
2

I used the Groovy metaClass to workaround this issue, but am not sure it would work in all cases.

I created a Class to hold the required elements, but left out the optional elements that could possibly have a null (or empty) value.

private class User {
    def id
    def username
}

Then, I added the data to this class. My use case was fairly complex so this is a simplified version just to show an example of what I did:

User a = new User(id: 1, username: 'john')
User b = new User(id: 2, username: 'bob')
def usersList = [a,b]

usersList.each { u ->
    if (u.id == 1)
        u.metaClass.hobbies = ['fishing','skating']
}
def jsonBuilder = new JsonBuilder([users: usersList])
println jsonBuilder.toPrettyString()

Results:

{
"users": [
    {
        "id": 1,
        "username": "john",
        "hobbies": [
            "fishing",
            "skating"
        ]
    },
    {
        "id": 2,
        "username": "bob"
    }
  ]
}
acvcu
  • 2,476
  • 10
  • 40
  • 56
1

If you do not need use JSONBuilder you can use com.fasterxml.jackson:

Make object:

private static final ObjectMapper JSON_MAPPER = new ObjectMapper().with {
    setSerializationInclusion(JsonInclude.Include.NON_NULL)
    setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
    setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE)
    setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE)
}

and display your list of maps like that (maps can have any Object inside):

println(JSON_MAPPER.writeValueAsString(listOfMaps))