Custom Jackson serializer on specific fields

落爺英雄遲暮 提交于 2019-12-24 01:17:28

问题


I'm looking to have multiple jackson deserializers for the same object(s) all based on a custom annotation.

Ideally I'd have a single POJO like:

public class UserInfo {
   @Redacted    
   String ssn;

   String name;
}

Under "normal" conditions I want this object to be serialized the default way:

{"ssn":"123-45-6789", "name":"Bob Smith"}

but for logging purposes (for example) I want to redact the SSN so it doesn't get saved in our logs:

{"ssn":"xxx-xx-xxxx", "name":"Bob Smith"}

I've also looked into using @JsonSerialize and come up with:

public class UserInfo {

    @JsonSerialize(using = RedactedSerializer.class, as=String.class)
    String firstName;
    String lastName;

}

The problem with this is that it ALWAYS uses this rule. Can multiple @JsonSerializers be added and only the specified one be used within the runtime code?

I've also seen "views" but ideally I'd like to atleast show that the field was present on the request - even if I dont know the value.


回答1:


The 100% safe way would be to use different DTO in different requests. But yeah, if you cant do that, use @JsonView and custom serializer, something like:

class Views {
    public static class ShowSSN {}
}

private static class MyBean{
    @JsonSerialize(using = MyBeanSerializer.class)
    @JsonView(Views.ShowSSN.class)
    String ssn;
    //getter setter constructor
}

private class MyBeanSerializer extends JsonSerializer<String> {
    @Override
    public void serialize(String value, JsonGenerator gen,
                          SerializerProvider serializers) throws IOException {
        Class<?> jsonView =  serializers.getActiveView();
        if (jsonView == Views.ShowSSN.class) 
            gen.writeString(value); // your custom serialization code here
        else 
            gen.writeString("xxx-xx-xxxx");
    }
} 

And use it like:

public static void main(String[] args) throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    MyBean bean = new MyBean("123-45-6789");

    System.out.println(mapper.writerWithView(Views.ShowSSN.class)
                             .writeValueAsString(bean));
    // results in {"ssn":"123-45-6789"}

    System.out.println(mapper.writeValueAsString(bean));
    // results in {"ssn":"xxx-xx-xxxx"}
}

Also for example in spring it would be really easy to use

@Controller
public class MyController {
    @GetMapping("/withView")       // results in {"ssn":"123-45-6789"}
    @JsonView(Views.ShowSSN.class)
    public @ResponseBody MyBean withJsonView() {
        return new MyBean("123-45-6789");
    }

    @GetMapping("/withoutView")    // results in {"ssn":"xxx-xx-xxxx"}
    public @ResponseBody MyBean withoutJsonView() {
        return new MyBean("123-45-6789");
    }

}



回答2:


I think you could achieve that dynamically by coding not annotations, inside your methods, you can set the proper Serializer and switch between them

(The code depends on your Jackson version)

ObjectMapper mapper = new ObjectMapper();
SimpleModule testModule = new SimpleModule("MyModule", new Version(1, 0, 0, null));
testModule.addSerializer(new RedactedSerializer()); // assuming serializer declares correct class to bind to
mapper.registerModule(testModule);

https://github.com/FasterXML/jackson-docs/wiki/JacksonHowToCustomSerializers



来源:https://stackoverflow.com/questions/47129953/custom-jackson-serializer-on-specific-fields

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!