Home > OS >  Serialize objects to a map with the object class as the key?
Serialize objects to a map with the object class as the key?

Time:10-05

I'm writing a application using Spring boot and jackson for JSON parsing. I need to handle another service which produces JSON like this:

{
  "task-id": 5081,
  "task-created-on": {
    "java.util.Date": 1631022026000
  }
}

Notably, certain fields like the date field here are serialized into a map with a single key-value pair, where the key is a java classname and the value is the actual value of the field.

I've been going through the jackson documentation and haven't found anything about this format. Is there a way to configure jackson to produce and parse fields in this format?

At a minimum, I need to handle dates formatted this way. But I believe the service also uses this format for other objects, where the map key will be the name of some arbitrary java class and the value will be a map of its own. So I'd be interested in a solution that handles more than just dates if possible.

CodePudding user response:

It can be easily done with custom serializer in Jackson by following steps.
First, create objects for serialization as follows:

class MyDateObject {
    private Date date;

    //general getter/setter
}

class Task {
    @JsonProperty("task-id")
    private int taskId;

    @JsonProperty("task-created-on")
    private MyDateObject taskCreatedOn;

    //general getters/setters
}

Second, define your custom serializer: (Please note that I used myDateObject.getDate().getClass().getName() to get the class name of date field.)

class DateSerializer extends StdSerializer<MyDateObject> {
    public DateSerializer() {
        this(null);
    }

    protected DateSerializer(Class<MyDateObject> t) {
        super(t);
    }

    @Override
    public void serialize(MyDateObject myDateObject, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeNumberField(myDateObject.getDate().getClass().getName(), myDateObject.getDate().getTime());
        jsonGenerator.writeEndObject();
    }
}

Finally, register the serializer with ObjectMapper for the MyDateObject class and perform the serialization:

MyDateObject myDateObject = new MyDateObject();
myDateObject.setDate(new Date());

Task task = new Task();
task.setTaskId(5081);
task.setTaskCreatedOn(myDateObject);

SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(MyDateObject.class, new DateSerializer());

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(simpleModule);

System.out.println(objectMapper.writeValueAsString(task));

The expected output is:

{"task-id":5081,"task-created-on":{"java.util.Date":1633402076254}}


Please refer to Jackson – Custom Serializer for more information.

CodePudding user response:

I did something like this, maybe it will work for you:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class TaskRequest {

  @JsonProperty("task-id")
  private Long taskId;

  @JsonProperty("task-created-on")
  Map<String, Long> taskCreatedOn;
 }

@RestController
@RequestMapping("/test")
public class TestController {

@PostMapping
public void test(@RequestBody TaskRequest taskRequest) {
    taskRequest.getTaskCreatedOn().forEach((key, value) -> {
        try {
            Class<?> c = Class.forName(key);
            Constructor<?> cons = c.getConstructor(long.class);
            Object date = cons.newInstance(value);
            System.out.println(date);
        } catch (Exception e) {
            e.printStackTrace();
        }
    });
  }
}
  • Related