I have a pojo-like object with following structure:
#JsonFilter("MyFilter")
public MyDTO {
public int id;
public List<MyNestedDTO> nestedDTO;
public MyNestedDTO {
public int id;
public String name;
...
}
...
}
I want to completely omit name-field from a serialized output. I use SimpleBeanPropertyFilter like this:
ObjectMapper mapper = new ObjectMapper();
PropertyFilter columnFilter = new SimpleBeanPropertyFilter() {
#Override
public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer)
throws Exception {
if (include(writer)) {
if (writer.getName().equalsIgnoreCase("nestedDTO")) {
return;
}
writer.serializeAsField(pojo, jgen, provider);
} else if (!jgen.canOmitFields()) {
writer.serializeAsOmittedField(pojo, jgen, provider);
}
}
...
};
FilterProvider filters = new SimpleFilterProvider().addFilter("MyFilter", columnFilter);
String result = mapper.writer(filters).writeValueAsString(MyDTOObj);
I see that I can't catch the moment of nestedDTO.name field serialization by this serializeAsField() realization. Explain to me how I can get it, please.
If you want to skip serialization/deserialization of a field, just annotate it with #JsonIgnore :
public MyDTO {
public int id;
public List<MyNestedDTO> nestedDTO;
public MyNestedDTO {
public int id;
#JsonIgnore
public String name;
...
}
...
}
Related
I want to convert a json into Java class by having custom deserializer.
I'm able to serialize ACC_NUM, NAME and any other fields from json but not sure what can be done to convert MOBILE_NUMBER_1,MOBILE_NUMBER_2 like fields into single JSONArray(See AccountInfo class). There can be many more fields like this and count also is not fixed. Example there can be ADDRESS_1, ADDRESS_2 till ADDRESS_20 and so on and all this fields should go in JSONArray of ADDRESS after deserilization.
I have a Map of Map which holds info like this:
{
"accountInfo": {
"ACC_NUM": "1234567890",
"NAME": "John Cena",
"MOBILE_NUMBER_1": "12376534",
"MOBILE_NUMBER_2": "12376534",
"MOBILE_NUMBER_3": "12376534",
"MOBILE_NUMBER_4": "12376534"
},
"someOther": {
//similer to above
}
}
This info I want to convert to the following class CommonInfo:
public class CommonInfo {
private AccountInfo accountInfo;
//other properties...
}
#JsonIgnoreProperties(ignoreUnknown = true)
public class AccountInfo {
#JsonProperty("ACC_NUM")
private FieldValue<BigInteger> accountNum;
#JsonProperty("NAME")
private FieldValue<String> name;
#JsonProperty("MOBILE_NUMBER")
private FieldValue<JSONArray> mobileNumber;
}
//FieldValue class
public interface FieldValue<T> {
T getInitialValue();
void setInitialValue(T initialValue);
T getValue();
void setValue(T value);
}
#JsonInclude(JsonInclude.Include.ALWAYS)
public class FieldValueImpl<T> implements FieldValue<T> {
protected T initialValue;
protected T value;
//getters, setters, cons..
}
My service code takes json/Map and tries to convert it to CommonInfo class
#Service
public class MyService {
private final ObjectMapper jsonMapper = new ObjectMapper();
#PostConstruct
protected void init() {
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(FieldValue.class, new FieldValueSerializer());
simpleModule.addDeserializer(FieldValue.class, new FieldValueDeserializer());
jsonMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
jsonMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
jsonMapper.registerModule(simpleModule);
}
public CommonInfo setPojoResult(Map<String, LinkedHashMap<String, String>> contentAsMap) {
return jsonMapper.convertValue(contentAsMap, CommonInfo.class);
}
}
Serializer and Deserializer looks like this:
public class FieldValueDeserializer extends JsonDeserializer<FieldValue<?>> implements ContextualDeserializer {
private JavaType valueType;
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
throws JsonMappingException {
var deserializer = new FieldValueDeserializer();
if (property == null) {
deserializer.valueType = ctxt.getContextualType().containedType(0);
} else {
var wrapperType = property.getType();
var valueType = wrapperType.containedType(0);
deserializer.valueType = valueType;
}
return deserializer;
}
#Override
public FieldValue<?> deserialize(JsonParser parser, DeserializationContext context) throws IOException {
FieldValueDeserializer deserializer = new FieldValueDeserializer();
deserializer.getKnownPropertyNames();
FieldValue<?> fieldValueImpl = new FieldValueImpl<>();
if (valueType.toString().contains("java.time.LocalDate")) {
JsonNode node = parser.getCodec().readTree(parser);
FieldValue<LocalDate> f1 = new FieldValueImpl<>();
f1.setValue(DateUtils.convertJulianToLocalDate(node.textValue()));
return f1;
} else {
fieldValueImpl.setValue(context.readValue(parser, valueType));
}
return fieldValueImpl;
}
}
//--
public class FieldValueSerializer extends StdSerializer<FieldValue> {
public FieldValueSerializer() {
this(null);
}
public FieldValueSerializer(Class<FieldValue> vc) {
super(vc);
}
#Override
public void serialize(FieldValue value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeString(String.valueOf(value.getCurValue()));
}
}
I want to have an output like
{ Orga1: [ dep1, dep2], Orga2: [dep88, dep99], ...}
but somehow I fail to get it properly done.
I have the following structure:
#JsonSerialize(using = OrganisationSerializer.class)
public class Organisation {
String name;
private HashMap<String, Department> lstDepartments = new HashMap<>();
public List<Department> getList() {
return lstDepartments.values().stream().collect(Collectors.toList());
}
}
with the nested class
#JsonSerialize(using = DepartmentSerializer.class)
public class Department {
String name;
HashMap<String, Role4Filter> lstRole = new HashMap<>();
public List<Role4Filter> getList() {
return lstRole.values().stream().collect(Collectors.toList());
}
...
}
The major problem is that the HashMap needs to be transferred into a List which needs to be serialized. But somehow I fail to convert into a JSON properly.
My approach with
public class OrganisationSerializer extends JsonSerializer<Organisation> {
#Override
public void serialize(Organisation value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartObject();
gen.writeArrayFieldStart(value.name);
for (final Department item : value.getList()) {
gen.writeObject(item);
}
gen.writeEndArray();
gen.writeEndObject();
}
fails with the exception that
com.fasterxml.jackson.databind.JsonMappingException: org..Department cannot be cast to org..Organisation
Any ideas? Or is there some other annotation possible (beside the serializer?)
I'm using Java.
I have the following class:
class MyClass{
#JsonProperty("test")
String test;
String myPrivateTest;
}
I'm using Jackson ObjectWriter to write my JSON object to file/as string as follow:
ObjectWriter writer = objMapper.writer(new DefaultPrettyPrinter());
writer.writeValue(new File(path), myObject);
I want to write to file only test member and not myPrivateTest, How can I do it?
EDIT
I have been tried with:
class MyClass{
#JsonProperty("test")
String test;
#JsonIgnore
String myPrivateTest;
}
And with
#JsonIgnoreProperties({"myPrivateTest"})
class MyClass{
#JsonProperty("test")
String test;
String myPrivateTest;
}
And It is still write myPrivateTest to the file
Using Jackson 2.9.6
Just adding the #JsonProperty to the fields you want is enough.
public class MyClass {
#JsonProperty("test")
String test;
String myPrivateTest;
public static void main(String[] args) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
MyClass c = new MyClass();
c.test = "HELLO";
c.myPrivateTest = "WORLD";
System.out.println(objectMapper.writeValueAsString(c));
}
}
Output:
{"test":"HELLO"}
String myPrivateTest is correctly ignored
You can ignore specific fields with providing PropertyFilter as below :
#JsonFilter("someFilter")
#Data // lombok
#NoArgsConstructor // lombok
class SomeClass {
private String name;
private int someField;
public SomeClass(String name, int sf){
this.name=name;
this.someField = sf;
}
}
#Test
public void test() throws JsonProcessingException {
SomeClass someClass = new SomeClass("test",1);
ObjectMapper mapper = new ObjectMapper();
PropertyFilter theFilter = new SimpleBeanPropertyFilter() {
#Override
public void serializeAsField
(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer)
throws Exception {
if (include(writer)) {
// here you can define the properties to ignore
if (!writer.getName().equals("name")) {
writer.serializeAsField(pojo, jgen, provider);
return;
}
} else if (!jgen.canOmitFields()) {
writer.serializeAsOmittedField(pojo, jgen, provider);
}
}
};
FilterProvider filters = new SimpleFilterProvider()
.addFilter("someFilter", theFilter);
String objAsString = mapper
.writer(filters)
.writeValueAsString(someClass);
assertThat(objAsString).doesNotContain("name");
}
Serialized output is :
{"someField":1}
I needed a better hibernate enum mapping and this page served me well (Except I used char type instead of int).
Next question is how can I serialize/deserialize an enum in a generic way?
Think of a Gender enum:
#JsonSerialize(using = PersistentEnumSerializer.class)
#JsonDeserialize(using = PersistentEnumDeserializer.class)
public enum Gender implements PersistentEnum {
MALE("M", "Male"), FEMALE("F", "Female");
private String code;
private String display;
Gender(String code, String display) {
this.code = code;
this.display = display;
}
public String getName() {
return name();
}
public String getCode() {
return code;
}
public String getDisplay() {
return display;
}
public String toString() {
return display;
}
}
which implements getName(), getCode() and getDisplay() methods of PersistentEnum interface. Serializing is easy:
public class PersistentEnumSerializer extends JsonSerializer<PersistentEnum> {
#Override
public void serialize(PersistentEnum object, JsonGenerator generator, SerializerProvider provider) throws IOException, JsonProcessingException {
generator.writeStartObject();
generator.writeFieldName("name");
generator.writeString(object.getName());
generator.writeFieldName("code");
generator.writeString(object.getCode());
generator.writeFieldName("display");
generator.writeString(object.getDisplay());
generator.writeEndObject();
}
}
but how can I deserialize in java 6? In java 8, I would add a static method to PersistentEnum interface.
public class PersistentEnumDeserializer extends JsonDeserializer<PersistentEnum> {
#Override
public PersistentEnum deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
//String value = node.get("name").asText();
//TODO Somehow I need to get Gender.MALE if the json is {"name":"MALE","code":"M","display":"Male"}
return null;
}
}
One possible solution is to add a new method getType() to PersistentEnum which will identify the type of Enum.
#JsonSerialize(using = PersistentEnumSerializer.class)
#JsonDeserialize(using = PersistentEnumDeserializer.class)
public enum Gender implements PersistentEnum {
#Override
public String getType() {
return "gender";
}
}
Serializer should also be modified to include type while serialization.
public class PersistentEnumSerializer extends JsonSerializer<PersistentEnum> {
#Override
public void serialize(PersistentEnum object, JsonGenerator generator, SerializerProvider provider) throws IOException, JsonProcessingException {
generator.writeStartObject();
generator.writeFieldName("name");
generator.writeString(object.getName());
generator.writeFieldName("code");
generator.writeString(object.getCode());
generator.writeFieldName("display");
generator.writeString(object.getDisplay());
generator.writeFieldName("type");
generator.writeString(object.getType());
generator.writeEndObject();
}
}
Deserializer can be written as shown below.
public class PersistentEnumDeserializer extends JsonDeserializer<PersistentEnum> {
#Override
public PersistentEnum deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
return findEnum(node.get("type").asText(), node.get("name").asText());
}
private PersistentEnum findEnum(String type, String name) {
switch (type) {
case "gender":
return Gender.valueOf(name);
// handle other types here.
default:
return null;
}
}
}
While #Justin Jose's solution is not the one I'm looking for (because for each enum we need to add to findEnum method), it gave me a good hint.
If getType is implemented like this:
#Override
public String getType() {
return getClass().getSimpleName();
}
and findEnum like this
private PersistentEnum findEnum(String type, String name) {
Class<?> c = null;
try {
c = Class.forName("enums." + type); //Assuming all PersistentEnum's are in "enums" package
if (PersistentEnum.class.isAssignableFrom(c)) {
Method method = c.getMethod("name");
for (Object object : c.getEnumConstants()) {
Object enumName = method.invoke(object);
if (name.equals(enumName))
return (PersistentEnum) object;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
it may work. Not tested and possibly vulnerable.
I've a Model object Group
public class Group {
String title;
List<User> members;
String createdBy;
}
I'm using Jackson to serialize this Object. Instead of serializing the whole User object in list "members" I want to serializer just the user.getTitle() field.
Basically I want a HashMap to be something like
{
"title" : "sometitle"
"members" : [user1.getTitle(), user2.getTitle()]
}
I've written a custom serializer for this
public class GroupSerializer extends JsonSerializer<Circle> {
#Override
public void serialize(Group value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
if(value != null) {
gen.writeStartObject();
gen.writeStringField("title", value.getTitle());
gen.writeStringField("createdBy", value.getCreatedBy());
gen.writeFieldName("members");
gen.writeStartArray();
for(User user : value.getMembers()) {
gen.writeString(user.getEmail());
}
gen.writeEndArray();
gen.writeEndObject()
}
}
}
But it's not working. How do I serialize only a field of List instead of whole User Object?
I suggest that you look into Jackson's Converter interface, which seems more suited to the task than creating a custom serializer.
One approach it to create a Converter instance and add it to the ObjectMapper, so that it will be used for the serialization of all User instances.
public class UserConverter extends StdConverter<User, String> {
#Override
public String convert(User user) {
return user.getTitle();
}
}
Register it on your ObjectMapper like this:
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(User.class, new StdDelegatingSerializer(new UserConverter()));
ObjectMapper om = new ObjectMapper().registerModule(simpleModule);
Another approach, in case you don't want to convert all User instances to String, is to annotate selected properties with a converter like this:
public class Group {
String title;
#JsonSerialize(converter = ListUserConverter.class)
List<User> members;
String createdBy;
}
And have a corresponding converter that looks something like this:
public class ListUserConverter extends StdConverter<List<User>, List<String>> {
#Override
public List<String> convert(List<User> users) {
return users.stream().map(User::getTitle).collect(Collectors.toList());
}
}
Try like below :
Group:
#JsonIgnoreProperties(ignoreUnknown=true)
public class Group {
#JsonSerialize(using= TitleSerializer.class)
List<User> members;
//getters and setters
}
User:
public class User {
private String title;
//getters and setters
}
Custom Serializer :
public class TitleSerializer extends StdSerializer<List<User>> {
private static List<User> users=new ArrayList<User>();
protected TitleSerializer(Class<List<User>> t) {
super(t);
// TODO Auto-generated constructor stub
}
#SuppressWarnings("unchecked")
public TitleSerializer(){
this((Class<List<User>>) users.getClass());
}
#Override
public void serialize(List<User> users, JsonGenerator paramJsonGenerator,
SerializerProvider paramSerializerProvider) throws IOException {
paramJsonGenerator.writeStartObject();
List<String> titles=new ArrayList<String>(users.size());
for(User user: users){
titles.add(user.getTitle());
}
paramJsonGenerator.writeObjectField("members", titles);
paramJsonGenerator.writeEndObject();
}
}
Test :
Group group=new Group(Arrays.asList(new User("a"),new User("b"),new User("c")));
ObjectMapper mapper = new ObjectMapper();
String serialized = mapper.writeValueAsString(group);
System.out.println("output "+serialized);
Output:
{"members":["a","b","c"]}