4

I'm developing a REST application using application/json Content-Type (server). I'm developing the REST client library for it as well (client). Both server and client use the same Jackson annotated classes/resources.

One of my server use cases includes adding a field which should be write-only (enable only deserialization). I do it like this:

public class MyClass {

  @JsonProperty(access = WRITE_ONLY)
  private String field;

  ...

}

However, since this class is used on the client side too, I am unable to send value to this field because the serialization processing skips it. I want the client to be able serialize this field. The server and client use separate object mappers.

I can use separate implementations of JsonSerializer<MyClass> and set it to the object mapper but this mean that I have to serialize all other fields manually. I don't want this.

I am wondering, if there is some way to instruct object mapper (or inject something into it) to handle such field differently?

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
Aleydin Karaimin
  • 944
  • 1
  • 6
  • 16
  • You could try using a custom annotation like `@Server` and implement this https://stackoverflow.com/questions/43342097/jackson-custom-annotation-for-custom-value-serialization Shared module between server and client could have methods like `getServerObjectMapper()` and `getClientObjectMapper()`, which would have this serializer added or not accordingly. – stirante Feb 10 '21 at 16:35
  • Thank you for your answer but I don't know how this fit into my case. In the example `@JsonSerialize(using = UnitSerializer.class)` is used onto the field . So it is automatically resolved in runtime. Programmatically, I only can add serializer per certain type. Also, I don't want to introduce shared module by now if it is possible – Aleydin Karaimin Feb 10 '21 at 16:52
  • using @JsonIgnore on getter method Only, answer in the link [link](https://stackoverflow.com/questions/12505141/only-using-jsonignore-during-serialization-but-not-deserialization) – Nasir Hussain Feb 10 '21 at 18:16
  • @NasirHussain, it works on the server side when. However, on the client side when I send this field, jackson ignores it. – Aleydin Karaimin Feb 10 '21 at 18:19

1 Answers1

2

When you use common model classes on two sides: client and server you can register all differences using com.fasterxml.jackson.databind.Module class and its implementations. In case, some Jackson configuration should be available only on one side you can move it to MixIn classes/interfaces and register only on given side.

Simple example:

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.IOException;

public class ClientServerApp {

    public static void main(String[] args) throws IOException {
        // Client side
        ObjectMapper clientMapper = JsonMapper.builder().build();

        // create payload on client side
        MyClass instance = new MyClass(1, "fieldValue");
        String payload = clientMapper.writeValueAsString(instance);

        // send to server
        System.out.println("Send: " + payload);
        // ..

        // Server side
        ObjectMapper serverMapper = JsonMapper.builder()
                .addModule(new ServerModule())
                .build();
        MyClass deserialized = serverMapper.readValue(payload, MyClass.class);
        System.out.println("Deserialized: " + deserialized);

        System.out.println("Payload on server side: " + serverMapper.writeValueAsString(deserialized));
    }
}

class ServerModule extends SimpleModule {
    public ServerModule() {
        setMixInAnnotation(MyClass.class, MyClassServerMixIn.class);
        // add other configuration
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class MyClass {

    private int id;
    private String field;
}

interface MyClassServerMixIn {

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    String getField();
}

Above code prints:

Send: {"id":1,"field":"fieldValue"}
Deserialized: MyClass(id=1, field=fieldValue)
Payload on server side: {"id":1}

As you can notice, only ObjectMapper on client side can serialise field property.

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
  • 1
    I figured it out yesterday and now I see your answer. It is exact the same think that I found. Anyway, I left it to collect more suggestions. This one handle this case nicely, so I mark it as resolved :). – Aleydin Karaimin Feb 11 '21 at 09:36