How can I make jackson, if even possible without making my own complex serializer of org.apache.commons.lang3.tuple.Pair, to serialize both my left and right object in a proper way. So for example I have to following code:
public class MyPairTest {
public static void main(String[] args) {
Address address1 = new Address("Axel Street 1", "London");
Address address2 = new Address("Axel Street 2", "Copenhagen");
Map<String, Pair> map = new HashMap<>();
map.put("Address", Pair.of(address1, address2));
Pair p = Pair.of(address1, address2);
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
try {
String serializeMapWithPair = mapper.writeValueAsString(map);
String serializePair = mapper.writeValueAsString(p);
System.out.println(serializeMapWithPair);
System.out.println(serializePair);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
Gson gson = new Gson();
System.out.println(gson.toJson(p));
}
}
And the resulting output is:
{
"Address" : {
"com.example.api.beans.Address#224edc67" : {
"street" : "Axel Street 2",
"city" : "Copenhagen"
}
}
}
{
"com.example.api.beans.Address#224edc67" : {
"street" : "Axel Street 2",
"city" : "Copenhagen"
}
}
{"left":{"street":"Axel Street 1","city":"London"},"right":{"street":"Axel Street 2","city":"Copenhagen"}}
So the first two outputs are serializing Pair with jackson ObjectMapper where as the last is with Gson, I want to achieve the result as in the Gson example, but with jackson and without the left and right part. The Gson example I got from this jira improvement suggestion and I know that they state the following "Commons-lang has no dependencies, so Jackson or anything similar can not be used." But was wondering if Jackson them self have made a serializer or the kind for this, cannot find anything like it out there so maybe there is no simple solution to this with jackson? Or should I just drop it and use Gson, and then just manually manipulate and remove the left and right parts?
You can write a custom serializer:
private static final class PairSerializer extends JsonSerializer<Pair> {
#Override
public void serialize(Pair value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartObject();
gen.writeObjectField("left", value.getLeft());
gen.writeObjectField("right", value.getRight());
gen.writeEndObject();
}
}
and register it with your ObjectMapper:
mapper.registerModule(new SimpleModule().addSerializer(Pair.class, new PairSerializer()));
Anyhow I think that I will just go with implementing my own generic class with a before and after field and then serialize that, works even how I want it to be in the end, so I end up with something like the following class:
public class MyPair<T> {
private T before;
private T after;
private MyPair(T before, T after) {
this.before = before;
this.after = after;
}
public static <T> MyPair<T> of(final T before, final T after) {
return new MyPair<>(before, after);
}
public T getBefore() {
return this.before;
}
public T getAfter() {
return this.after;
}
public void setBefore(T before) {
this.before = before;
}
public void setAfter(T after) {
this.after = after;
}
}
and then I can just call MyPair.of(address1, address2) and get that serialized as
{
"Address" : {
"before" : {
"street" : "Axel Street 1",
"city" : "London"
},
"after" : {
"street" : "Axel Street 2",
"city" : "Copenhagen"
}
}
}
Which is what I was expecting :). Just need to make sure that it is called with the same object type, else throw compile-time error, seems that does not work because right now of() can be called with two different types.
To answer the question with details:
Jackson treats the Pair<K,V> implements Map.Entry<K,V> as an item of a map and generates wrong output.
The only way to bypass is to write your own Pair class which NEVER implements the Map.Entry interface.
Related
I'm trying to implement some simple Json serialization functionality but I'm having a hard time coping with the massive complexity of Gson.
So basically I have a bunch of Entity classes which reference each other with a lot of circular reference. To serialize this structure to JSON I want to keep track of the objects already serialized. The Entity classes all implement an interface called Identified which has one method String getId() giving a globally unique id. So during serializiation of one root element, I want to store all encountered ids in a Set and decide based on that set, whether to fully serialize an object or to serialize that object as a stub
"something": {
"__stub": "true",
"id": "..."
}
This shouldn't be too hard a task in my opinion, but I haven't been able to put something together. Using a custom JsonSerializer I'm not able to have an object (that is not to be serialized as a stub) serialized in the default way. Using a TypeAdapterFactory, I'm not able to access the actual object.
So, any help on how to achieve this, would be very nice!
Best regards
I'm not sure if it's possible easily. As far as I know, Gson promotes immutability and seems to lack custom serialization context support (at least I don't know if it's possible to use custom JsonSerializationContext wherever possible). Thus, one of possible work-around might be the following:
IIdentifiable.java
A simple interface to request a custom ID for an object.
interface IIdentifiable<ID> {
ID getId();
}
Entity.java
A simple entity that can hold another entity references in two manners:
a direct dependency to a "next" entity;
a collection of references to other references.
final class Entity
implements IIdentifiable<String> {
#SerializedName(ID_PROPERTY_NAME)
private final String id;
private final Collection<Entity> entities = new ArrayList<>();
private Entity next;
private Entity(final String id) {
this.id = id;
}
static Entity entity(final String id) {
return new Entity(id);
}
#Override
public String getId() {
return id;
}
Entity setAll(final Entity... entities) {
this.entities.clear();
this.entities.addAll(asList(entities));
return this;
}
Entity setNext(final Entity next) {
this.next = next;
return this;
}
}
IdentitySerializingTypeAdapterFactory.java
I didn't find any easier way rather than making it a type adapter factory, and, unfortunately, this implementation is totally stateful and cannot be reused.
final class IdentitySerializingTypeAdapterFactory
implements TypeAdapterFactory {
private final Collection<Object> traversedEntityIds = new HashSet<>();
private IdentitySerializingTypeAdapterFactory() {
}
static TypeAdapterFactory identitySerializingTypeAdapterFactory() {
return new IdentitySerializingTypeAdapterFactory();
}
#Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
final boolean isIdentifiable = IIdentifiable.class.isAssignableFrom(typeToken.getRawType());
final TypeAdapter<T> delegateAdapter = gson.getDelegateAdapter(this, typeToken);
if ( isIdentifiable ) {
return new TypeAdapter<T>() {
#Override
public void write(final JsonWriter out, final T value)
throws IOException {
final IIdentifiable<?> identifiable = (IIdentifiable<?>) value;
final Object id = identifiable.getId();
if ( !traversedEntityIds.contains(id) ) {
delegateAdapter.write(out, value);
traversedEntityIds.add(id);
} else {
out.beginObject();
out.name(REF_ID_PROPERTY_NAME);
writeSimpleValue(out, id);
out.endObject();
}
}
#Override
public T read(final JsonReader in) {
throw new UnsupportedOperationException();
}
};
}
return delegateAdapter;
}
}
The type adapter firstly tries to check if a given entity has been already traversed. If yes, then it's writing a special object similar to your one (the behavior could be rewritten via the strategy pattern, of course, but let it be more simple). If no, then the default type adapter is obtained, and then the given entity is delegated to that adapter, and registered as a traversed one if the latter type adapter succeeds.
The rest
And here is the rest.
SystemNames.java
final class SystemNames {
private SystemNames() {
}
private static final String SYSTEM_PREFIX = "__$";
static final String ID_PROPERTY_NAME = SYSTEM_PREFIX + "id";
static final String REF_ID_PROPERTY_NAME = SYSTEM_PREFIX + "refId";
}
GsonJsonWriters.java
final class GsonJsonWriters {
private GsonJsonWriters() {
}
static void writeSimpleValue(final JsonWriter writer, final Object value)
throws IOException {
if ( value == null ) {
writer.nullValue();
} else if ( value instanceof Double ) {
writer.value((double) value);
} else if ( value instanceof Long ) {
writer.value((long) value);
} else if ( value instanceof String ) {
writer.value((String) value);
} else if ( value instanceof Boolean ) {
writer.value((Boolean) value);
} else if ( value instanceof Number ) {
writer.value((Number) value);
} else {
throw new IllegalArgumentException("Cannot handle values of type " + value);
}
}
}
Testing
In the test below, there are three entities identified by FOO, BAR, and BAZ string identifiers. All of them have circular dependencies like this:
FOO -> BAR, BAR -> BAZ, BAZ -> FOO using the next property;
FOO -> [BAR, BAZ], BAR -> [FOO, BAZ], BAZ -> [FOO, BAR] using the entities property.
Since the type adapter factory is stateful, even GsonBuilder must be created from scratch thus not having "spoiled" state between use. Simply speaking, once a Gson instance is used once, it must be disposed, so there are GsonBuilder suppliers in the test below.
public final class Q41213747Test {
private static final Entity foo = entity("FOO");
private static final Entity bar = entity("BAR");
private static final Entity baz = entity("BAZ");
static {
foo.setAll(bar, baz).setNext(bar);
bar.setAll(foo, baz).setNext(baz);
baz.setAll(foo, bar).setNext(foo);
}
#Test
public void testSerializeSameJson() {
final String json1 = newSerializingGson().toJson(foo);
final String json2 = newSerializingGson().toJson(foo);
assertThat("Must be the same between the calls because the GSON instances are stateful", json1, is(json2));
}
#Test
public void testSerializeNotSameJson() {
final Gson gson = newSerializingGson();
final String json1 = gson.toJson(foo);
final String json2 = gson.toJson(foo);
assertThat("Must not be the same between the calls because the GSON instance is stateful", json1, is(not(json2)));
}
#Test
public void testOutput() {
out.println(newSerializingGson().toJson(foo));
}
private static Gson newSerializingGson() {
return newSerializingGson(GsonBuilder::new);
}
private static Gson newSerializingGson(final Supplier<GsonBuilder> defaultGsonBuilderSupplier) {
return defaultGsonBuilderSupplier.get()
.registerTypeAdapterFactory(identitySerializingTypeAdapterFactory())
.create();
}
}
{
"__$id": "FOO",
"entities": [
{
"__$id": "BAR",
"entities": [
{
"__$refId": "FOO"
},
{
"__$id": "BAZ",
"entities": [
{
"__$refId": "FOO"
},
{
"__$refId": "BAR"
}
],
"next": {
"__$refId": "FOO"
}
}
],
"next": {
"__$refId": "BAZ"
}
},
{
"__$refId": "BAZ"
}
],
"next": {
"__$refId": "BAR"
}
}
Deserialization of such stuff looks really complicated. At least using GSON facilities.
Do you consider rethinking your JSON model in order to avoid circular dependencies in JSON output? Maybe decomposing your objects to a single map like Map<ID, Object> and making references transient or #Expose-annotated could be easier for you to use? It would simplify deserialization as well.
In my Android app I have json, which looks like :
{
"Records": [
{
"RowIndex": "0",
"NameValue": {
"Name": "PropertyName1",
"Value": "PropertyValue1"
}
}{
"RowIndex": "1",
"NameValue": {
"Name": "PropertyName2",
"Value": "PropertyValue2"
}
}
]
}
I need to parce this json to object, which looks like:
public class MyClass {
public String PropertyName1;
public String PropertyName2;
}
And result after parsing should be:
public String PropertyName1 = "PropertyValue1";
public String PropertyName2 = "PropertyValue2";
Basically, the first json is equivalent of:
{
"PropertyName1" : "PropertyValue1",
"PropertyName2" : "PropertyValue2"
}
Question: How can I parce first json without usage swith/case to search for the necessary Property?
You'll have to go down the dark path of reflection I'm afraid.
you can parse the json into an intermediary object which has a map for namevalue.
then you use the below code (ofcourse just copy paste the bits you need) to loop over the map of key/value pairs. for each key look up the field you want, and set it. If you're guaranteed only to need to set public variables then you can use getFields and can skip the setAccessible.
public class Test {
public static void main(String[] argv) {
MyClass myClass = new MyClass();
Class<?> classObject = myClass.getClass();
// Field fields[] = classObject.getFields(); // if you want to get only public fields.
Field fields[] = classObject.getDeclaredFields(); // any field
for(Field f : fields) {
System.out.println(f.getName());
try {
// if member is private: security managers may object but the default java allows it
f.setAccessible(true);
f.set(myClass, "abc");
} catch (IllegalAccessException e) {
// handle access exception:
e.printStackTrace();
}
}
System.out.println("prop 1: " + myClass.PropertyName1);
System.out.println("prop 2: " + myClass.PropertyName2);
}
public static class MyClass {
public String PropertyName1;
private String PropertyName2;
}
}
Actually.. there is a non-reflect way but that will replace your implementation of the object you have.
If you change your class:
public class MyClass {
public String PropertyName1;
public String PropertyName2;
}
to
public class MyClass {
private Map<String, String> properties = new HashMap<String, String>();
public void setProperties(Map<String, String> props) { this.properties = props; }
public String getPropertyName1() {
return lookupProperty("PropertyName1");
}
public String getPropertyName2() {
return lookupProperty("PropertyName2");
}
private String lookupProperty(String property) {
if (properties.containsKey(property) {
return properties.get(property);
} else {
return null;
}
}
}
then you could parse the name value map into a map, and construct a myclass with it.
just listing it for completeness, though changing your domain model to fit a json input is not ideal.
I would recommend either way to do the input parsing, and then copy over the model into your actual domain object rather than using the json-model in your application. that way if the json model ever changes, your domain model will not change.
One method I can think of (which doesn't sound too great) is to actually make an object that matches the JSON response you get back. Then, map THAT NameValue object to MyClass
So, something like
public class NameValue {
public string Name;
public String Value;
public MyClass getMyClass(){
MyClass myClass = new MyClass();
myClass.PropertyName2 = Value;
return myClass;
}
}
You can come up with a better way to map it, obviously. But this is just an example of something I might do if I was given a response JSON I didn't particularly care for. You can similarly reverse it (have MyClass be able to create a NameValue object) so you can send data back in the correct format.
I have a Pojo that contains one member displayPropsJsonwhich is a clientside json string. It is validated with a JSON schema before storing on the server.
i.e.
public class Item {
Long id; //23
String name; //"itemsName"
String displayPropsJson; // "{\"bold\" : true, \"htmlAllowed\" : true, \"icon\" :\"star.jpg\" }"
}
I'd like the serialized version of this to output the displayPropsJson as displayProps sub object for example:
{
"id" :23,
"name: : "itemsName",
"displayProps" : {
"bold" : true,
"htmlAllowed" : true,
"icon" : "star.jpg"
}
}
How can I do this with a Jackson serializer that outputs elements and the json string as json?
The displayPropsJson will vary but is always valid json.
You can consider two options apart of creating a custom serializer.
Use the #JsonRawString annotation to mark a String field that
should be serialized as is without quoting of characters.
Make the ObjectMapper available inside your object instance (consider value injection) and provide a getter method that returns JsonNode deserialized from your json string value.
Here is an example demonstrating both:
public class JacksonRawString {
public static class Item {
final private ObjectMapper mapper;
public Long id = 23l;
public String name = "itemsName";
#JsonRawValue
public String displayPropsJson = "{\"bold\" : true, \"htmlAllowed\" : true, " +
"\"icon\" :\"star.jpg\" }";
public JsonNode getDisplayPropsJson2() throws IOException {
return mapper.readTree(displayPropsJson);
}
public Item(ObjectMapper mapper) {
this.mapper = mapper;
}
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
System.out.println(
mapper.writerWithDefaultPrettyPrinter().writeValueAsString(new Item(mapper)));
}
}
Output:
{
"id" : 23,
"name" : "itemsName",
"displayPropsJson" : {"bold" : true, "htmlAllowed" : true, "icon" :"star.jpg" },
"displayPropsJson2" : {
"bold" : true,
"htmlAllowed" : true,
"icon" : "star.jpg"
}
}
Note that the displayPropsJson2 get pretty output since it was serialized as JsonNode
Yes I am sure this can be done with custom Jackson serializer. Another thing you could do is implement JsonSerializable,
}
Yet another possibility is to implement the JsonSerializable interface
A final possibility would be to switch libraries and use Google's GSON, which makes it easy to serialize objects into and out of json.
From the land of .NET I have a generic class define like so..
public class SyncWrapper<T, I>
{
public IList<T> Data { get; set; }
public IList<I> DeleteIds { get; set; }
public DateTime LastSyncDateTime { get; set; }
}
I was able to create an instance of this object from json by simply calling ...
JsonConvert.DeserializeObject<SyncWrapper<T, Guid>>(json);
Now I've been given the task of porting this code over to Java/Android. Having never touched Java before, I've a lot to learn!
Anyway, so far I've tried Gson and Jackson to get the object from json but no joy. I think that I won't be able to call andthing with the <T> involved gson.fromJson(json, SyncWrapper<T, UUID>.class) for example as there is a problem with type Erasure!
My efforts so far have looked like this....
Gson
Gson gson = new Gson();
SyncWrapper<MyClass, UUID> result = gson.fromJson(json, new TypeToken<SyncWrapper<MyClass, UUID>>() { }.getType());
This compiles but the result is an empty SyncWrapper
Jackson
ObjectMapper mapper = new ObjectMapper();
SyncWrapper<MyClass, UUID> result = mapper.readValue(json, new TypeReference<SyncWrapper<MyClass, UUID>>() { });
This compiles but crashes the app when executed!!!
My Java version of SyncWrapper....
public class SyncWrapper<T, I> {
private DateTime lastSyncDateTime;
private Collection<T> data;
private Collection<I> deleteIds;
public Collection<T> getData() {
return data;
}
public void setData(Collection<T> data) {
this.data = data;
}
public Collection<I> getDeleteIds() {
return deleteIds;
}
public void setDeleteIds(Collection<I> deleteIds) {
this.deleteIds = deleteIds;
}
public DateTime getLastSyncDateTime() {
return lastSyncDateTime;
}
public void setLastSyncDateTime(DateTime lastSyncDateTime) {
this.lastSyncDateTime = lastSyncDateTime;
}
}
I've been really thrown in at the deep end by the powers that be (all programming is the same isn't it?), so any help really appreciated.
I'm not precious about which library I use (Gson, Jackson, etc)
Update
An example of the Json that is to be deserialized...
{
"Data": [
{
"Name": "Company A",
"Id": "7d5d236c-c2b5-42dc-aea5-99e6752c8a52"
},
{
"Name": "Company B",
"Id": "44444444-0000-0000-0000-444444444444"
},
{
"Name": "Company C",
"Id": "249a4558-05c6-483f-9835-0056804791c9"
}
],
"DeleteIds": [
"5f7873a6-b2ee-4566-9714-1577b81384f4",
"1f224a39-16c3-441d-99de-8e58fa8f31c2"
],
"LastSyncDateTime": "\/Date(1393580073773+0000)\/"
}
..or this (more often than not, the DeleteIds will be null)...
{
"Data": [
{
"Name": "Company A",
"Id": "7d5d236c-c2b5-42dc-aea5-99e6752c8a52"
},
{
"Name": "Company B",
"Id": "44444444-0000-0000-0000-444444444444"
},
{
"Name": "Company C",
"Id": "249a4558-05c6-483f-9835-0056804791c9"
}
],
"DeleteIds": null,
"LastSyncDateTime": "\/Date(1393580073773+0000)\/"
}
For the above json I would be mapping to a SyncWrapper where T is Company...
public class Company extends ModelBase {
private String name;
public Company(UUID id, String name) {
super(id);
setName(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Here's the issues:
Your field names in your Java classes don't match the field names in the JSON; capitalization matters. This is why you're getting back absolutely nothing after parsing.
I'm going to go with Gson examples simply because I know that off the top of my head. You can do the same things in Jackson, but I'd need to look them up:
public class SyncWrapper<T, I> {
#SearializedName("LastSyncDateTime")
private DateTime lastSyncDateTime;
#SearializedName("Data")
private Collection<T> data;
#SearializedName("DeleteIds")
private Collection<I> deleteIds;
This tells Gson which fields in Java map to the fields in JSON. You could also go with a field naming policy instead, since it looks like all your fields are upper camel case:
Gson g = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.build();
Now your fields will match up. The next issue is going to be that UUID class. That class in Java is not a string; it's a class that generates UUIDs. Just use String for the type that holds it in your Java class.
The DateTime class ... same issue. And on top of that you've got a bit of a weird value in your JSON for the date. You'll either want to store that as a String as well, or you're going to have to write a custom deserializer to deal with it.
With those changes, I think you're good to go.
Edit to add from the comments: If you really need the Java UUID class rather than just the String representation, you can write a chunk of code that takes care of this for you:
class UUIDDeserializer implements JsonDeserializer<UUID>
{
#Override
public UUID deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException
{
return UUID.fromString(je.getAsString());
}
}
You can then register this with Gson:
Gson g = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.registerTypeAdapter(UUID.class, new UUIDDeserializer())
.build();
This will populate the UUID typed fields in your class with UUID instances. This is the same thing you'd need to do with that funky date value.
I suggest using Jackson for this; it has a more clear API and does not require creating a new type as Gson (where you have to extend a class to be able to do that).
Example:
public static <T> T fromJsonToGenericPojo(
String json, Class<?> classType, Class<?>... genericTypes) {
JavaType javaType = TypeFactory.defaultInstance()
.constructParametricType(classType, genericTypes);
try {
return OBJECT_MAPPER.readValue(json, javaType);
} catch (IOException e) {
LOGGER.error(e.getMessage(), e);
throw new IllegalArgumentException(e);
}
}
I have a class like this:
public class DeserializedHeader
int typeToClassId;
Object obj
I know what type of object obj is based on the typeToClassId, which is unfortunately only known at runtime.
I want to parse obj out based on typeToClassId - what's the best approach here? Annotations seem like they're out, and something based on ObjectMapper seems right, but I'm having trouble figuring out what the best approach is likely to be.
Something along the lines of
Class clazz = lookUpClassBasedOnId(typeToClassId)
objectMapper.readValue(obj, clazz)
Obviously, this doesn't work since obj is already deserialized... but could I do this in 2 steps somehow, perhaps with convertValue?
This is really complex and painful problem. I do not know any sophisticated and elegant solution, but I can share with you my idea which I developed. I have created example program which help me to show you how you can solve your problem. At the beginning I have created two simple POJO classes:
class Product {
private String name;
// getters/setters/toString
}
and
class Entity {
private long id;
// getters/setters/toString
}
Example input JSON for those classes could look like this. For Product class:
{
"typeToClassId" : 33,
"obj" : {
"name" : "Computer"
}
}
and for Entity class:
{
"typeToClassId" : 45,
"obj" : {
"id" : 10
}
}
The main functionality which we want to use is "partial serializing/deserializing". To do this we will enable FAIL_ON_UNKNOWN_PROPERTIES feature on ObjectMapper. Now we have to create two classes which define typeToClassId and obj properties.
class HeaderType {
private int typeToClassId;
public int getTypeToClassId() {
return typeToClassId;
}
public void setTypeToClassId(int typeToClassId) {
this.typeToClassId = typeToClassId;
}
#Override
public String toString() {
return "HeaderType [typeToClassId=" + typeToClassId + "]";
}
}
class HeaderObject<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
#Override
public String toString() {
return "HeaderObject [obj=" + obj + "]";
}
}
And, finally source code which can parse JSON:
// Simple binding
Map<Integer, Class<?>> classResolverMap = new HashMap<Integer, Class<?>>();
classResolverMap.put(33, Product.class);
classResolverMap.put(45, Entity.class);
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
String json = "{...}";
// Parse type
HeaderType headerType = mapper.readValue(json, HeaderType.class);
// Retrieve class by integer value
Class<?> clazz = classResolverMap.get(headerType.getTypeToClassId());
// Create dynamic type
JavaType type = mapper.getTypeFactory().constructParametricType(HeaderObject.class, clazz);
// Parse object
HeaderObject<?> headerObject = (HeaderObject<?>) mapper.readValue(json, type);
// Get the object
Object result = headerObject.getObj();
System.out.println(result);
Helpful links:
How To Convert Java Map To / From JSON (Jackson).
java jackson parse object containing a generic type object.