简体   繁体   中英

Jackson serialization: Wrap fields

There is a good known case when we unwrap nested object and write its fields into the main object, and I need to make an inverse task.

I have a POJO:

class A {
    private String id = "id1";

    @JsonWrap("properties")
    private String property1 = "...";

    @JsonWrap("properties")
    private String property2 = "...";

    // getters and setters
}

Default serializer will produce as expected

{
    "id": "id1",
    "property1": "...",    
    "property2": "..."    
}

But, my JSON should match some specification, and to do that, I need to wrap property1 and property2 inside properties wrapper. So the result should looks like:

{
    "id": "id1",
    "properties": 
    {
        "property1": "...",
        "property2": "..."
    }
}

I don't want to change the structure of the POJO so I see 3 possible ways:

  1. Write custom serializer. But as it seems to me, to write such serializer will takes more efforts then serialize objects by hands.
  2. Create proxy Java object which will reflect the right structure of JSON, and serialize such proxy.
  3. Modify JSON after it have been generated. (I'm afraid it would be a great overhead for rereading and rewriting of JSON).

Does anybody make such Serializer or maybe know another options to generate JSON with the structure I need?

For custom serializer I want to reuse standard BeanSerializer, I dont want to write out all fields manually:

  1. Hide annotated fields.
  2. Write out bean, without annotated fields, but don't close object. (Don't call jgen.writeEndObject(); )
  3. Write out wrapped fields.
  4. Close object.

You need change your model.

@JsonSerialize(using = ASerializer.class)
class A {
    private String id;
    private String property1;
    private String property2;

    // getters and setters

    public static class ASerializer extends JsonSerializer<A> {
        @Override
        public void serialize(A value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
            jgen.writeStartObject();
            jgen.writeStringField("id", value.getId());
            jgen.writeObjectFieldStart("properties");
            jgen.writeStringField("property1", value.getProperty1());
            jgen.writeStringField("property2", value.getProperty2());
            jgen.writeEndObject();
            jgen.writeEndObject();
        }
    }
}

Run in main:

A a = new A();
a.setId("id1");
a.setProperty1("...");
a.setProperty2("...");
ObjectMapper mapper = new ObjectMapper();
ObjectWriter writer = mapper.writer();
String json = writer.writeValueAsString(a);
System.out.println(json);

output:

{"id":"id1","properties":{"property1":"...","property2":"..."}}

It sounds like you need to create a Custom Serializer: http://wiki.fasterxml.com/JacksonHowToCustomSerializers

Of course, if you are creating Java objects from a similar JSON structure you'll likely need to create a Custom Deserializer as well.

Remember, you can always use reflection to create a 'generic' serializer if you find many of your objects share a similar structure.

To get that functionality without altering your model, take a look at writing a custom serializer to accomplish what Jackson can't figure out natively. We annotate the model class A with specific directions to use a defined serializer, and then use the JsonGenerator to specifically define the structure we are after.

@JsonSerialize(using = ASerializer.class)
class A {
    private String field1;
    private String innerField1;
    private String innerField2;
    // getters and setters


    public static class ASerializer extends JsonSerializer<A> {
        @Override
        public void serialize(A value, JsonGenerator jgen, SerializerProvider provider) 
          throws IOException, JsonProcessingException {
            jgen.writeStartObject();
                jgen.writeStringField("field1", value.getField1());
                jgen.writeObjectFieldStart("wrapper");
                    jgen.writeStringField("innerField1", value.getInnerField1());
                    jgen.writeStringField("innerField2", value.getInnerField2());
                jgen.writeEndObject();
            jgen.writeEndObject();
        }
    }
}

I used a static inner class in this case, but feasibly you can place the Serializer wherever best fits your project structure based on visibility. For one-off special case serializers, this is what I tend to do.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM