I have a method that when I send a String (which is Standard JSON output) to it it parses it and returns the section I want. It works for the most part but if the String has a low value such as 5.8244367E-4 in it it will fail with the error:
com.codesnippets4all.json.exceptions.JSONParsingException: #Key-Heirarchy::root/resultList/result[5]/ #Key::PercentMisses Invalid VALUE_TOKEN...#Position::282
public String parse(String json, String key) {
JsonParserFactory factory = JsonParserFactory.getInstance();
JSONParser parser = factory.newJsonParser();
Map<?, ?> jsonMap = parser.parseJson(json);
String parsed = jsonMap.get("result").toString();
return parsed;
}
Is there any obvious way to get around this or else an alternate approach I can take to get the same result?
Thanks.
Related
sorry for duplicating the question, but my problem is other.
I have JSON parser method where I parse from json-string to map. But json-string has a value which is json-string too. Something like that:
{
"status_code":"255",
"data":"{\"user\":{\"idpolzovatel\":1,\"id_poluch_tip\":1,\"fio_polzovatel\":\"Andrew Artificial\",\"login\":\"imi\",\"parol\":\"698d51a19d8a121ce581499d7b701668\",\"key\":null,\"nachalnik\":1,\"buhgalter\":0,\"delopr\":1},\"token\":\"230047517dd122c8f8116a6fa591a704\"}",
"message":"Successfull!"
}
So, my parse-method:
public Map<String, String> convertToMapFromJSON(String res){
ObjectMapper objectMapper = new ObjectMapper();
Map<String, String> response = new HashMap<String, String>();
try {
response = objectMapper.readValue(res, new TypeReference<Map<String, String>>);
int t = 0;
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
I get response in client:
ResponseEntity<String> responseEntity = restTemplate.postForEntity(REST_SERVICE_URI + "/auth/", data, String.class);
get body
String res = responseEntity.getBody();//получаем тело запроса в формате JSON
then use those method:
Map<String, String> response = convertToMapFromJSON(res);
Map<String, String> data1 = convertToMapFromJSON(response.get("data"));
Map<String, String> userDetailes = convertToMapFromJSON(data1.get("user"));
but, when I use last method data1.get("user"); I get exception:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to java.lang.String
ok, got it. So, data1.get("user") isn't a string, it's linkedHashMap. So, I could do this then:
Map<String, String> userDetailes = data1.get("user");
? But then I get the error, where IDE say me, that data1.get("user") is a string.
Screenshot from debugger:
So, how can I get this LinkedHashMap with my userdata? Sorry, for my english. Thank you.
Java apply type erasure for generics. It checks type correctness at compile time and then remove generic signature in compile code (ByteCode). Therefore, there's no check at runtime.
See this example which have same behaviour as your JSON library:
/** Returns a generic map which all keys are string but not values **/
T <T extends Map> raw(Class<T> clazz) {
Map object = new LinkedHashMap();
object.put("string", "This is a String");
object.put("map" , new LinkedHashMap());
return (T) object;
}
Here is your code:
/** codes you try to execute/write **/
void withStringValues() {
Map<String,String> object = raw(Map<String,String>.class);
String string = object.get("string"); // Ok
String map = object.get("map"); // ClassCastException
Map map = object.get("map"); // Doesn't compile
}
As you can see the call to raw is considered valid as compiled code don't check for generics. But it makes an invalid and implicit cast from Map to Map<String,String> which actually doesn't occured in compiled code.
Generics are remove and is the compiled version:
void withTypeErasure() {
Map object = raw(Map.class);
String string = (String) object.get("string");
String map = (String) object.get("map");
}
As you can see, Java compiler has removed all generic and adds necessary casts. You can see what's going wrong here.
Your real code must look like this:
void withRealValues() {
Map<String,Object> object = raw(Map<String,Object>.class);
String string = (String) object.get("string"); // Ok
Map<String,Object> map = (Map) object.get("map"); // Ok
}
Looks like ObjectMapper has decoded the string to be of JSON format and has parsed it for you. You could just add a new method to parse (data1.get("user")) which returns a Map.
I want to parse below given data in to some java object, but I am not able to parse. String is as follows -
{\"objectsTree\":\"{\"Address\":[],\"Customer\":[\"Address\"]}\",\"objectsSequence\":\"[\"Customer\",\"Address\"]\"}
I have tried parsing this into HashMap and HashMap
but this is returning malformed JSON exception, and that is making sense, because of too many double quotes objects are ending abruptly. I want to parse this as follows-
{
"objectsTree":"{"Address":[],"Customer":["Address"]}",
"objectsSequence":"["Customer","Address"]"
}
here you can see that objectsTree is one object against one string and objectSequence is another. to be specific object tree is supposed to be a treemap , object sequence is supposed to be a ArrayList.
Any Idea how should I proceed.
code update-
package org.syncoms.backofficesuite.controller;
import java.util.HashMap;
import com.google.gson.Gson;
public class Test {
public static void main(String[] args) {
//String json = "{\"Success\":true,\"Message\":\"Invalid access token.\"}";
String json ="{\"objectsTree\":\"{\"Address\":[],\"Customer\":[\"Address\"]}\",\"objectsSequence\":\"[\"Customer\",\"Address\"]\"}";
Gson jsonParser = new Gson();
#SuppressWarnings("unchecked")
HashMap<String,Object> jo = (HashMap<String,Object>) jsonParser.fromJson(json, HashMap.class);
System.out.println(jo);
//Assert.assertNotNull(jo);
//Assert.assertTrue(jo.get("Success").getAsString());
}
}
the error which I am getting -
Exception in thread "main" com.google.gson.JsonParseException: Failed parsing JSON source: java.io.StringReader#201644c9 to Json
at com.google.gson.JsonParser.parse(JsonParser.java:59)
at com.google.gson.Gson.fromJson(Gson.java:443)
at com.google.gson.Gson.fromJson(Gson.java:396)
at com.google.gson.Gson.fromJson(Gson.java:372)
at org.syncoms.backofficesuite.controller.Test.main(Test.java:16)
Caused by: com.google.gson.ParseException: Encountered " <IDENTIFIER_SANS_EXPONENT> "Address "" at line 1, column 19.
Was expecting one of:
"}" ...
"," ...
The main issue here is that the input is simply not a valid JSON String, and no JSON parser is going to accept it. the doulbe qoutes have to be escaped.
a Valid JSON String is as follows:
String jsonInput = "{\"objectsTree\":\"{\\\"Address\\\":[],\\\"Customer\\\":[\\\"Address\\\"]}\",\"objectsSequence\":\"[\\\"Customer\\\",\\\"Address\\\"]\"}";
and this can be parsed using, for instance, Jackson:
ObjectMapper om = new ObjectMapper();
TypeFactory tf = om.getTypeFactory();
JavaType mapType = tf.constructMapType(HashMap.class, String.class, String.class);
Map<String, String> map = (Map<String, String>)om.readValue(jsonInput, mapType);
System.out.println(map);
Printout is:
{objectsSequence=["Customer","Address"], objectsTree={"Address":[],"Customer":["Address"]}}
There are multiple ways you could do that.
Firstly, if your data has always the same format you can simply create some methods which will create your TreeMap and ArrayList as required. You can do everything you want with String.replace(), StringTokenizer, matcher pattern. You can split your data into tokens and based on your needs place them in your required data structure. I find this way quite efficient and once you get to know better how to parse data and extract what you need, you can use this in many other examples.
If your data is formatted in JSON then there might be even easier ways of parsing it.You can decode it as Java object quite easy.
JSON string is not well formed one. Try as below
{
"objectsTree":{"Address":[],"Customer":["Address"]},
"objectsSequence":["Customer","Address"]
}
JSON key is always string &
JSON values can be:
•A number (integer or floating point)
•A string (in double quotes)
•A Boolean (true or false)
•An array (in square brackets)
•An object (in curly braces)
•null.
See the below code with well formed string and its output
String a = "{\r\n" +
"\"objectsTree\":{\"Address\":[],\"Customer\":[\"Address\"]},\r\n" +
"\"objectsSequence\":[\"Customer\",\"Address\"]\r\n" +
"}";
ObjectMapper mapper = new ObjectMapper();
HashMap<String,Object> jo = (HashMap<String,Object>) mapper.readValue(a, HashMap.class);
System.out.println("result: "+ jo);
result: {objectsTree={Address=[], Customer=[Address]}, objectsSequence=[Customer, Address]}
with your json string
String json ="{\"objectsTree\":\"{\"Address\":[],\"Customer\":[\"Address\"]}\",\"objectsSequence\":\"[\"Customer\",\"Address\"]\"}";
ObjectMapper mapper = new ObjectMapper();
HashMap<String,Object> jo = (HashMap<String,Object>) mapper.readValue(json, HashMap.class);
System.out.println("result: "+ jo);
error :
Exception in thread "main" org.codehaus.jackson.JsonParseException: Unexpected character ('A' (code 65)): was expecting comma to separate OBJECT entries
at [Source: java.io.StringReader#77d2b01b; line: 1, column: 20]
at org.codehaus.jackson.JsonParser._constructError(JsonParser.java:943)
In your json string, for key objectsTree, the value is started with \" and its matching \" is closed before string Address. This is causing the parse error.
"{\"Address
The other two answers also saying that your json string is in invalid format.
I also added the supported json values for your reference.
If you change to correct format, any json parser will work,
I'm relatively new to Java and I'm asking to write test of JSON response server.
I found JSONassert very useful but I didn't succeed to write the method getRESTData.
Anybody can help please?
#Test
public void testGetFriends() throws JSONException {
JSONObject data = getRESTData("/friends/367.json");
String expected = "{friends:[{id:123,name:\"Corby Page\"}"
+ ",{id:456,name:\"Solomon Duskis\"}]}";
JSONAssert.assertEquals(expected, data, false);
}
You can get the data as String and pass it into JSONAssert.assertEquals.
Converting to JSONObject isn't necessary.
To fetch data from an URL, you can use URL.getContent method:
final String data = new URL("...").getContent();
String expected = "{friends:[{id:123,name:\"Corby Page\"}"
+ ",{id:456,name:\"Solomon Duskis\"}]}";
JSONAssert.assertEquals(expected, data, false);
This can also be achieved with ModelAssert - https://github.com/webcompere/model-assert , which can take anything serializable to JSON as an input:
#Test
public void testGetFriends() throws JSONException {
JSONObject data = getRESTData("/friends/367.json");
String expected = "{friends:[{id:123,name:\"Corby Page\"}"
+ ",{id:456,name:\"Solomon Duskis\"}]}";
assertJson(data).isEqualTo(expected);
}
IIRC JSONObject is essentially a Map, so assertJson will convert it to the internal JsonNode format it uses for comparison.
I am getting the data from the Zookeeper node like this
byte[] bytes = client.getData().forPath("/my/example/node1");
String ss = new String(bytes);
Here ss will have data like this which is a simple JSON String consisting of key value pair -
{"description":"Some Text", "machinename":"machineA", "ipaddress":"192.128.0.0"}
Now I want to append one more key value pair at the end to the above JSON String. This is the below key value pair I want to append -
"version":"v3"
So the final JSON String will look like this -
{"description":"Some Text", "machinename":"machineA", "ipaddress":"192.128.0.0", "version":"v3"}
What's the best and efficient way to do this?
Use a JSON Parser/Generator to parse your given JSON to a tree structure and then add your JSON field.
With Gson, that would look something like this
Gson gson = new Gson();
JsonObject jsonObject = gson.fromJson(ss, JsonObject.class); // parse
jsonObject.addProperty("version", "v3"); // modify
System.out.println(jsonObject); // generate
prints
{"description":"Some Text","machinename":"machineA","ipaddress":"192.128.0.0","version":"v3"}
Will Zookeeper always return valid JSON or their custom format? Be aware of that.
When it comes to JSON processing, string manipulation only works in special and simple cases. For the general case, a good JSON parser library should be used.
Jackson is among the top of such libraries in terms of performance, efficiency, versatility and reliability, plus it is published under the commercial-friendly Apache 2.0 license.
Following is a simple implementation of the requested answer in Jackson.
public static void main(String[] args)
{
String ss = "{\"description\":\"Some Text\", \"machinename\":\"machineA\", \"ipaddress\":\"192.128.0.0\"}";
System.out.println("JSON string before: " + ss);
try
{
ObjectMapper mapper = new ObjectMapper();
Map<String, String> map = (Map<String, String>)mapper.readValue(ss, Map.class);
map.put("version", "v3");
ss = mapper.writeValueAsString(map);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("JSON string after: " + ss);
}
Basic string manipulation. Insert your additional string before the final close brace }. Make sure to add a comma.
Json objects don't need to be ordered.
String json = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
String json2 = "\"version\":\"v3\"";
json2 = ',' + json2;
String json3 = json.substring(0,json.length()-1) + json2 + json.charAt(json.length()-1);
That should be the simplest, most efficient way, if that's all you need to do.
For additional reading on String manipulation,
http://docs.oracle.com/javase/tutorial/java/data/manipstrings.html
If I try to deserialize my json:
String myjson = "
{
"intIdfCuenta":"4720",
"intIdfSubcuenta":"0",
"floatImporte":"5,2",
"strSigno":"D",
"strIdfClave":"FT",
"strDocumento":"1",
"strDocumentoReferencia":"",
"strAmpliacion":"",
"strIdfTipoExtension":"IS",
"id":"3"
}";
viewLineaAsiento asiento = gson.fromJson(formpla.getViewlineaasiento(),viewLineaAsiento.class);
I get this error:
com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: For input string: "5,2"
How can I parse "5,2" to Double???
I know that if I use "floatImporte":"5.2" I can parse it without any problem but I what to parse "floatImporte":"5,2"
Your JSON is in first place bad. You shouldn't be representing numbers as strings at all. You should basically either have all String properties in your ViewLineaAsiento Java bean object representation as well, or to remove those doublequotes from JSON properties which represent numbers (and fix the fraction separator to be . instead of ,).
If you're absolutely posisive that you want to continue using this bad JSON and fix the problem by a workaround/hack instead of fixing the problem by its roots, then you'd need to create a custom Gson deserializer. Here's a kickoff example:
public static class BadDoubleDeserializer implements JsonDeserializer<Double> {
#Override
public Double deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException {
try {
return Double.parseDouble(element.getAsString().replace(',', '.'));
} catch (NumberFormatException e) {
throw new JsonParseException(e);
}
}
}
You can register it via GsonBuilder#registerTypeAdapter() as follows:
Gson gson = new GsonBuilder().registerTypeAdapter(Double.class, new BadDoubleDeserializer()).create();
ViewLineaAsiento asiento = gson.fromJson(myjson, ViewLineaAsiento.class);