DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. Languages
  4. Micronaut Mastery: Decode JSON Using Custom Constructor Without Jackson Annotations

Micronaut Mastery: Decode JSON Using Custom Constructor Without Jackson Annotations

Let's take a look at how to decode JSON in Micronaut without the need for Jackson annotations.

Hubert Klein Ikkink user avatar by
Hubert Klein Ikkink
·
Aug. 20, 18 · Tutorial
Like (3)
Save
Tweet
Share
9.33K Views

Join the DZone community and get the full member experience.

Join For Free

Micronaut uses Jackson to encode objects to JSON and decode JSON to objects. Micronaut adds a Jackson ObjectMapper bean to the application context with all configuration to work properly. Jackson can by default populate an object with values from JSON as the class has a no argument constructor and the properties can be accessed. But if our class doesn't have a no argument constructor we need to use the @JsonCreator and @JsonProperty annotations to help Jackson. We can use these annotations on the constructor with arguments that is used to create an object.

But we can even make it work without the extra annotations, so our classes are easier to read and better reusable. We need to add the Jackson ParameterNamesModule as a module to the ObjectMapper instance in our application. And we need to compile our sources with the -parameter argument, so the argument names are preserved in the compiled code. Luckily the -parameter option is already added to our Gradle build when we create a Micronaut application. All we have to do is to add the ParameterNamesModule in our application.

We need to add a dependency on com.fasterxml.jackson.module:jackson-module-parameter-names to our compile classpath. Micronaut will automatically find the ParameterNamesModule and add it to the ObjectMapper using the findAndRegisterModules method.

So we change our build.gradle file first:

// File: build.gradle
...
dependencies {
    ...
    compile "com.fasterxml.jackson.module:jackson-module-parameter-names:2.9.0"
    ...
}
...

In our application we have the following class that has an argument constructor to create an immutable object:

package mrhaki;

import java.util.Objects;

public class Language {
    private final String name;

    private final String platform;

    public Language(final String name, final String platform) {
        this.name = name;
        this.platform = platform;
    }

    public String getName() {
        return name;
    }

    public String getPlatform() {
        return platform;
    }

    @Override
    public boolean equals(final Object o) {
        if (this == o) { return true; }
        if (o == null || getClass() != o.getClass()) { return false; }
        final Language language = (Language) o;
        return Objects.equals(name, language.name) &&
                Objects.equals(platform, language.platform);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, platform);
    }
}

We write the following sample controller to use the Language class as a return type (we wrap it in a Mono object so the method is reactive):

package mrhaki;

import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import reactor.core.publisher.Mono;

@Controller("/languages")
public class LanguagesController {

    @Get("/groovy")
    public Mono<Language> getGroovy() {
        return Mono.just(new Language("Groovy", "JVM"));
    }

}

And in our test, we use HttpClient to invoke the controller method. The exchange method will trigger a decode of the JSON output of the controller to a Language object:

package mrhaki

import io.micronaut.context.ApplicationContext
import io.micronaut.http.HttpRequest
import io.micronaut.http.HttpStatus
import io.micronaut.http.client.HttpClient
import io.micronaut.runtime.server.EmbeddedServer
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification

class LanguagesControllerSpec extends Specification {

    @AutoCleanup
    @Shared
    private static EmbeddedServer server = ApplicationContext.run(EmbeddedServer)

    @AutoCleanup
    @Shared
    private static HttpClient client = server.applicationContext.createBean(HttpClient, server.URL)

    void '/languages/groovy should find language Groovy'() {
        given:
        final request = HttpRequest.GET('/languages/groovy')

        when:
        final response = client.toBlocking().exchange(request, Language)

        then:
        response.status() == HttpStatus.OK

        and:
        response.body() == new Language('Groovy', 'JVM')
    }


}

When we wouldn't have the dependency on jackson-module-parameter-names and not use the -parameter compiler option we get the following error message:

[nioEventLoopGroup-1-6] WARN  i.n.channel.DefaultChannelPipeline - An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
io.micronaut.http.codec.CodecException: Error decoding JSON stream for type [class mrhaki.Language]: Cannot construct instance of `hello.conf.Language` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (byte[])"{"name":"Groovy","platform":"JVM"}"; line: 1, column: 2]

But with the dependency and -parameter compiler option we have a valid test without any errors. Jackson knows how to use the argument constructor to create a new Language object.

Written with Micronaut 1.0.0.M4.

JSON Jackson (API) Annotation

Published at DZone with permission of Hubert Klein Ikkink, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • How Elasticsearch Works
  • Multi-Tenant Architecture for a SaaS Application on AWS
  • Unlock the Power of Terragrunt’s Hierarchy
  • 5 Common Firewall Misconfigurations and How to Address Them

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: