I need to convert json string to java object and display it as a long. The json string is a fixed array of long numbers:
{numbers
[ 268627104, 485677888, 506
We can use the below code solution for number Long:
Document doc = documentCursor.next();
JsonWriterSettings relaxed = JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build();
CustomeObject obj = gson.fromJson(doc.toJson(relaxed), CustomeObject.class);
If serializing to a String is an option for you, you can configure GSON to do so with:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setLongSerializationPolicy( LongSerializationPolicy.STRING );
Gson gson = gsonBuilder.create();
This will produce something like:
{numbers : [ "268627104", "485677888", "506884800" ] }
Got the same issue, after some investigation here is what I found.
The behavior:
Gson
would convert it as Double
,Jackson
would convert it as Integer
or Long
, depends on how large the number is.Possible solutions:
Gson
's return value from Double
to Long
, explicitly.Jackson
instead.Code - test for Jackson
ParseNumberTest.java:
import java.util.List;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* test - jackson parse numbers,
*
* @author eric
* @date Jan 13, 2018 12:28:36 AM
*/
public class ParseNumberTest {
@Test
public void test() throws Exception {
String jsonFn = "numbers.json";
ObjectMapper mapper = new ObjectMapper();
DummyData dd = mapper.readValue(this.getClass().getResourceAsStream(jsonFn), DummyData.class);
for (Object data : dd.dataList) {
System.out.printf("data type: %s, value: %s\n", data.getClass().getName(), data.toString());
Assert.assertTrue(data.getClass() == Double.class || data.getClass() == Long.class || data.getClass() == Integer.class);
System.out.printf("%s\n\n", "------------");
}
}
static class DummyData {
List<Object> dataList;
public List<Object> getDataList() {
return dataList;
}
public void setDataList(List<Object> dataList) {
this.dataList = dataList;
}
}
}
numbers.json:
{
"dataList": [
150000000000,
150778742934,
150000,
150000.0
]
}
How to run:
Jackson
& TestNG
.numbers.json
at the same package as ParseNumberTest.java
.Output:
data type: java.lang.Long, value: 150000000000
------------
data type: java.lang.Long, value: 150778742934
------------
data type: java.lang.Integer, value: 150000
------------
data type: java.lang.Double, value: 150000.0
------------
PASSED: test
I did not find a solution to my problem of gson formatting numbers ending in 0 to scientific notation. I instead used a work-around to convert this scientific notation into a double that I formatted with commas. "value" is the json string.
private String formatNumber(String value) {
double dValue = Double.parseDouble(value);
String pattern = "#,###";
DecimalFormat formatter = new DecimalFormat(pattern);
String newNumber = formatter.format(dValue);
return newNumber;
}
This doesn't answer the question asked but is an added step to work-around the problem to display the numbers as required by the system.
Another work around is to use the JsonParser class instead. This will return the Gson object representations (JsonElement) rather than a user defined class, but avoids the problem of conversion to scientific notation.
import java.lang.reflect.Type;
import java.util.Map;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
public class GsonTest
{
public static void main(String[] args)
{
String json = "{numbers:[268627104,485677888,506884800]}";
Gson gson = new Gson();
Type type = new TypeToken<Map<String, Object>>(){}.getType();
Map<String, Object> jsonMap = gson.fromJson(json, type);
System.out.println("Gson output:");
System.out.println(jsonMap);
JsonParser jsonParser = new JsonParser();
JsonElement jsonElement = jsonParser.parse(json);
System.out.println("JsonParser output:");
System.out.println(jsonElement);
}
}
Code Output:
Gson output:
{numbers=[2.68627104E8, 4.85677888E8, 5.068848E8]}
JsonParser output:
{"numbers":[268627104,485677888,506884800]}
I had a similar problem, and it not only converts integers to double, but it actually loses precision for certain long numbers, as described in this related question.
I tracked down this conversion to ObjectTypeAdapter
's read
method, specifically:
case NUMBER:
return in.nextDouble();
It may be possible to plug in a modified TypeAdapter
for Object
, but I couldn't get that to work, so instead I just copied the read
method (Object read(JsonReader in)
) to my own code and modified the above lines to this:
case NUMBER:
final String s = in.nextString();
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
// ignore
}
try {
return Long.parseLong(s);
} catch (NumberFormatException e) {
// ignore
}
return Double.parseDouble(s);
I wish Gson did this by default..
Then I put the other connecting pieces in a helper method that looks something like this:
public static Object parse(final Reader r) {
try (final JsonReader jr = new JsonReader(r)) {
jr.setLenient(true);
boolean empty = true;
Object o = null;
try {
jr.peek();
empty = false;
o = read(jr);
} catch (EOFException e) {
if (!empty) {
throw new JsonSyntaxException(e);
}
}
if (o != null && jr.peek() != JsonToken.END_DOCUMENT) {
throw new JsonIOException("JSON document was not fully consumed.");
}
return o;
} catch (IOException e) {
throw new JsonIOException(e);
}
}
So now instead of new Gson().fromJson(r, Object.class)
, I call parse(r)
.
This works well for me because I want to be able to parse json data with any structure, but if you have a particular class you're targeting, you probably just need to eliminate occurrences of Object
within that class's members.