Jackson serializes list of objects with empty objects

Viewed 552

I'm trying to serialize a very large object graph that uses @JsonView (I don't know if that's relevant or not). What I'm finding is that for a list of objects within the graph, Jackson is serializing a list of empty objects, such as [{},{},{}]. None of the attributes are in the string. All scalar attributes serialize just fine. I'm only having trouble with the lists.

I've verified several times that the attributes are being set in the objects. Part of my POJO looks like so:

public class ProfessionalData implements Serializable {
    @JsonProperty("collegeEducation")
    @JsonView(Views.Myview.class)
    @Valid
    private List<CollegeEducation> collegeEducation = new ArrayList<CollegeEducation>();
    @JsonProperty("managementCommittee")
    @JsonView(Views.Myview.class)
    @Valid
    private List<ManagementCommittee> managementCommittee = new ArrayList<ManagementCommittee>();
  //getters and setters
}

This is the ObjectMapper code:

@Override
public String convertToDatabaseColumn(@NotNull MyPojoItem item) {
    try {
        //Disable Default_view_Inclusion, so fields without a view annotation wont be included
        objectMapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
        objectMapper.setDateFormat(df);         //Transform the Object to String
        String result = objectMapper
                .writerWithView(Views.MyView.class)
                .writeValueAsString(item);
        return result;
    } catch (Exception ex) {
        LOG.error("Failed to convertToDatabaseColumn: " + ex.getMessage());
        return null;
    }
}

Can someone tell me what this usually means?

1 Answers

You could do something like this for both lists. A custom serializer to write your objects there from a List. For example, for the CollegeEducation list:

public class CollegeEducationSerializer extends JsonSerializer<List<CollegeEducation>> 
{
   @Override
   public void serialize(List<CollegeEducation> list, JsonGenerator json,
        SerializerProvider provider) throws IOException 
   {
      if (list == null || list.isEmpty())
         return;

      json.writeFieldName("CollegeEducationArray");
      json.writeStartArray();
      for (CollegeEducation ce : list) 
      {
         json.writeStartObject();
         json.writeObjectField("CollegeEducation", ce);
         json.writeEndObject();    
      }

      json.writeEndArray();
   }
}

And specify it on the annotations:

@JsonSerialize(using = CollegeEducationSerializer.class)
private List<CollegeEducation> collegeEducation = new ArrayList<CollegeEducation>();

In order to read it, the same logic applies (a custom deserializer for both lists).


For something like a generic approach, you could also do something like this:

public class CustomListSerializer extends JsonSerializer<List<? extends ListElement>> 
{
   @Override
   public void serialize(List<? extends ListElement> list, JsonGenerator json,
        SerializerProvider provider) throws IOException 
   {
      if (list == null || list.isEmpty())
           return;

      json.writeFieldName(list.get(0).getElementType()+"Array");
      json.writeStartArray();

      for (ListElement le : list) 
      {
         json.writeStartObject();
         json.writeObjectField(le.getElementType(), le);
         json.writeEndObject();    
      }

      json.writeEndArray();
   }
}

Then you could use it for all your Lists:

@JsonSerialize(using = CustomListSerializer.class)
private List<CollegeEducation> collegeEducation = new ArrayList<>();

@JsonSerialize(using = CustomListSerializer.class)
private List<ManagementCommittee> managementCommittee = new ArrayList<>();

For this I added a customProperties Map, which may be prone to errors here. So the approach would be writing manually the desired fields, instead of using json.writeObjectField(le.getElementType(), le);

public abstract class ListElement
{
   public abstract String getElementType();
   public Map<String, String> properties = new HashMap<>();

   public final String name;
   public final int id; 
  
   public ListElement(String name, int id)
   {
      this.name = name;
      this.id = id;
   }
}

and, f.e:

public class CollegeEducation extends ListElement
{
   protected String location;
   protected String director;

   public CollegeEducation(String name, int id, String location, String director)
   {
       super(name,id);

       this.director = director;
       this.location= location;

       properties.put("director", director);
       properties.put("location", location);

      //...
   }

   public String getElementType()
   {
      return "CollegeEducation";
   }
   //...
}

//...

In the serializer:

  json.writeFieldName(list.get(0).getElementType()+"Array");
  json.writeStartArray();

  for (ListElement le : list) 
  {
     json.writeStartObject();
     json.writeFieldName(le.getElementType()+"-"+le.id);

     json.writeStartObject();
     json.writeStringField("name", le.name);
     for(Map.Entry<String,String> kv : le.properties.entrySet())
          json.writeStringField(kv.getKey(), kv.getValue());
     json.writeEndObject();    
    
     json.writeEndObject();    
  }

  json.writeEndArray();

You should get something like:

{
  "CollegeEducationArray":
  [ 
    { "CollegeEducation-22" : 
      {
        "name" : "Lauaxeta",
        "director" : "AJerk",
        "location": "Bilbao"
       }
     },
     { "CollegeEducation-55" : 
       {
        "name" : "Harvard",
        "director" : "OtherJerk",
        "location": "Rwanda"
       }  
     }
  ]
}

enter image description here

Related