0

I am writing a custom Serializer (Jackson JSON) for List class, this list could be inferred with different class types, so I'll need to grab object fields values using reflection.

Note, all this classes has public values (no setters and getters), so Invoking the getter will not be an option.

This is what I get so far:

package com.xxx.commons.my.serializer;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import org.apache.commons.lang3.reflect.FieldUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.List;

public class ListSerializer extends StdSerializer<List> {

public ListSerializer() {
    super(List.class);
}

@Override
public void serialize(List aList, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {

    if (aList != null) {

        jsonGenerator.writeStartObject();

        for (int index = 0 ; index < aList.size(); index++) {

            try {

                Object next = aList.get(index);

                List<Field> fields = FieldUtils.getAllFieldsList(next.getClass());

                Object object = next.getClass().newInstance();

                for (int j = 0 ; j < fields.size(); j ++ ) {

                    jsonGenerator.writeObjectField(String.format("%s[%s]",fields.get(j).getName(),index) , object);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }
        jsonGenerator.writeEndObject();
    }
}
}

MyTest

package com.xxx.commons.my.serializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.junit.Test;

public class ListSerializerTest {

    private ObjectMapper mapper = new ObjectMapper();

    @Test
    public void test() throws Exception {

        SimpleModule module = new SimpleModule();

        module.addSerializer(new ListSerializer());

        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        mapper.registerModule(module);

        MyTempClassParent parent = new MyTempClassParent();
        parent.mylist.add(new MyTempClass("1","2","3"));

        String json = mapper.writeValueAsString(parent);

        System.out.println(json);
    }   

}

Example classes:

public class MyTempClass {

    public MyTempClass() {
    }

    public MyTempClass(String value1, String value2, String value3) {
        this.valueA = value1;
        this.valueB = value2;
        this.valueC = value3;
    }

    public String valueA;
    public String valueB;
    public String valueC;
}

public class MyTempClassParent {

   public List<MyTempClass> mylist = new LinkedList<>();
}

Any ideas or alternatives for writing this ?

Pedro Sosa
  • 39
  • 9
  • Can't you use the `SerializerProvider` to get a serializer for each List item and serialize it using this serializer? – kidney Apr 01 '19 at 16:54
  • Possible duplicate of [Retrieving the inherited attribute names/values using Java Reflection](https://stackoverflow.com/questions/1042798/retrieving-the-inherited-attribute-names-values-using-java-reflection) – SergeyB Apr 01 '19 at 17:04
  • SergeyB I didn't find in any answer ... how to grab the value of the fields. Getting the fields is easy. – Pedro Sosa Apr 01 '19 at 17:37

1 Answers1

0

Maybe you should just use ObjectMapper with setting property accessor to get access to every field:

ObjectMapper mapper = new ObjectMapper();
    mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);

    MyDtoAccessLevel dtoObject = new MyDtoAccessLevel();

    String dtoAsString = mapper.writeValueAsString(Arrays.asList(dtoObject));
    System.out.println(dtoAsString);

result: [{"stringValue":null,"intValue":0,"floatValue":0.0,"booleanValue":false}] dto:

class MyDtoAccessLevel {
    private String stringValue;
    int intValue;
    protected float floatValue;
    public boolean booleanValue;
    // NO setters or getters

--edit For getting values from objects by reflection:

    @Override
public void serialize(List aList, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {

    if (aList != null) {

        jsonGenerator.writeStartObject();

        for (int index = 0 ; index < aList.size(); index++) {

            try {

                Object next = aList.get(index);

                List<Field> fields = FieldUtils.getAllFieldsList(next.getClass());

                for (int j = 0 ; j < fields.size(); j ++ ) {

                    jsonGenerator.writeObjectField(String.format("%s[%s]",fields.get(j).getName(),index) , fields.get(j).get(next));
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }
        jsonGenerator.writeEndObject();
    }
}

Please write in question, what do you want to have in output.

pethryth
  • 56
  • 5
  • thank you for your response, that helps but doesn't do what I need, this will produce: {"mylist":[{"valueA":"1","valueB":"2","valueC":"3"}]} and I need { "mylist.valueA" : "1" ... – Pedro Sosa Apr 01 '19 at 17:29
  • checkout edited comment and write what do you expect in output for myList with 2 elements – pethryth Apr 01 '19 at 19:06