fasterxml serialize using toString and deserialize using String constructor
Asked Answered
A

2

15

I have a POJO which looks something like this:

public class Thing
{
   private final int x;
   private final int y;
   private final int z;

   public Thing(String strThing)
   {
       // parse strThing which is in some arbitrary format to set x, y and z
   }

   @Override
   public String toString()
   {
       // return a string representation of thing
       // (same format as that parsed by the constructor)
   }

   @Override
   public boolean equals(Object obj) ...

   @Override
   public int hashCode() ...

}

and I want to use it as a key for a map (e.g. HashMap<Thing, SomeOtherPOJO>) which, when serializing to json, uses the toString() representation of Thing for the key, and when deserializing, uses the String constructor. Is this possible using something simple like jackson databind annotations? What would be the best way to tackle this?

Alga answered 22/7, 2015 at 16:48 Comment(0)
A
38

Through experimentation (I think the documentation could have been a little clearer on this) I have discovered that I can use the JsonCreator annotation on the String constructor and JsonValue on the toString() method to achieve what I want:

public class Thing
{
   private final int x;
   private final int y;
   private final int z;

   @JsonCreator
   public Thing(String strThing)
   {
       // parse strThing which is in some arbitrary format to set x, y and z
   }

   @Override
   @JsonValue
   public String toString()
   {
       // return a string representation of thing
       // (same format as that parsed by the constructor)
   }

   @Override
   public boolean equals(Object obj) ...

   @Override
   public int hashCode() ...

}
Alga answered 23/7, 2015 at 9:56 Comment(2)
efficient approachKasey
You would think such a lovely time saver would be in BOLD and pop out but alas the ability to write good documentation seems to be a skills that is dying.Lave
S
3

you can make a new class which extends JsonSerializer and override its serialize method. Write your implementation which you were suppose to write inside toString() method, into JsonGenerator's writeString() method as shown. You will have to use jackson-core-asl.jar and jackson-mapper-asl.jar in your project. Below is JsonThingSerializer.java class

import java.io.IOException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;


public class JsonThingSerializer extends JsonSerializer<Thing>{ //note use of generics

@Override
public void serialize(Thing myThing, JsonGenerator gen,
        SerializerProvider provider) throws IOException,
        JsonProcessingException {

    gen.writeString("I want this way.. which I was thinking to implement inside toString() method "+" "+myThing.getX()+" "+myThing.getY()+" "+myThing.getZ());
}

}

Use below annotations in your Thing.java

import org.codehaus.jackson.annotate.JsonAutoDetect;
import org.codehaus.jackson.map.annotate.JsonSerialize;

@JsonAutoDetect
@JsonSerialize(using=JsonThingSerializer.class)
public class Thing
{
   private final int x;
   private final int y;
   private final int z;

   public Thing(String strThing)
   {
       // parse strThing which is in some arbitrary format to set x, y and z
       //insert your own implementation to get x, y and z
       x=y=z=10;
   }

   @Override
   public String toString()
   {
       //no need to override this for json serialization.. mapper will not use it
   }

   @Override
   public boolean equals(Object obj){
     //you can have your own implementation
   }

   @Override
   public int hashCode() {
     //you can have your own implementation
    }

public int getX() {
    return x;
}

public int getY() {
    return y;
}

public int getZ() {
    return z;
}

}

You can test your code using below code.

import java.io.IOException;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;

public class TestJsonSerialization {

    public static void main(String[] args) {

        Thing thing =new Thing("your strThing which is in some arbitrary format to set x, y and z");
        ObjectMapper mapper =new ObjectMapper();
        try {
            String thingString = mapper.writeValueAsString(thing);
            System.out.println(thingString);
        } catch (JsonGenerationException e) {
            e.printStackTrace();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

Blow is the output :

"I want this way.. which I was thinking to implement inside toString() method 10 10 10"

Sherlynsherm answered 22/7, 2015 at 18:33 Comment(2)
I have found that there is a much simpler way to achieve this. See my answer.Alga
This is super helpful if you want to serialize/deserialize a member and you don't control the source for the member's class though (but you need to annotate the member with JsonSerialize or JsonDeserialize instead).Urchin

© 2022 - 2024 — McMap. All rights reserved.