GSON: Serialize object with java.util.TreeSet - java

How can I serialize a TreeSet properly? In order to give you an idea of what's not working I've set up this little demo project. The main goal is to print a JSON string of my QData object.
App.java
package de.company.gsonserializer;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
public class App
{
public static void main( String[] args )
{
QData qdata = new QData();
ArrayList<LData> arrayList = new ArrayList<LData>(1);
LData l = new LData();
Map<String, String> unsortedBuabList = new HashMap<String, String>();
for (int i = 0; i < 5; i++) {
unsortedBuabList.put("Key-" + i, "Value" + i);
}
SortedSet<Map.Entry<String, String>> sortedBuabList = new TreeSet<Map.Entry<String, String>>(
new Comparator<Map.Entry<String, String>>() {
public int compare(Map.Entry<String, String> e1, Map.Entry<String, String> e2) {
return e1.getValue().compareTo(e2.getValue());
}
});
sortedBuabList.addAll(unsortedBuabList.entrySet());
l.setBuabList(sortedBuabList);
arrayList.add(l);
qdata.setLocations(arrayList);
System.out.println( qdata.toString() );
}
}
QData.java
package de.it2media.gsonserializer;
import java.util.ArrayList;
import com.google.gson.Gson;
public class QData {
private ArrayList<LData> locations = new ArrayList<LData>(0);
public ArrayList<LData> getLocations() {
return locations;
}
public void setLocations(ArrayList<LData> locations) {
this.locations = locations;
}
#Override
public String toString(){
Gson gson = new Gson();
String thisObj = gson.toJson(this);
return thisObj;
}
}
LData.java
package de.it2media.gsonserializer;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Map.Entry;
public class LData {
private SortedSet<Entry<String, String>> buabList = new TreeSet<Map.Entry<String, String>>();
public SortedSet<Entry<String, String>> getBuabList() {
return buabList;
}
public void setBuabList(SortedSet<Entry<String, String>> buabList) {
this.buabList = buabList;
}
}
The output: {"locations":[{"buabList":[{},{},{},{},{}]}]}
Expected output would be something like: {"locations":[{"buabList":[{"key":"Key-0","value":"Value0"},{"key":"Key-1","value":"Value1"},{"key":"Key-2","value":"Value2"},{"key":"Key-3","value":"Value3"},{"key":"Key-4","value":"Value4"}]}]}
Do you might know why GSON is not working as I'd expect it to work?
Thanks for any help, highly appreciated!

The problem you are running into has nothing to do with the TreeSet, but rather with the fact that GSON does not know how to serialize a map Entry in the way that you would like. You therefore need to write a custom serializer for it, which looks something like this:
public static class EntrySerializer implements JsonSerializer<Entry<String, String>> {
#Override
public JsonElement serialize(Entry<String, String> entry, Type typeOfSrc, JsonSerializationContext context) {
JsonElement serializedKey = context.serialize(entry.getKey());
JsonElement serializedValue = context.serialize(entry.getValue());
JsonObject jsonObject = new JsonObject();
jsonObject.add("key", serializedKey);
jsonObject.add("value", serializedValue);
return jsonObject;
}
}
When you create the Gson object, you then need to register this custom serializer:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Entry.class, new EntrySerializer())
.create();
You can read more about custom serializers and deserializers in the GSON documentation.

Related

DynamoDBTypeConverted doesn't work in Java

