I want to serialize nulls for a specific field or class.
I've also researched stackoverflow but couldn't see a result.
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.serializeNulls().create();
I used it but it did not work.
My Json result is two type. Pojo crashes when it's like the first case.
"character": {
"id": "",
"name": ""
},
"character": {
"id": 10,
"name": "İrem"
}
My crash's log:
com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: empty String
at com.google.gson.internal.bind.TypeAdapters$7.read(TypeAdapters.java:228)
at com.google.gson.internal.bind.TypeAdapters$7.read(TypeAdapters.java:218)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
My Pojo
#SerializedName("name")
private String name;
#SerializedName(value = "id")
private Integer id; // or private int id; Both are failing.
1) First option, not receive a String. If the number is not available, you should be receiving null instead. So it would look like this:
"character": {"id": null,"name": null}
This would solve the issue.
2) Second option - change your data model to not look for Integer, instead just an Object. But this means more validation work for you.
#SerializedName("name")
private String name;
#SerializedName(value = "id")
private Object id;
So once you parse it like this:
MyPojo pojo = gson.fromJson(jsonString, MyPojo.class);
You will have to do this to use the id field:
if(pojo.getId() instanceof Integer) {
// you know you have a number
} else if (pojo.getId() instanceof String) {
// you know it's not a valid number
}
That's it!
3) third option: have two data model.
First one for Integer:
#SerializedName("name")
private String name;
#SerializedName(value = "id")
private Integer id;
Second one for String:
#SerializedName("name")
private String name;
#SerializedName(value = "id")
private String id;
So when you parse it, you parse it to a JsonObject:
JsonObject jsonObject = gson.fromJson(jsonString, JsonObject.class);
Then check which one you need to map to:
try {
Integer id = jsonObject.get("id").getAsInt();
// it's a number!!
MyIntegerPojo integerPojo = new Gson().fromJson(jsonObject, MyIntegerPojo.class);
} catch (ClassCastException e) {
// it's not a number
MyStringPojo stringPojo = new Gson().fromJson(jsonObject, MyStringPojo.class);
}
I really recommended the first option - it is cleaner and it would be the right way to do it.
Your problem is the id is String not an Integer in
"character": {
"id": "",
"name": ""
}
You should fix it from the backend.
Possible solutions are 2:
BE has to be consistent on this field and always provide a String or a Number (accepted by Json - there's not such a Double or Integer in Json).
Follow this small tutorial which tells you how to create a custom parser for your classes.
What you want to do is check for the instance of id when parsing it, and always use an Integer (or a null value) if an empty/invalid String is provided.
If you want to prevent Null Pointer Exceptions during reading json values, you can also do the following method:
1.) convert to com.google.gson.JsonObject
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
...
JsonObject convertedJsonObject = JsonParser.parseString( jsonString);
2.) Check if element exist and is not JsonNull
if( convertedJsonObject.has("key") && !convertedJsonObject.get("key").isJsonNull()){
... do your variable assignment
}
Related
I am having a string like ,
name = " {
"Name" : "MyName"
}"
and having a Model class like ,
#Valid
Class Model {
#JsonProperty("Name")
#Size(min = 1)
#NotNull
private String name;
}
Now I am converting the string to Java Object by following code,
Model name = objectMapper.readValue(name, Model.class);
So the validation(min = 1 and not null) is not happening with this.
How can I validate when I am converting a string to java object?
i will try to help u.
I have an idea and it´s that u divide the process in 2 parts:
First, u take the json and put in on a JSONObject:
import org.json.JSONObject;
...
JSONObject json= new JSONObject(name);
Then, u can call, for example, a function passing a json that validate the values of json an if is correct return a model object:
public Model functionExample(JSONObject json){
try{
if(json.has("Name") && json.getString("Name")!=null){
return new Model(json.getString("Name"));
}
}catch(Exception ex){
return new Model();
}
}
This check if the field "Name" exists and it´s not null.
I don´t know if it is what u want.
I hope it help u.
I have a JSON which looks like this (number of fields heavily reduced for the sake of example):
{
"content": {
"id": {"content": "1"},
"param1": {"content": "A"},
"param2": {"content": "55"}
}
}
Keep in mind, that I don't have control over it, I can't change it, that is what I get from API.
I've created a POJO class for this looking like that:
public class PojoClass {
private String id;
private String param1;
private String param2;
// getters and setters
}
Then I parse JSON with Jackson (I have to use it, please don't suggest GSON or else):
ObjectMapper om = new ObjectMapper();
JsonNode jsonNode = om.readTree(json).get("content");
PojoClass table = om.readValue(jsonNode.toString(), PojoClass.class);
And this doesn't work, because of id, param1 and param2 having JSON in them, not straight values. The code works fine with JSON like this:
{
"content": {
"id": "1",
"param1": "A",
"param2": "55"
}
}
But unfortunately the values I need are stored under "content" fields.
What is the cleanest way to resolve this?
I understand that I can hardcode this and extract all values into variables one by one in constructor or something, but there are a lot of them, not just 3 like in this example and obviously this is not the correct way to do it.
You can modify the JsonNode elements like "id": {"content": "1"} to {"id": "1"} inside your json string accessing them as ObjectNode elements with an iterator and after deserialize the new json obtained {"id":"1","param1":"A","param2":"55"} like below:
String content = "content";
ObjectMapper om = new ObjectMapper();
JsonNode root = om.readTree(json).get(content);
Iterator<String> it = root.fieldNames();
while (it.hasNext()) {
String fieldName = it.next();
((ObjectNode)root).set(fieldName, root.get(fieldName).get(content));
}
PojoClass table = om.readValue(root.toString(), PojoClass.class);
System.out.println(table); //it will print PojoClass{id=1, param1=A, param2=55}
I have an Object like this:
public class Marketplace {
private String name;
private int id;
private List<String> supportedLanguages;
}
and I have an input Json format String like this:
{
"name":"US",
"id":1,
"supportedLanguages":{"en_US", "es_US"}
}
I tried something like this first but failed:
objectMapper.readValue(marketplaceInJsonString, Marketplace.class);
Then I tried something like this but still failed:
JsonNode jsonNode = objectMapper.readValue(marketplaceInJsonString, JsonNode.class);
Marketplace marketplace = new Marketplace(jsonNode.get("name").asText()), jsonNode.get("id").asInt(), jsonNode.findValuesAsText("supportedLanguages"));
I think the key problem here is that I don't find the correct way to map supportedLanguages as a List of String.
And is there any format problem of the Json String input?
Please help, and really appreciate.
Your json string is not valid json
change it to
{
"name": "US",
"id": 1,
"supportedLanguages": [
"en_US",
"es_US"
]
}
Testing code:
String marketplaceInJsonString = "{\"name\":\"US\",\"id\":1,\"supportedLanguages\":[\"en_US\",\"es_US\"]}";
ObjectMapper objectMapper = new ObjectMapper();
Marketplace marketplace = objectMapper.readValue(marketplaceInJsonString, Marketplace.class);
System.out.println(marketplace);
//output
Marketplace(name=US, id=1, supportedLanguages=[en_US, es_US])
Im getting an error trying to take a json array to a list of objects, the exception is as below.
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 170
here is the code i am trying to use
public static void main(String[] args) throws FileNotFoundException {
Gson gson = new Gson();
Test test = new Test();
JsonElement json = gson.fromJson(test.getFile("fieldTypes.json"), JsonElement.class);
String result = gson.toJson(json);
System.out.println(result);
Type listType = new TypeToken<ArrayList<JiraField>>(){}.getType();
JiraField[] jiraFields = gson.fromJson(result, listType);
for (JiraField jiraField : jiraFields) {
System.out.println(jiraField);
}
}
This is the file contents
[
{
"id": "issuetype",
"key": "issuetype",
"name": "Issue Type",
"custom": false,
"orderable": true,
"navigable": true,
"searchable": true,
"clauseNames": [
"issuetype",
"type"
],
"schema": {
"type": "issuetype",
"system": "issuetype"
}
},
{
"id": "timespent",
"key": "timespent",
"name": "Time Spent",
"custom": false,
"orderable": false,
"navigable": true,
"searchable": false,
"clauseNames": [
"timespent"
],
"schema": {
"type": "number",
"system": "timespent"
}
}
]
The file is being read from the resources folder but that is working fine and the sysout is correctly showing the json contents. i assume there is something im doing wrong ?
ok, so. turns out to be caused by un mapped fields, this is how i got it working
in the object you add the #Expose annotation for the fields you want
public class JiraField {
#Expose
private String id ;
#Expose private String key ;
#Expose private String name ;
#Expose private boolean custom ;
#Expose private boolean orderable ;
#Expose private boolean navigable ;
#Expose private String[] clauseNames ;
Then when you initialise gson, you do it like this
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
then it works :)
JiraFields{id='issuetype', key='issuetype', name='Issue Type', custom=false, orderable=true, navigable=true, clauseNames=[issuetype, type]}
JiraFields{id='timespent', key='timespent', name='Time Spent', custom=false, orderable=false, navigable=true, clauseNames=[timespent]}
Try this:
Type listType = new TypeToken<ArrayList<JiraField[]>>(){}.getType();
I think we need to specify the JiraField[] in-order to get Array.
I'm assuming you have a minified JSON according to the exception message ...at line 1 column 170.... It would be nice if you provide both exact your exact JSON document and your mapping as well, since the posted formatted JSON now has the layout destroyed. I'm also assuming your mapping is custom and basically something like:
final class JiraField {
#SerializedName("id")
final String id = null;
#SerializedName("key")
final String key = null;
#SerializedName("name")
final String name = null;
#SerializedName("custom")
final boolean isCustom = Boolean.valueOf(false);
#SerializedName("orderable")
final boolean isOrderable = Boolean.valueOf(false);
#SerializedName("navigable")
final boolean isNavigable = Boolean.valueOf(false);
#SerializedName("searchable")
final boolean isSearchable = Boolean.valueOf(false);
#SerializedName("clauseNames")
final List<String> clauseNames = null;
#SerializedName("schema")
final List<String> schema = null;
}
Note the JiraField.schema field type: it's a list. Trying to deserialize a list of JiraFields with the default Gson configuration would result into:
java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 170 path $[0].schema
The symptom and the column are exactly the same (however, again, I'm just assyming your JSON file is minified), but note the path $[0].schema you probably missed to post: it says that an array begin token [ is expected (since the field is a List<String>), but was an object begin token {. Changing the field type to a more appropriate arbitrary Map<String, String> or any other custom appropriate POJO would fix it. Partially.
#SerializedName("schema")
final Map<String, String> schema = null;
Why partially? Your type token is bound to an ArrayList, but you're casting the deserialization result to an array JiraField[], therefore you'd get something like this:
Exception in thread "main" java.lang.ClassCastException: java.util.ArrayList cannot be cast to [LJiraField;
To fix it, you have to declare your jiraFields variable matching the deserialized value type: it's an array list, but not an array -- these are not the same in Java.
List<JiraField> jiraFields = ...
Another note regarding deserialization is that you don't need an intermediate json object to deserialize from. test.getFile most likely returns a java.io.Reader, so you can pass it directly to deserialize. Summarizing all up, the following should work for you:
// Immutable and thread-safe, can be instantiated once and shared
private static final Gson gson = new Gson();
// Immutable and thread-safe value type, the same story, + List instead of ArrayList - interfaces are usually much better
private static final Type listType = new TypeToken<List<JiraField>>() {
}.getType();
public static void main(final String... args)
throws IOException {
try ( final Reader reader = test.getFile("fieldTypes.json") ) {
final List<JiraField> jiraFields = gson.fromJson(reader, listType);
for ( final JiraField jiraField : jiraFields ) {
System.out.println(jiraField.key + " => " + jiraField.name);
}
}
}
Output:
issuetype => Issue Type
timespent => Time Spent
Type listType = new TypeToken<ArrayList<JiraField>>(){}.getType();
ArrayList<JiraField> jiraFields = gson.fromJson(result, listType);
for (JiraField jiraField : jiraFields) {
System.out.println(jiraField);
}
Try This code
Comment this line
Type listType = new TypeToken<ArrayList<JiraField>>(){}.getType();
Try with this
JiraField[] jiraFields = gson.fromJson(result, JiraField[].class);
I have a string output from server and I am trying to extract some values form the string.
Here is the output from server:
jsonString =
{
"MEANING":"reduce",
"DISPLAY":"",
"TYPE_CD":1,
"SELECTED_IND":1,
"CNT":1,
"SOURCES":[
{ "a":1 }
]
}
Code:
JsonReader reader = new JsonReader(new StringReader(jsonString));
DataObject obj1 = new Gson().fromJson(reader, DataObject.class);
DataObject Class:
DataObject
{
private int MEANING;
private int CNT;
private String TYPE_CD;
private String DISPLAY;
private String MEANING;
private List<Long> SOURCES;
public String getSourceTypeMeaning()
{
return this.MEANING;
}
public String getSourceTypeDisplay()
{
return this.DISPLAY;
}
public String getSourceTypeCd()
{
return this.TYPE_CD;
}
public int getSourceCount()
{
return this.CNT;
}
public List<Long> getSourceList()
{
return this.SOURCES;
}
}
but getting this error
Expected a string but was BEGIN_OBJECT at line 1 column 132
I am not able to find the issue with my code.
Other answers are pointing out that the problem is in the SOURCES field, and that's true, but the solutions they're giving are not correct...
You can't use just a Map to parse the SOURCES field, because this field is indeed an array! You have:
"SOURCES": [ ... ]
Since you have square brackets [ ], you have an array! And it's true there's a Map, but it is contained in the array...
So, what you need to parse that field correctly is:
private List<Map<String, int>> SOURCES;
Note that we use a Map to allow the content of SOURCES to have multiple and unknown values, so that this code could parse not only your JSON, but also something like:
"SOURCES":[
{ "a":1, "b":2 },
{ "c":3 },
{ "x":99, "y":98, "z":97 }
]
SOURCES variable should be Map<String,Long>,because in JSON string SOURCES is key-value collection ("a":1) where "a" is string and 1 is number.
Hope this helps.
Check this
"SOURCES":[
{ "a":1 }
]
This will represent a List of map not List of long.
So change your List<long> to List<Map<String, Long>> or List<Map<Object, Long>>.