2

I have an ArrayList<Person> that contains a few Person type objects. The Person class has the following attributes with their getters and setters:

private int id;
private String name;
private String email;
private LocalDate birthDate;

I'd like to export the ArrayList<Person> to a JSON output file with the exact same format: persons.json:

[
  {
    "id": 1,
    "name": "The Best",
    "email": "thenextbigthing@gmail.com",
    "birthDate": "1981-11-23"
  },
  {
    "id": 2,
    "name": "Andy Jr.",
    "email": "usa@gmail.com",
    "birthDate": "1982-12-01"
  },
  {
    "id": 3,
    "name": "JohnDoe",
    "email": "gameover@gmail.com",
    "birthDate": "1990-01-02"
  },
  {
    "id": 4,
    "name": "SomeOne",
    "email": "rucksack@gmail.com",
    "birthDate": "1988-01-22"
  },
  {
    "id": 5,
    "name": "Mr. Mxyzptlk",
    "email": "bigman@hotmail.com",
    "birthDate": "1977-08-12"
  }
]

I've tried to create an Array from the ArrayList and create the output from that Array but I have a problem with that which I cannot work it around. I'm getting output data for the birthDate attribute looking like this:

"birthDate" : {
    "year" : 1952,
    "month" : "JANUARY",
    "chronology" : {
      "id" : "ISO",
      "calendarType" : "iso8601"
    },
    "era" : "CE",
    "leapYear" : true,
    "dayOfMonth" : 27,
    "monthValue" : 1,
    "dayOfWeek" : "SUNDAY",
    "dayOfYear" : 27
  }

How can I make every attribute have the same output format as provided in the example persons.json output file. I'm not allowed to use any other Jackson library besides core, annotations and databind. I'm also not allowed to change attribute type inside the class.

sasieightynine
  • 434
  • 1
  • 5
  • 16

4 Answers4

3

So if you're not allowed to add maven dependency of

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.4.0</version>
</dependency>

then you can write custom serializer for your class as below:

public class MyCustomSerializer extends JsonSerializer<Person> {

    @Override
    public void serialize(Person value, JsonGenerator jgen,
                          SerializerProvider provider) throws IOException,
            JsonProcessingException {
        if (value != null) {
            jgen.writeStartObject();

            jgen.writeStringField("date", DateTimeFormatter.ofPattern("yyyy-MM-dd").format(value.getBirthDate()));
            // parse other fields here manually

            jgen.writeEndObject();
        }
    }
}

and add below annotation to your Person.class:

@JsonSerialize(using = MyCustomSerializer.class)

It parses date as :

{
    "date": "2018-08-23"
}

If you don't want to write custom serializer another alternative is to using @JsonGetter annotation but either you should add @JsonIgnore to birthDate field or give value as same name to @JsonGetter("sameAsFieldName"). If you give another value to your @JsonGetter and don't add @JsonIgnore to your field, it serialize both your field and @JsonGetter return value.

You can add a method to your class as below:

@JsonGetter("birthDate")
public String getSerializedLocalDate() {
    return DateTimeFormatter.ofPattern("yyyy-MM-dd").format(this.getBirthDate());
}
Emre Savcı
  • 3,034
  • 2
  • 16
  • 25
  • is there no way to add an annotation to the relevant field only, and provide a serializer for that? – Walter Tross Aug 23 '18 at 21:00
  • @WalterTross of course, you can add `@JsonSerialize` annotation to a field and change `JsonSerializer`. But for our example LocalDate, if we give `JsonSerializer` and override serialize method as `jgen.writeStringField("date", DateTimeFormatter.ofPattern("yyyy-MM-dd").format(value.getBirthDate()));` , the serialized person includes `{ "birthDate" : { "date": "2018-08-24" } }` and it is not desired output. If we serialize just `jgen.writeString(DateTimeFormatter.ofPattern("yyyy-MM-dd").format(value.getBirthDate()));` it throws exception on runtime while serializing. – Emre Savcı Aug 23 '18 at 21:54
  • thanks, but there ought to be an easier way. Anyway, I would edit the answer, if I were you – Walter Tross Aug 23 '18 at 21:57
  • @WalterTross I add another alternative solution using JsonGetter. So you don't have to write custom serializer. But there are some key points needs to be consider. – Emre Savcı Aug 23 '18 at 22:16
1
public void saveListToJSON(String fileName) throws
            MyCustomException {

        DateTimeFormatter dtf
        = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        ObjectNode newStud;
        int id;
        String name;
        String email;
        String birthDate;

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
        ArrayNode root = objectMapper.createArrayNode();

        for (Person person : this.persons) {
            newStud = objectMapper.createObjectNode();
            id = person.getId();
            name = person.getName();
            email = person.getEmail();
            birthDate = person.getBirthDate().format(dtf);
            newStud.put("id", id);
            newStud.put("name", name);
            newStud.put("email", email);
            newStud.put("birthDate", birthDate);
            root.add(newStud);
        }

        try {
            objectMapper.writeValue(new File(fileName), root);
        } catch (IOException ex) {
            throw new MyCustomException("The given output file "
                    + fileName + " cannot be opened for writing.");
        }
sasieightynine
  • 434
  • 1
  • 5
  • 16
0

If I remember correctly, Jackson uses the mutators to create the JSON. In the getBirthDate mutator return a formatted date using SimpleDateFormatter or one of the many Text formatters.

class Person {
   private int id;
   private String name;
   private String email;
   private LocalDate birthDate;

   // MUTATORS HERE


   @JsonProperty("birthDate")
   public String getStringBirthDate(){
      // Turn to formatted (yyyy-mm-dd) String
   } 
}
Dan
  • 979
  • 1
  • 8
  • 29
-1

You can do it by using ObjectMapper

public static void main(String[] args) throws IOException {
    ObjectMapper mapper = new ObjectMapper();

    // to enable java.time serializing
    mapper.registerModule(new JavaTimeModule());
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

    // just to format output
    mapper.enable(SerializationFeature.INDENT_OUTPUT);

    // JSON - your provided JSON file
    Person[] arrayList
            = mapper.readValue(JSON, Person[].class);

    System.out.println(mapper.writeValueAsString(arrayList));
}
Mr. Skip
  • 417
  • 4
  • 10