Logs throw
Converter not found for EnhancedType(java.util.ArrayList<domains.requirementsAndDefinitionForTracingEvents.Event>)
error.
I decided to write a custom converter
package converters;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverter;
import domains.requirementsAndDefinitionForTracingEvents.Event;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class EventListToMapConverter implements DynamoDBTypeConverter<List<Map<String, ?>>, List<Event>> {
#Override
public List<Map<String, ?>> convert(List<Event> events) {
List<Map<String, ?>> convertedList = new ArrayList<>();
for(Event e: events) {
Map map = new HashMap<>();
map.put("eventCode", e.getEventCode());
map.put("remark", e.getRemark());
map.put("requiredOwn", e.isRequiredOwn());
convertedList.add(map);
}
return convertedList;
}
#Override
public List<Event> unconvert(List<Map<String, ?>> stringStringMap) {
List<Event> convertedList = new ArrayList<>();
for(Map<String, ?> map: stringStringMap) {
Event e = new Event();
e.setEventCode((String) map.get("eventCode"));
e.setRemark((String) map.get("remark"));
e.setRequiredOwn((Boolean) map.get("requiredOwn"));
convertedList.add(e);
}
return convertedList;
}
}
I added it like this
package domains.requirementsAndDefinitionForTracingEvents;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverted;
import converters.EventListToMapConverter;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
import java.util.ArrayList;
#DynamoDbBean
public class RequirementsAndDefinitionForTracingEvents {
public RequirementsAndDefinitionForTracingEvents() {
}
public RequirementsAndDefinitionForTracingEvents(ArrayList<Event> tracingEventCodes, AdditionalRules additionalRules) {
this.tracingEventCodes = tracingEventCodes;
this.additionalRules = additionalRules;
}
#DynamoDBTypeConverted(converter = EventListToMapConverter.class)
private ArrayList<Event> tracingEventCodes;
private AdditionalRules additionalRules;
public AdditionalRules getAdditionalRules() {
return additionalRules;
}
public ArrayList<Event> getTracingEventCodes() {
return tracingEventCodes;
}
public void setTracingEventCodes(ArrayList<Event> tracingEventCodes) {
this.tracingEventCodes = tracingEventCodes;
}
public void setAdditionalRules(AdditionalRules additionalRules) {
this.additionalRules = additionalRules;
}
}
but actually nothing changed. Still the same error. What's the problem in here?
I heard of another thing which are converted properties in
#DynamoDBBean attribute but it is quite hard to understand for me if I want to map List<Map<String, ?>>
Also I have seen that #DynamoDBConvertedType is usually used with #DynamoDBDocument but couldn't find any proper for me documentation. I have template.yaml file where I define table name which can be 'TABLE_NAME'. Then do I define #DynamoDBTable attribute on the main object with TableName = 'TABLE_NAME' ?
Should I mark every child class as #DynamoDBDocument? Will it work or it doesn't matter if I use dynamodbbean or dynamodbdocument if I still parse my JSON file with a gson parser?
Well, just needed to change ArrayList to List :- )

GSON: encode map of objects and preserve type when decoding

I have a class like:
public class MyClass {
private final Map<Property, Object> properties;
}
where Property is an enum.
Let's say that properties contains 2 elements, one whose value is a Double and one whose value is a class instance having only one attribute called ownerName. When I serialise this class I get the following string:
{"properties":{"NAME":{"ownerName":"MyBucket"},"DIVISOR":33.0}}
The problem is that when I tried to obtain a MyClass instance from the string above, the value for NAME property will be a Map instead of an instance of the class having ownerName attribute. I tried to write a custom serializer/deserializer but I was not able to do that only for NAME property. Any ideas?
You need to write custom deserialiser for the whole Map. Custom deserialiser could look like below:
class PropertyJsonDeserializer implements JsonDeserializer<Map<Property, Object>>, JsonSerializer<Map<Property, Object>> {
#Override
public Map<Property, Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (!json.isJsonObject()) {
return Collections.emptyMap();
}
JsonObject root = json.getAsJsonObject();
Map<Property, Object> result = new LinkedHashMap<>();
root.entrySet().forEach(entry -> {
Property property = Property.valueOf(entry.getKey());
switch (property) {
case DIVISOR:
result.put(property, entry.getValue().getAsDouble());
break;
case NAME:
Object owner = context.deserialize(entry.getValue(), Owner.class);
result.put(property, owner);
}
});
return result;
}
#Override
public JsonElement serialize(Map<Property, Object> src, Type typeOfSrc, JsonSerializationContext context) {
return context.serialize(src, Map.class);
}
}
Example usage:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.annotations.JsonAdapter;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.EnumMap;
import java.util.LinkedHashMap;
import java.util.Map;
public class GsonApp {
public static void main(String[] args) throws Exception {
Map<Property, Object> properties = new EnumMap<>(Property.class);
properties.put(Property.DIVISOR, new BigDecimal("33.0"));
properties.put(Property.NAME, new Owner());
MyClass myClass = new MyClass(properties);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(myClass);
System.out.println(json);
myClass = gson.fromJson(json, MyClass.class);
System.out.println(myClass);
}
}
class MyClass {
#JsonAdapter(PropertyJsonDeserializer.class)
private final Map<Property, Object> properties;
public MyClass(Map<Property, Object> properties) {
this.properties = properties;
}
// getters, setters, toString
}
class Owner {
private String ownerName = "MyBucket";
// getters, setters, toString
}
enum Property {
NAME, DIVISOR
}
Above code prints:
{
"properties": {
"NAME": {
"ownerName": "MyBucket"
},
"DIVISOR": 33.0
}
}
MyClass{properties={NAME=Owner{ownerName='MyBucket'}, DIVISOR=33.0}}

is there a way to use gson well to get an list with arrays that have 4 variables in java

I'm making an 3D engine using lwjgl.
I have tried to make a class to using a list of HashMap but the HashMap only accepts 2 variables so that does not work.
Part of my code for getting the JSON file
Gson().fromJson(string.toString(), BlockIndexFile.class);
the BlockIndexFile class
public class BlockIndexFile {
List<HashMap<String, String>> blocks = new ArrayList<HashMap<String, String>>();
public void setBlocks(List<HashMap<String, String>> blocks) {
this.blocks = blocks;
}
public List<HashMap<String, String>> getBlocks(){
return this.blocks;
}
}
and the json file
{
"blocks":
[
{
"name": "Foo",
"id": "foo",
"model": "cube1",
"texture": "foo"
}
]
}
I expected to be able to use a HashMap to get the id and then use that to get the other variables like the texture and model.
HashMap can contain more than 2 variables. See below example how you could use it:
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class GsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
Gson gson = new GsonBuilder().create();
BlockIndexFile blockIndexFile;
try (FileReader fileReader = new FileReader(jsonFile)) {
blockIndexFile = gson.fromJson(fileReader, BlockIndexFile.class);
}
HashMap<String, String> node0 = blockIndexFile.getBlocks().get(0);
System.out.println("id => " + node0.get("id"));
System.out.println("model => " + node0.get("id"));
System.out.println("texture => " + node0.get("id"));
}
}
Above code prints:
id =>foo
model =>foo
texture =>foo
Instead Map you can create POJO and code should be much easier and concise:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
public class GsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
Gson gson = new GsonBuilder().create();
BlockIndexFile blockIndexFile;
try (FileReader fileReader = new FileReader(jsonFile)) {
blockIndexFile = gson.fromJson(fileReader, BlockIndexFile.class);
}
Block node0 = blockIndexFile.getBlocks().get(0);
System.out.println(node0);
}
}
class BlockIndexFile {
private List<Block> blocks = new ArrayList<>();
// getters, setters
}
class Block {
private String id;
private String name;
private String model;
private String texture;
// getters, setters, toString
}
Above code prints:
Block{id='foo', name='Foo', model='cube1', texture='foo'}

spring messaging converting json string to map

Using Spring Integration and I have a json string (see below) and the following code:
public SomethingBean convert(Message<?> inMessage) {...}
Json string
{
"addressIdentification": {
"identifierType": "nemtom",
"addressIdentifier": "eztse"
},
"postcode": "BH1EH",
"country": "5"
}
I'd like to use the following method signature:
public SomethingBean convert(Message<Map<String, ?>> inMessage) {...}
Is it possible to convert the json string to Map automatically?
Thanks,
V.
Just use Spring Integration out of the box component:
<json-to-object-trnsfrormer type="java.util.Map"/>
before your SomethingBean invocation.
Use any JSON parsing library such as GSON or Jackson and convert it into Java Object.
GSON:
String jsonString = "{\"addressIdentification\":{\"identifierType\":\"nemtom\",\"addressIdentifier\":\"eztse\"},\"postcode\":\"BH1EH\",\"country\":\"5\"}";
Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> data = new Gson().fromJson(jsonString, type);
System.out.println(new GsonBuilder().setPrettyPrinting().create().toJson(data));
for(Map.Entry<String, Object> entry:data.entrySet()){
System.out.println(entry.getKey()+":"+entry.getValue());
}
Jackson:
String jsonString = "{\"addressIdentification\":{\"identifierType\":\"nemtom\",\"addressIdentifier\":\"eztse\"},\"postcode\":\"BH1EH\",\"country\":\"5\"}";
JSONObject jsonObject = new JSONObject(jsonString);
System.out.println(jsonObject);
JSONObject addressIdentification = jsonObject.getJSONObject("addressIdentification");
System.out.println("identifierType:" + addressIdentification.get("identifierType"));
System.out.println("addressIdentifier:" + addressIdentification.get("addressIdentifier"));
System.out.println("postcode:" + jsonObject.get("postcode"));
System.out.println("country:"+jsonObject.get("country"));
output:
identifierType:nemtom
addressIdentifier:eztse
postcode:BH1EH
country:5
Read more...
You can do with below code
package com.mboot.generator.models;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Container for all request keys and values values maybe single value or list
*/
public class OptionParameter {
private Map<OptionKey, Object> parameter = new HashMap<>();
public OptionParameter add(OptionKey OptionKey, Object value) {
if (value != null && parameter.get(OptionKey) == null)
parameter.put(OptionKey, value);
return this;
}
public Object get(OptionKey OptionKey) {
return parameter.get(OptionKey);
}
public void iterator(ParameterIterator iterator) {
parameter.entrySet().stream().sorted((k, v) -> k.getKey().name().compareTo(v.getKey().name()))
.forEach((e) -> iterator.parameter(e.getKey(), e.getValue()));
}
public interface ParameterIterator {
void parameter(OptionKey key, Object value);
}
#Override
public String toString() {
return "OptionParameter{" + "parameter=" + parameter + '}';
}
#Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
OptionParameter parameter1 = (OptionParameter) o;
return Objects.equals(parameter, parameter1.parameter);
}
#Override
public int hashCode() {
return Objects.hash(parameter);
}
}
And your message converter should be like below
package com.mboot.generator.utils;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.mboot.generator.models.OptionKey;
import lombok.SneakyThrows;
public class StringToMapConverter implements Converter<String, Map<OptionKey, Object>> {
#Autowired
private ObjectMapper objectMapper;
TypeFactory factory = TypeFactory.defaultInstance();
#Override
#SneakyThrows
public Map<OptionKey, Object> convert(String source) {
// Map<OptionKey, Object> result = objectMapper.readValue(source,
// factory.constructMapType(Map.class, OptionKey.class, Object.class));
JavaType javaType = objectMapper.getTypeFactory().constructParametrizedType(Map.class, OptionKey.class, Object.class);
return objectMapper.readValue(source,javaType);
}
}

Convert List<Object> to List<String> without for?

How can I convert a List of Objects to corresponding List of Strings without scanning all elements by for loop?
You could try this:
List<String> variable = (List<String>)(List<?>) yourList;
In the comments you specified that you wanted to call the toString() method. This is possible with guava (https://code.google.com/p/guava-libraries/):
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.List;
public class Application {
public static void main(String[] args) {
Function<Object, String> objectToString = new Function<Object, String>() {
public String apply(Object object) {
return object.toString();
}
};
List<Object> yourList = new ArrayList<Object>();
yourList.add("foo");
List<String> strings = Lists.transform(yourList, objectToString);
}
}

Categories

Resources