2

I am using Quarkus framework. I am new to Java, so I don't have knowledge of libraries, so I just created these classes and returning them as response:

Code

public class Person {

  private ArrayList<Data> data = new ArrayList();

}

public class Data {

  private String name;
  private String age;
  private String job;

}

Wanted

I want to print the object as JSON and omit all null values.

I want to create a list of people, but I want the attribute job only to appear when the person is more than 30 years old.

The final result should be like:

    {
     data:
          [
           {
            "name": John,
            "age": 40,
            "job": Medic
           },
           {
            "name": Paul,
            "age": 29
           },
           {
            "name": Lua,
            "age": 20
           }
          ]
    }

Question

What is the way to do that?

hc_dev
  • 8,389
  • 1
  • 26
  • 38
  • 1
    Can you show us what code you have tried? It would be helpful to know what library you are using and how you are calling that library. – markspace Jul 25 '23 at 16:57
  • 1
    It sounds like what you really want is to print your object as a json and omit all null values. – OH GOD SPIDERS Jul 25 '23 at 17:00
  • 3
    Converting Java objects to a JSON String can be done with existing libraries like jackson. see: [Converting Java objects to JSON with Jackson](https://stackoverflow.com/questions/15786129/converting-java-objects-to-json-with-jackson) - As for controlling whether null values should be included in the JSON or not, there are multiple ways to control that behavior: https://www.baeldung.com/jackson-ignore-null-fields – OH GOD SPIDERS Jul 25 '23 at 17:06
  • Thank you @OHGODSPIDERS , jackson solved that issue for me! – Ruan Roussenq Alves Jul 25 '23 at 17:11

2 Answers2

1

In this particular case, the MappingCustomizer class is annotated with @Singleton, which means that there will be only one instance of this class created by Quarkus at runtime, and this instance will be reused throughout the application.

The customize method is implemented to customize the behavior of the ObjectMapper instance. In this case, the method sets the serialization inclusion to JsonInclude.Include.NON_NULL, which means that any properties with null values will be excluded from the JSON output.

By using this customization, any JSON output generated by the application will exclude any properties with null values, which can help to reduce the size of the output and make it more concise.

To use this customization in your Quarkus application, you can register the MappingCustomizer class with the ObjectMapperCustomizer interface, like so:

@Singleton
public class MappingCustomizer implements ObjectMapperCustomizer {
    @Override
    public void customize(ObjectMapper mapper) {
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }
}
1

Serialize an object to JSON using Jackson library

With the hint of OH GOD SPIDERS's, follow the answers to:

These are the 3 steps to use Jackson:

  1. add Jackson dependencies jackson-core, jackson-annotations and jackson-databind to your project, e.g. to Maven POM
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.15.2</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.15.2</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>
</dependency>
  1. Create an ObjectMapper to use for JSON mapping (generation/parsing)
var mapper = new ObjectMapper();
// optional configuration
  1. pass your object instance to the suitable write.. method
try {
    // convert object to JSON string and print it 
    String json = mapper.writeValueAsString(person)
    System.out.println(json);
}
catch (JsonGenerationException | JsonMappingException  e) {
    // catch various errors
    e.printStackTrace();
}

ℹ️ Note: You need public getters so Jackson mapper can access and serialize to JSON.

Configure desired serialization

Exclude null, empty or absent values

To exclude null values in written JSON, you can add the annotation @JsonInclude(Include.NON_NULL) to your properties:

public class Data {

  private String name;
  private String age;
  @JsonInclude(Include.NON_NULL)
  private String job;

  // getters and setters omitted

}

There are a few of similar ex-/inclusion options:

  • NON_ABSENT

    Value that indicates that properties are included unless their value is: null "absent" value of a referential type (like Java 8 Optional, or {link java.util.concurrent.atomic.AtomicReference}); that is, something that would not deference to a non-null value.

  • NON_EMPTY

    Value that indicates that only properties with null value, or what is considered empty, are not to be included.

  • NON_NULL

    Value that indicates that only properties with non-null values are to be included.

Conditional in-/exclusion

I want to create a list of people, but I want the attribute job only to appear when the person is more than 30 years old.

Following are 2 ways to achieve this conditional serialization:

  • [KISS] a "quick and dirty" solution
  • [SOLID] a safe and maintainable design

[KISS] Simple modification of default getter

Assume the class Data is only used for the JSON-representation (what we call a DTO). Then I would solve this simply by implementing a conditional getter like:

public class Data {

  private String name;
  private String age;
  @JsonInclude(Include.NON_NULL)
  private String job;

  public String getJob() {
    // exclude in JSON if 30 or younger
    if (Integer.valueOf(age) <= 30) {
       return null;
    }

    return job;
  }

  // remaining getters and setters omitted

}

⚠️ Warning: This modification breaks the conventional behavior of Java-getters. They should be free of conditions. Thus I added a comment to explain the "why" and kept the if-block easily removable to undo.

[SOLID] Add a custom accessor

Clean-code (expressive/additive) that follows design-principles (SRP/OCP) as recommended by Reilas would:

  1. keep getJob as your accessor, and
  2. make a different method to provide the conditional check, call it job or jobIfOver30.

Example:

private String job;

// other fields and getters omitted to focus on custom property serialization

@JsonIgnore  // 1. ignore the original property to keep accessor
public String getJob() {
  return job;
}

@JsonProperty("job")  // 2. conditional serialization with original name
@JsonInclude(Include.NON_NULL)
public String jobIfOver30() {
  if (Integer.valueOf(age) > 30) {
     return job;
  }

  return null;
}

️ Benefit: We don't need a comment and can invert the if-statement because the added method has

  • a single responsibility (JSON serialization only) and
  • a self-expressive name.

See also

Unfortunately not the case here, but when the condition depends only on values of the same property, you could use the @JsonInclude annotation with CUSTOM option as explained in:

hc_dev
  • 8,389
  • 1
  • 26
  • 38
  • 1
    As a recommendation, keep _getJob_ as your accessor, and make a different method to provide the conditional check, call it _job_ or _jobIfOver30_. – Reilas Jul 25 '23 at 21:34
  • @Reilas Thanks for your [SOLID](https://en.wikipedia.org/wiki/SOLID) alternative. A solution to prefer in the long-run. Added it to my answer. Feel free to edit and improve, because I am not sure if `@JsonIgnore` is needed and applied correctly here. – hc_dev Jul 26 '23 at 20:35