I'm trying to read this json file:
{
"username": "someusername",
"password": "12345678",
"ipAddresses": {
"2015-09-12": "127.0.0.1"
}
}
Using this class to store the info:
private final class SavedPlayer {
private final String username;
private final String password;
private final HashMap<LocalDate, String> ipAddresses;
private SavedPlayer(
String username, String password,
HashMap<LocalDate, String> ipAddresses
) {
this.username = username;
this.password = password;
this.ipAddresses = ipAddresses;
}
}
And this part of the code throws an exception:
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
SavedPlayer savedPlayer = GSON.fromJson(reader, SavedPlayer.class);
This is the thrown exception:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 5 column 17
How can I read this stored HashMap properly?
Edit: it works fine when i use <String, String> instead of <LocalDate, String>
Gson allows you to register your own custom serializers and deserializers. This is done by defining two parts:
Json Serialiers: Need to define custom serialization for an object
Json Deserializers: Needed to define custom deserialization for a type
Instance Creators: Not needed if no-args constructor is available or a deserializer is registered
GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(LocalDate.class, new MyDeserializer());
registerTypeAdapter call checks if the type adapter implements more than one of these interfaces and register it for all of them.
for more information check gson-user-guide
Here is an example of how to write a custom deserialize for LocalDate
public class LocalDateJsonDeserializer implements JsonDeserializer<LocalDate> {
public LocalDate deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return new LocalDate(json.getAsJsonPrimitive().getAsString());
}
}
and use this line for creating GSON.
final static Gson GSON = new GsonBuilder().registerTypeAdapter(LocalDate.class, new LocalDateJsonDeserializer()).setPrettyPrinting().create();
To bind JSON to LocalDate, you either have to write your custom serializer/deserialzer by implementing JsonDeserializer and registering with GSON using method registerTypeAdapter(), or you can use existing library for that: https://github.com/gkopff/gson-javatime-serialisers
Edit: You can find GSON deserializing key-value to custom object.
I tried the code you pasted with Gson 2.4 library. Also I converted the LocalDate to String. It is working absolutely fine.
public class SavedPlayer {
private final String username;
private final String password;
private final HashMap<String, String> ipAddresses;
private SavedPlayer(
String username, String password,
HashMap<String, String> ipAddresses
) {
this.username = username;
this.password = password;
this.ipAddresses = ipAddresses;
}
public static void main(String[] args) throws FileNotFoundException {
final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
SavedPlayer savedPlayer = GSON.fromJson("{\n" +
" \"username\": \"someusername\",\n" +
" \"password\": \"12345678\",\n" +
" \"ipAddresses\": {\n" +
" \"2015-09-12\": \"127.0.0.1\"\n" +
" }\n" +
"}", SavedPlayer.class);
System.out.println(savedPlayer.username);
System.out.println(savedPlayer.password);
for (Map.Entry entry : savedPlayer.ipAddresses.entrySet()) {
System.out.println("Key: " + entry.getKey() + "\tValue: " + entry.getValue());
}
}
}
Output:
someusername
12345678
Key: 2015-09-12 Value: 127.0.0.1
Related
I understand that I can create a map of a specified object with Jackson by using TypeReference. For instance, taking a class ...
public class Error {
/** Error code */
private final String errcode;
/** Error message */
private final String error;
// Getter
}
... and the data in JSON
{
"firstError":{
"errcode":"1234",
"error":"The 1. message"
},
"secondError":{
"errcode":"5678",
"error":"The 2. message"
}
}
... I can deserialize with
TypeReference<HashMap<String, Error>> typeRef = new TypeReference<HashMap<String, Error>>() {};
Map<String, Error> map = new ObjectMapper().readValue(jsonInput, typeRef);
My question is now: What can I do if my JSON looks like this?
{
"date":"2022-01-01",
"server":"myfancyserver",
"errors":{
"firstError":{
"errcode":"1234",
"error":"The 1. message"
},
"secondError":{
"errcode":"5678",
"error":"The 2. message"
}
}
}
First, in order to deserialize the nested Error objects containing final fields, it is needed to mark the all-args constructor with #JCreator annotation and use #JsonProperty to set the values properly:
public class Error {
private final String errcode;
private final String error;
#JsonCreator
public Error(#JsonProperty("errcode") String errcode, #JsonProperty("error") String error) {
this.errcode = errcode;
this.error = error;
}
// ... getters/toString, etc.
}
Then ObjectMapper::convertValue should be used to read the contents of the map from the JsonNode:
String json = ...; // input JSON string
ObjectMapper om = new ObjectMapper();
JsonNode node = om.readTree(json); // throws JsonProcessingException
Map<String, Error> errors = om.convertValue(
node.get("errors"), new TypeReference<Map<String, Error>>(){}
);
errors.forEach((k, v) -> System.out.println(k + " = " + v));
// -> firstError = {errcode='1234', error='The 1. message'}
// -> secondError = {errcode='5678', error='The 2. message'}
I have a JSON Data which is a collection of objects like the one given below. I have to deserialize the below given data into a list of objects
[{\"Name\": \"Initialize\", \"Library\": \"BKS\", \"Type\": \"setup\", \"Status\": \"PASS\", \"StartTime\": \"20190429 15:06:36.020\", \"EndTime\": \"20190429 15:06:39.476\", \"Environment\": \"CLC-ER\", \"ScenarioName\": \"BKS-DISVAS\", \"ElapsedTime\": 456.0},{\"Name\": \"Initialize\", \"Library\": \"BKS\", \"Type\": \"setup\", \"Status\": \"PASS\", \"StartTime\": \"20190429 15:06:36.020\", \"EndTime\": \"20190429 15:06:39.476\", \"Environment\": \"CLC-ER\", \"ScenarioName\": \"BKS-DISVAS\", \"ElapsedTime\": 456.0}]
I have tried the following code snippet, but I am getting the following error.
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING
at com.google.gson.Gson.fromJson(Gson.java:822) ~[gson-2.3.1.jar:na]
at com.google.gson.Gson.fromJson(Gson.java:875) ~[gson-2.3.1.jar:na]
My Model class is given below
public class KeywordDetails {
public KeywordDetails() {
}
#JsonProperty("Name")
public String Name;
#JsonProperty("Type")
public String Type;
#JsonProperty("Library")
public String Library;
#JsonProperty("StartTime")
public String StartTime;
#JsonProperty("EndTime")
public String EndTime;
#JsonProperty("Status")
public String Status;
#JsonProperty("ScenarioName")
public String ScenarioName;
#JsonProperty("Environment")
public String Environment;
#JsonProperty("ElapsedTime")
public double ElapsedTime;
}
The deserialization code is given below,
Gson serializer = new Gson();
Type listType = new TypeToken<ArrayList<KeywordDetails>>() {
}.getType();
JsonParser parser = new JsonParser();
JsonElement kwJson = parser.parse(ser.getKeywordStats());
System.out.println(kwJson);
List<KeywordDetails> keywordDetails = serializer.fromJson(kwJson, listType);
The ser.getKeywordStats() method is giving the above JSON.
I should be able to deserialize the JSON into the list or array of objects.
As I am new to Java, I am not able to find the root cause or fix for this issue, can anyone help me with this issue?
Why JsonProperty instead of SerializableName? Are you mixing Jackson and Gson?
What is the output of ser.getKeywordStats()?
Because I have tested your code by hardcoding that json-string instead of ser.getKeywordStats(), and it worked without any issue.
Here is a working snippet,
private static final String ESCAPED_STRING =
"[{\"Name\": \"Initialize\", \"Library\": \"BKS\", \"Type\": \"setup\", \"Status\": \"PASS\", \"StartTime\": \"20190429 15:06:36.020\", \"EndTime\": \"20190429 15:06:39.476\", \"Environment\": \"CLC-ER\", \"ScenarioName\": \"BKS-DISVAS\", \"ElapsedTime\": 456.0},{\"Name\": \"Initialize\", \"Library\": \"BKS\", \"Type\": \"setup\", \"Status\": \"PASS\", \"StartTime\": \"20190429 15:06:36.020\", \"EndTime\": \"20190429 15:06:39.476\", \"Environment\": \"CLC-ER\", \"ScenarioName\": \"BKS-DISVAS\", \"ElapsedTime\": 456.0}]";
public static void main(String[] args) throws JsonProcessingException {
Gson gson = new Gson();
Type t= new TypeToken<List<KeywordDetails>>(){}.getType();
JsonParser parser = new JsonParser();
JsonElement jsonElements = parser.parse(ESCAPED_STRING);
System.out.println(jsonElements);
List<KeywordDetails> kd = gson.fromJson(jsonElements, t);
System.out.println(kd);
}
This has to do something with your String, the string must be void of the \ slashes to make it a valid JSON. So, I have used JsonParser to construct a valid JSON from the escaped String.
Use Gson or JsonParser not both.
Try with this code:
Gson serializer = new Gson();
Type listType = new TypeToken<ArrayList<KeywordDetails>>() {}.getType();
List<KeywordDetails> keywordDetails = serializer.fromJson(ser.getKeywordStats(), listType);
I don't know #JsonProperty annotation. You can use (or not) #SerializedName instead.
Then you can simply deserialize your json like this.
String json = ...
Gson gson = new Gson();
List< KeywordDetails > keywords = gson.fromJson(json, new TypeToken<List< KeywordDetails >>(){}.getType());
I have a Json string which has a string message field.
String:
{ "Type" : "Text",
"Subject" : "data received",
"Message" :"{\\"language\\":\\"US\\",\\"data\\":\\"signature\\"}"
}
I want to convert it into the following structure:
Notification.java
public class Notification {
String type;
String subject;
Message message;
}
Message.java
public class Message {
String language;
String data;
}
Is there a way in which I can directly convert the string to a Java object of the above structure? I want to avoid deserializing twice.
You can create a custom Deserializer to deserialize the Message text into Message object and annotate the Message class with #JsonDeserialize:
#JsonDeserialize(using = MessageDeserializer.class)
public class Message {
String language;
String data;
}
public class MessageDeserializer extends JsonDeserializer<Message> {
public MessageDeserializer() {
super();
}
#Override
public Message deserialize(
final JsonParser jsonParser, final DeserializationContext deserializationContext) throws
IOException, JsonProcessingException {
final String messageText = jsonParser.getText();
// parse messageText into Message object
}
}
I am not sure my solution is acceptable since it does require additional explicit call to ObjectMapper to perform deserialization of the string value of Message.
However, this is it is done during the buildup of Notification object and does not require a String message property.
You need to add a ctor with String argument to Message class, where you can deserialize the String into Map and extract the instance propertieds:
public Message(String str) {
try {
#SuppressWarnings("unchecked")
Map<String, Object> map =
(Map<String, Object>)new ObjectMapper().readValue(str, Map.class);
language = map.containsKey("language") ? map.get("language").toString() : null ;
data = map.containsKey("data") ? map.get("data").toString() : null ;
} catch (Exception e) {
e.printStackTrace();
}
}
the new ctor will be called by Jackson when you deserialize a Notification object:
Notification n = (Notification)new ObjectMapper().readValue(reader, Notification.class);
You can convert json string into key-value pairs in Map.You will have to do twice as the Message value is again a json string.Use org.json for JSONObject
Map<String, String> map = new HashMap<String, String>();
JSONObject j = new JSONObject(str);
Iterator<String> keys = j.keys();
while( keys.hasNext() ){
String key = (String)keys.next();
String val = j.getString(key);
map.put(key, val);}
Then retrieve the values by iterating over the keys and pass the values into the class constructor
Then map.get(key) can be used to retrieve the values and will be passed into constructors of the classes.
The org.json library is easy to use:
//Create Json object to parse string
// str is input string
JSONObject obj = new JSONObject(str);
//Create Message
Message mess = new Message();
JSONObject obj2 = new JSONObject(obj.getString("Message"));
mess.data = obj2.getString("data");
mess.language = obj2.getString("language");
//Create Notification
Notification noti = new Notification();
noti.message = mess;
noti.subject = obj.getString("Subject");
noti.type = obj.getString("Type");
I recive object like that
{
"data": "some data",
"social": {
"twitter": "id"
}
}
This is easly parsed using next classes
public class SocialLinks {
#Expose
private String data;
#Expose
private Social social;
}
public class Social {
#Expose
private String twitter;
}
Unfortunatly due to some issues, if social is empty it is returened as array
{
"data": "some data",
"social": [
]
}
How can I parse it with gson? (I am not a developer of server side and cannot affect responce meassages)
You can do that using these classes.
SocialLinks.java
public class SocialLinks {
private String data;
private Social social;
// Getters && Setters
}
Social.java:
public class Social {
private String twitter;
// Getters & Setters
}
And here is your main method
public class GsonApp {
private static final String TEST_JSON = "{\n" +
" \"data\": \"some data\",\n" +
" \"social\": {\n" +
" \"twitter\": \"id\"\n" +
" }\n" +
"}";
public static void main(String[] args) throws Exception {
final Gson gson = new GsonBuilder().create();
// Read Example
final SocialLinks socialLinks = gson.fromJson(TEST_JSON, SocialLinks.class);
System.out.println(gson.toJson(socialLinks));
// Write with null Social
final SocialLinks socialLinks1 = new SocialLinks();
socialLinks1.setData("MyData");
System.out.println(gson.toJson(socialLinks1));
// Write with empty Social (social.twitter is null)
final SocialLinks socialLinks2 = new SocialLinks();
socialLinks2.setData("MyData");
socialLinks2.setSocial(new Social());
System.out.println(gson.toJson(socialLinks2));
// Write with full Social
final SocialLinks socialLinks3 = new SocialLinks();
socialLinks3.setData("MyData");
socialLinks3.setSocial(new Social());
socialLinks3.getSocial().setTwitter("ID");
System.out.println(gson.toJson(socialLinks3));
}
}
This will output
{"data":"some data","social":{"twitter":"id"}}
{"data":"MyData"}
{"data":"MyData","social":{}}
{"data":"MyData","social":{"twitter":"ID"}}
Update
If you data type changes depending on your application state you may want to create Map object instead of DTO. Here is an example
private static final String TEST_JSON_2 = "{\n" +
" \"data\": \"some data\",\n" +
" \"social\": [\n" +
" ]\n" +
"}";
...
Type type = new TypeToken<Map<String, Object>>(){}.getType();
final Map<String, Object> socialLinks4 = gson.fromJson(TEST_JSON_2, type);
System.out.println(socialLinks4);
final Map<String, Object> socialLinks5 = gson.fromJson(TEST_JSON, type);
System.out.println(socialLinks5);
This will output
{data=some data, social=[]}
{data=some data, social={twitter=id}}
In my Android project I'm trying to create a list of MyType using gson. For this I use the following code:
String result = "[{\"text\": \"lala\", \"created\": \"123456\"}, {\"text\": \"lele\", \"created\": \"123456\"}]";
class ReceivedMessage {
String text;
String created;
}
List<ReceivedMessage> receivedMessages = new Gson().fromJson(result, new TypeToken<List<ReceivedMessage>>(){}.getType());
for (ReceivedMessage mess : receivedMessages) {
Log.wtf("This is it", mess.created);
}
Unfortunately I get a nullpointerexception. Does anybody know what I'm doing wrong here?
Defining the class "outside" worked pretty well:
public class ReceivedMessage {
String text;
String created;
}
public static void main(String[] args) {
String result = "[{\"text\": \"lala\", \"created\": \"123456\"}, "
+"{\"text\": \"lele\", \"created\": \"123456\"}]";
List<ReceivedMessage> receivedMessages = new Gson().fromJson(result,
new TypeToken<List<ReceivedMessage>>() {}.getType());
for (ReceivedMessage mess : receivedMessages) {
System.out.println("This is it " + mess.created);
}
....
}
Note: Gson uses reflection and so it needs access to the class.
Please try this :
Type listType = new TypeToken<ArrayList<ReceivedMessage>>() {}.getType();
List<ReceivedMessage> receivedMessages= new Gson().fromJson(result, listType);
Google Gson - deserialize list<class> object? (generic type)