November 21, 2020

JSON Binding (JSON-B) in Java EE 8

Instead of getting and adding single properties on JsonObjectBuilder or JsonObject, an entire Java Object could be serialized and deserialized.


        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>8.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- JSON-B 1.1 Impl -->
        <dependency>
            <groupId>org.eclipse</groupId>
            <artifactId>yasson</artifactId>
            <version>1.0.5</version>
            <scope>test</scope>
        </dependency>
package se.magnuskkarlsson.example.javaee8_p6spy.control;

import java.util.Locale;

import javax.json.bind.JsonbBuilder;

import org.junit.Test;

import com.github.javafaker.Faker;

import se.magnuskkarlsson.example.javaee8_p6spy.entity.Address;
import se.magnuskkarlsson.example.javaee8_p6spy.entity.User;

public class JSONBTest {

    private final Faker faker = new Faker(new Locale("sv-SE")); // default Locale("en", "")

    @Test
    public void serialize() throws Exception {
        User user = new User();
        user.setName(faker.name().fullName());

        Address address1 = new Address();
        address1.setStreet(faker.address().streetAddress());
        user.addAddress(address1);

        Address address2 = new Address();
        address2.setStreet(faker.address().streetAddress());
        user.addAddress(address2);

        // https://javaee.github.io/jsonb-spec/
        String result = JsonbBuilder.create().toJson(user);

        System.out.println(result);
    }

}

And the POJO classes.

package se.magnuskkarlsson.example.javaee8_p6spy.entity;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Temporal(TemporalType.TIMESTAMP)
    @Column
    private Date created = new Date();

    @Column(columnDefinition = "BIT")
    private Boolean enabled = Boolean.TRUE;

    @NotBlank
    @Size(min = 2, max = 255)
    @Column(length = 255)
    private String name;

    // https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/
    // Next best bidirectional @OneToMany.
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Address> addresses = new ArrayList<>();

    // ----------------------- Logic Methods -----------------------

    public void addAddress(Address address) {
        addresses.add(address);
        address.setUser(this);
    }

    public void removeAddress(Address address) {
        addresses.remove(address);
        address.setUser(null);
    }

    // ----------------------- Helper Methods -----------------------

    @Override
    public String toString() {
        return "User [id=" + id + ", created=" + created + ", enabled=" + enabled + ", name=" + name + ", addresses="
                + addresses + "]";
    }

    // ----------------------- Get and Set Methods -----------------------

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Date getCreated() {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public Boolean getEnabled() {
        return enabled;
    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Address> getAddresses() {
        return addresses;
    }

    public void setAddresses(List<Address> addresses) {
        this.addresses = addresses;
    }

}
package se.magnuskkarlsson.example.javaee8_p6spy.entity;

import javax.json.bind.annotation.JsonbTransient;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

@Entity
public class Address {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank
    @Size(min = 2, max = 255)
    @Column(length = 255)
    private String street;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    // https://javaee.github.io/tutorial/jsonb002.html
    @JsonbTransient
    private User user;

    // ----------------------- Logic Methods -----------------------

    // ----------------------- Helper Methods -----------------------

    @Override
    public String toString() {
        return "Address [id=" + id + ", street=" + street + "]";
    }

    // Note equals and hashCode are important to implement when working with detached objects.
    // https://vladmihalcea.com/the-best-way-to-implement-equals-hashcode-and-tostring-with-jpa-and-hibernate/
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Address)) {
            return false;
        }
        return id != null && id.equals(((Address) o).getId());
    }

    // Note equals and hashCode are important to implement when working with detached objects.
    @Override
    public int hashCode() {
        // Database-generated identifiers
        // https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
        return 31;
    }

    // ----------------------- Get and Set Methods -----------------------

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

}

And when run

{"addresses":[{"street":"Hjördiss Väg 79"},{"street":"Granallén 27"}],"created":"2020-11-21T19:32:33.302Z[UTC]","enabled":true,"name":"Maria Änglund"}

No comments: