I'm reading a Document from MongoDB which contains its unique identifier:
{
"_id" : ObjectId("5526888bd3d56a86cea8ea12"),
"name" : "user1"
}
I'd like to map that with a java class
public class Mapper {
Object _id;
String name;
}
As a result of my fromJson execution:
Mapper m = gson.fromJson(string, Mapper.class);
...the value stored in the _id field is {$oid=5526888bd3d56a86cea8ea12}. I'd like to store the id String in it. (e.g. "5526888bd3d56a86cea8ea12")
Can Gson do it for me automatically ?
Thanks
You can register a custom adapter to tell the parser that you only want to grab the value between parenthesis and quotes in the string value (note that id is now a String in your Mapper class). The regex can be changed to match the ids requirements that are generated.
class MapperAdapter implements JsonDeserializer<Mapper> {
private static final Pattern p = Pattern.compile("\\(\"([a-zA-Z\\d]+)\"\\)");
#Override
public Mapper deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jObj = json.getAsJsonObject();
String id = jObj.get("_id").getAsString();
String name = jObj.get("name").getAsString();
Matcher m = p.matcher(id);
if(!m.find()) {
throw new IllegalArgumentException("The id should be within parenthesis and quotes.");
}
return new Mapper(m.group(1), name);
}
}
and you register it in your parser:
Gson gson = new GsonBuilder().registerTypeAdapter(Mapper.class, new MapperAdapter()).create();
Mapper m = gson.fromJson(jsonString, Mapper.class);
This yield the output:
Mapper{id=5526888bd3d56a86cea8ea12, name=user1}
Related
I have a JSON string that is parsed using the GSON library into a Map like so:
static Type type = new TypeToken<Map<String, String>>() {}.getType();
// note the trailing/leading white spaces
String data = "{'employee.name':'Bob ','employee.country':' Spain '}";
Map<String, String> parsedData = gson.fromJson(data, type);
The problem I have is, my JSON attribute values have trailing/leading whitespaces that needs to be trimmed. Ideally, I want this to be done when the data is parsed to the Map using GSON. Is something like this possible?
You need to implement custom com.google.gson.JsonDeserializer deserializer which trims String values:
class StringTrimJsonDeserializer implements JsonDeserializer<String> {
#Override
public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
final String value = json.getAsString();
return value == null ? null : value.trim();
}
}
And, you need to register it:
Gson gson = new GsonBuilder()
.registerTypeAdapter(String.class, new StringTrimJsonDeserializer())
.create();
I have an enum that looks something like this
public enum Example {
EXAMPLE_1,
EXAMPLE_2,
EXAMPLE_3,
}
I am trying to parse a json string like this:
String json = "{\"blah\": \"Example.EXAMPLE_1\"}"
I have tried defining a class like this:
public class Blah {
Example blah;
}
and using
gson.fromJson(json, Blah.class)
but it just sets the field to null. Is there anyway to do this? I unfortunately can't control the format of the json string so I have to parse it as is.
Default gson will parse enum field without class name. You can customise your own json deserializer for Example enum.
JsonDeserializer<?> jd = new JsonDeserializer<Example>() {
#Override
public Example deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String enumStr = json.getAsString();
String enumVal = enumStr.split("\\."); // etc...
Example val = ... ...
//...
return val;
}
};
Gson gson = new GsonBuilder().registerTypeAdapter(Example.class, jd).create();
Try this json
String json = "{\"blah\": \"EXAMPLE_1\"}"
I'm trying to deserialize the following structure
{ meta: { keywords: [a, b, c, d]} ... }
other valid structures are
{ meta: { keywords: "a,b,c,d"} ... }
and
{ meta: {keywords: "a"} ...}
I have this classes
public class Data {
#PropertyName("meta")
MetaData meta;
...
}
public class MetaData {
List<String> keywords;
...
}
and a custom deserializer
public static class CustomDeserilizer implements JsonDeserializer<MetaData>{
#Override
public MetaData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
List<String> keywords = null;
Gson gson = new Gson();
MetaData metaData = gson.fromJson(json, AppMetaData.class);
JsonObject jsonObject = json.getAsJsonObject();
if (jsonObject.has("keywords")) {
JsonElement elem = jsonObject.get("keywords");
if (elem != null && !elem.isJsonNull()) {
if (jsonObject.get("keywords").isJsonArray()) {
keywords = gson.fromJson(jsonObject.get("keywords"), new TypeToken<List<String>>() {
}.getType());
} else {
String keywordString = gson.fromJson(jsonObject.get("keywords"), String.class);
keywords = new ArrayList<String>();
list.addAll(Arrays.asList(keywordString.split(",")));
}
}
}
metaData.setKeywords(keywords);
}
Then I try to apply the deserilizer:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Data.class,new CustomDeserilizer())
.create();
But I get a parsing error , because is trying to deserialize Data instead of MetaData, how can I apply this deserializer to make it work right?
I solved it creating a deserializer for my class Data.
public static class DataDeserilizer implements JsonDeserializer {
#Override
public Data deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Gson gson = new Gson();
Data data = gson.fromJson(json, Data.class);
JsonObject jsonObject = json.getAsJsonObject();
if (jsonObject.has("meta")) {
JsonElement elem = jsonObject.get("meta");
if (elem != null && !elem.isJsonNull()) {
Gson gsonDeserializer = new GsonBuilder()
.registerTypeAdapter(MetaData.class, new CustomDeserilizer())
.create();
gsonDeserializer.fromJson(jsonObject.get("meta"), Data.class);
}
}
return data;
}
}
And
Gson gson = new GsonBuilder()
.registerTypeAdapter(Data.class,new DataDeserilizer())
.create();
Pretty obvious, but is there a more elegant solution?
Firstly, rename your class to meta instead of metadata and make keywords String instead of List.Then use the following to map your JSonString into your object.
Gson gson = new GsonBuilder().create();
Meta meta = gson.from(yourJsonString,Meta.class);
In order to get keywords only, you need this.
JsonObject jsonObject = new JsonObject(yourJSonString);
String data = jsonObject.getJsonObject("meta").getString("keywords");
keywords is a JsonObject not an JsonArray so you can't directly map it
onto List. You can split the string to get keywords in an array.
String keywords[] = data.split(",");
Here's a concise solution that leverages Java inheritance to represent the nested structure; and therefore does not need to provide any actual instance member fields (mappings, etc) for capturing the nested String data that GSON maps.
Step 1: For readability, create an empty object to represent the nested mapping
public class StateRegionCitiesMap extends HashMap<String, List<String>> {
}
Step 2: Add the one line of actual code to do the mapping; no other serialize/deserialize logic to manage
protected void loadContent(JsonObject stateRegionsJsonObject) {
HashMap<String, StateRegionCitiesMap> stateRegionCitiesMap =
mGson.fromJson(
stateRegionsJsonObject,
new TypeToken<HashMap<String, StateRegionCitiesMap>>() {
}.getType()
);
}
Alternatively, you can skip the wrapper class altogether and just directly put <String, List<String>> in the GSON call. However, I find an explicit object helps to inform/remind whoever is reading the code, what the purpose is.
Example JSON:
The class StateRegionCitiesMap represents a multi-tier map structure for say:
[US State] -> [State-Region Key] -> [Sub-Region Key] -> CitiesArray[]
"CA": {
"Central CA": {
"Central Valley": [
"FRESNO",
"VISALIA"
],
"Sacramento Area": [
"SACRAMENTO",
"EL DORADO HILLS"
]
},
This suppose to achieve what you want easily. You should define an inner static class. You can keep nesting classes to define keywords as class Keywords, etc. Just remember to have a field in the containing class, i.e.
in your inner class have private Keywords keywords;
In your Main class:
Gson gson = new Gson();
Data data = gson.fromJson(SOME_JSON_STRING, Data.class);
In a class called Data:
public class Data {
private Meta meta;
static class Meta{
private String[] keywords;
}
}
I'm trying to parse some JSON data using gson in Java that has the following structure but by looking at examples online, I cannot find anything that does the job.
Would anyone be able to assist?
{
"data":{
"id":[
{
"stuff":{
},
"values":[
[
123,
456
],
[
123,
456
],
[
123,
456
],
],
"otherStuff":"blah"
}
]
}
}
You just need to create a Java class structure that represents the data in your JSON. In order to do that, I suggest you to copy your JSON into this online JSON Viewer and you'll see the structure of your JSON much clearer...
Basically you need these classes (pseudo-code):
class Response
Data data
class Data
List<ID> id
class ID
Stuff stuff
List<List<Integer>> values
String otherStuff
Note that attribute names in your classes must match the names of your JSON fields! You may add more attributes and classes according to your actual JSON structure... Also note that you need getters and setters for all your attributes!
Finally, you just need to parse the JSON into your Java class structure with:
Gson gson = new Gson();
Response response = gson.fromJson(yourJsonString, Response.class);
And that's it! Now you can access all your data within the response object using the getters and setters...
For example, in order to access the first value 456, you'll need to do:
int value = response.getData().getId().get(0).getValues().get(0).get(1);
Depending on what you are trying to do. You could just setup a POJO heirarchy that matches your json as seen here (Preferred method). Or, you could provide a custom deserializer. I only dealt with the id data as I assumed it was the tricky implementation in question. Just step through the json using the gson types, and build up the data you are trying to represent. The Data and Id classes are just pojos composed of and reflecting the properties in the original json string.
public class MyDeserializer implements JsonDeserializer<Data>
{
#Override
public Data deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException
{
final Gson gson = new Gson();
final JsonObject obj = je.getAsJsonObject(); //our original full json string
final JsonElement dataElement = obj.get("data");
final JsonElement idElement = dataElement.getAsJsonObject().get("id");
final JsonArray idArray = idElement.getAsJsonArray();
final List<Id> parsedData = new ArrayList<>();
for (Object object : idArray)
{
final JsonObject jsonObject = (JsonObject) object;
//can pass this into constructor of Id or through a setter
final JsonObject stuff = jsonObject.get("stuff").getAsJsonObject();
final JsonArray valuesArray = jsonObject.getAsJsonArray("values");
final Id id = new Id();
for (Object value : valuesArray)
{
final JsonArray nestedArray = (JsonArray)value;
final Integer[] nest = gson.fromJson(nestedArray, Integer[].class);
id.addNestedValues(nest);
}
parsedData.add(id);
}
return new Data(parsedData);
}
}
Test:
#Test
public void testMethod1()
{
final String values = "[[123, 456], [987, 654]]";
final String id = "[ {stuff: { }, values: " + values + ", otherstuff: 'stuff2' }]";
final String jsonString = "{data: {id:" + id + "}}";
System.out.println(jsonString);
final Gson gson = new GsonBuilder().registerTypeAdapter(Data.class, new MyDeserializer()).create();
System.out.println(gson.fromJson(jsonString, Data.class));
}
Result:
Data{ids=[Id {nestedList=[[123, 456], [987, 654]]}]}
POJO:
public class Data
{
private List<Id> ids;
public Data(List<Id> ids)
{
this.ids = ids;
}
#Override
public String toString()
{
return "Data{" + "ids=" + ids + '}';
}
}
public class Id
{
private List<Integer[]> nestedList;
public Id()
{
nestedList = new ArrayList<>();
}
public void addNestedValues(final Integer[] values)
{
nestedList.add(values);
}
#Override
public String toString()
{
final List<String> formattedOutput = new ArrayList();
for (Integer[] integers : nestedList)
{
formattedOutput.add(Arrays.asList(integers).toString());
}
return "Id {" + "nestedList=" + formattedOutput + '}';
}
}
I have this structure of my JSON response string:
{
"1":{
"data1":"1","data2":"test1", ...
},
"2":{
"data1":"6","data2":"test2", ...
},
...
}
And I want to get the values to put into an ArrayList<MyItem>. I use GSON and normally I can do it in this way:
ArrayList<MyItem> items =
gson.fromJson(jsonString, new TypeToken<ArrayList<MyItem>>() {}.getType());
The problem is, that it does not work, because my JSON String has numbers as keys, but I only want to get the values to put into the ArrayList (unfortunately, the JSON string can not be changed by myself). How can I do this efficiently?
I'd probably deserialize the JSON into a java.util.Map, get the values from the Map as a Collection using the Map.values() method, and then create a new ArrayList using the constructor that takes a Collection.
Write a custom deserializer.
class MyItem
{
String data1;
String data2;
// ...
}
class MyJSONList extends ArrayList<MyItem> {}
class MyDeserializer implements JsonDeserializer<MyJSONList>
{
public MyJSONList deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException
{
MyJSONList list = new MyJSONList();
for (Entry<String, JsonElement> e : je.getAsJsonObject().entrySet())
{
list.add((MyItem)jdc.deserialize(e.getValue(), MyItem.class));
}
return list;
}
}
Example:
String json = "{\"1\":{\"data1\":\"1\",\"data2\":\"test1\"},\"2\":{\"data1\":\"6\",\"data2\":\"test2\"}}";
Gson g = new GsonBuilder()
.registerTypeAdapter(MyJSONList.class, new MyDeserializer())
.create();
MyJSONList l = g.fromJson(json, MyJSONList.class);
for (MyItem i : l)
{
System.out.println(i.data2);
}
Output:
test1test2