Nolequen
Nolequen

Reputation: 4257

Jackson Serialization: Unwrap collection elements using

Is there a way to serialize collection and its elements unwrapped?

For example I want to serialize unwrapped all components:

class Model {

  @JsonProperty
  @JsonUnwrapped
  Collection<Object> components;

  Model(Collection<Object> components) {
    this.components = components;
  }

  static class Component1 {
    @JsonProperty
    String stringValue;

    Component1(String stringValue) {
      this.stringValue= stringValue;
    }
  }

  static class Component2 {
    @JsonProperty
    int intValue;

    Component2(int intValue) {
      this.intValue= intValue;
    }
  }

  public static void main(String[] args) throws JsonProcessingException {
    Model model = new Model(Arrays.asList(new Component1("something"), new Component2(42)));
    String json = new ObjectMapper().writeValueAsString(model);
    System.out.println(json);
  }
}

Expected:

{"stringValue":"something","intValue":42}

But actual result is:

{"components":[{"stringValue":"something"},{"intValue":42}]}

Upvotes: 5

Views: 2580

Answers (3)

Manos Nikolaidis
Manos Nikolaidis

Reputation: 22234

I can't see a way to do that without custom serialization. I recommend these 2 serializers:

class ValueSerializer extends JsonSerializer<Object> {
    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider sers) throws IOException {
        for (Field field : value.getClass().getDeclaredFields()) {
            try {
                field.setAccessible(true);
                gen.writeObjectField(field.getName(), field.get(value));
            } catch (IllegalAccessException ignored) {
            }
        }
    }
}

class ModelSerializer extends JsonSerializer<Model> {
    @Override
    public void serialize(Model model, JsonGenerator gen, SerializerProvider sers) throws IOException {
        gen.writeStartObject();
        for (Object obj : model.getComponents()) {
            gen.writeObject(obj);
        }
        gen.writeEndObject();
    }
}

Notice how we don't call writeStartObject() at ValueSerializer so no extra curly braces from here, neither from writeObjectField. On the other hand in ModelSerializer writheStartObject adds curly braces, and then we dump within them each object in components

You'd also need to annotate serializable classes to use these serializers e.g.

@JsonSerialize(using = ValueSerializer.class)
class Component1 {

@JsonSerialize(using = ValueSerializer.class)
class Component2 {

@JsonSerialize(using = ModelSerializer.class)
class Model {

Upvotes: 1

belbix
belbix

Reputation: 178

Not elegant, but work code. Sure about unique naming of key values

 @JsonProperty
@JsonSerialize(using = CollectionSerializer.class)
Collection<Object> components;

static class CollectionSerializer extends JsonSerializer<Object> {

    @Override
    public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
        jsonGenerator.writeStartObject();
        if (o instanceof Collection) {
            Collection c = (Collection) o;
            for (Object el : c) {
                if (el instanceof Component1) {
                    jsonGenerator.writeStringField("stringValue", ((Component1) el).stringValue);
                }
                if (el instanceof Component2) {
                    jsonGenerator.writeNumberField("intValue", ((Component2) el).intValue);
                }
            }
        }
        jsonGenerator.writeEndObject();
    }
}

Upvotes: 0

Nolequen
Nolequen

Reputation: 4257

Custom serializer might help:

  class ModelSerializer extends JsonSerializer<Model> {

    @Override
    public void serialize(Model model, JsonGenerator generator, SerializerProvider serializers) throws IOException {
      generator.writeStartObject();

      JsonSerializer<Object> componentSerializer = serializers.findValueSerializer(getClass());
      JsonSerializer<Object> unwrappingSerializer = componentSerializer.unwrappingSerializer(NameTransformer.NOP);
      unwrappingSerializer.serialize(this, generator, serializers);

      generator.writeEndObject();
    }
  }

Upvotes: 3

Related Questions