SlimyTadpole
SlimyTadpole

Reputation: 329

Dynamic property name for Jackson serialization

I have a number of classes I'm trying to serialize to JSON. They're very similar, so I'm wondering if there's a better way to do this than creating 3 very-near-identical classes each time this pattern shows up:

public class SomethingFoo {
  @JsonProperty("foo")
  Identifier foo

  // other properties
}

public class SomethingBar {
  @JsonProperty("bar")
  Identifier bar

  // other properties
}

public class SomethingBaz {
  @JsonProperty("baz")
  Identifier baz

  // other properties
}

Identifier is a class that only contains one field:

public class Identifier {
  @JsonProperty("name")
  String name = "";
}

What I would like to do, is change Identifier to something like:

public class Identifier {
  @JsonProperty("name")
  String name = "";

  @JsonIgnore
  IdentifierType type;
}

public Enum IdentifierType {
  FOO, BAR, BAZ;
}

I would then like to use the 'type' field, within Identifier, to change the name of the Identifier field in the objects that contain those Identifiers.

I would then like to do replace SomethingFoo, SomethingBar, and SomethingBaz with this:

public class Something {
  @JsonProperty(??????)
  Identifier name

  // other properties
}

I would like the property name of Something.identifier to be "foo", "bar", or "baz", depending on the value of Identifier.type.

Alternatively, I could also sub-class Identifier, rather than use an Enum.

The problem is that I'm trying to use a value within an object (or the type of the object, if sub-classes are used) to inform the property name in the Identifier's containing class. So I don't know if what I want to do is even possible without changing every class that contains an Identifier.

Edit:

The problem is I want 'Something' to be Serialized as one of these (Based on Identifier's enum type (or subclass, if that's a better way to accomplish this)):

{
  "foo" : { 
     "name" : "blahblahblah"
  }
}

{
  "bar" : { 
     "name" : "blahblahblah"
  }
}

{
  "baz" : { 
     "name" : "blahblahblah"
  }
}

Upvotes: 5

Views: 16587

Answers (2)

tarush grover
tarush grover

Reputation: 61

This can be done using JsonSerializer with your custom implementation. Please refer to below code.

 @JsonSerialize(using = Term.TermSerializer.class)
 public class Term {

//@JsonProperty("field")
private String field;

public Term() {

}

public Term(String field){
    this.field = field;
}

public String getField() {
    return field;
}

public void setField(String field) {
    this.field = field;
}

public static class TermSerializer extends JsonSerializer<Term> {
    @Override public void serialize(Term value, JsonGenerator jsonGenerator, SerializerProvider provider)
        throws IOException {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("price", value.getField());// dynamic field name
        jsonGenerator.writeEndObject();
    }
}

public static void main(String[] args){
    Term term = new Term("color");
    ObjectMapper mapper = new ObjectMapper();
    mapper.enable(SerializationFeature.INDENT_OUTPUT);
    mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
    String jsonString = null;
    try {
        jsonString = mapper.writeValueAsString(term);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
    }
    System.out.println(jsonString);
}

}

Upvotes: 6

mkobit
mkobit

Reputation: 47319

Attribute values for Annotations must be constant, so you cannot change that. I'm not sure I see a problem here. Why would your solution below not work? The @JsonProperty just tells the ObjectMapper what the name of the Json field should be.

public class Something {
  @JsonProperty(value = "id")
  Identifier identifier

  // other properties
}

If you serialized one of these objects it would come out to something this:

{
  "id": FOO,
  ...
}

Without having the value - "id" for @JsonProperty it would just use the field name:

{
  "identifier": FOO,
  ...
}

Jackson has a bunch of ways to customize it serializes and deserializes object. If you want the serialization to have more information (say if you add any fields to your Enum) or want to change how it was serialized there are ways to do that.. For Example, if you wanted the Enum to be serialized as an Object in Jackson 2.1.2 @JsonFormat you could do:

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum Identifier {...}

EDIT: I don't think serializing the data into the format described above necessarily makes sense as you are not really representing the object as JSON anymore as you are representing it as a different object. You already have a field on the object that discriminates what the Identifier for that object is and you can use that anywhere else. If you really wanted to serialize the data into the way you have described above, I believe you would have to implement your own JsonSerializer for that type like this (at least for Jackson 2.1):

public SomethingSerializer extends JsonSerializer<Something> {
  // Define serialization methods
  ...
}

And then extend SimpleModule, add the serializer, and register the module with ObjectMapper:

ObjectMapper mapper = new ObjectMapper();
SimpleModule testModule = new SimpleModule("MyModule", new Version(1, 0, 0, null));
testModule.addSerializer(new SomethingSerializer());
mapper.registerModule(testModule);

Example adapted from JacksonHowToCustomSerializers

Upvotes: 2

Related Questions