Im learning how to produce and consume JSON in rest services, but I wanna learn it well so im trying all possible cases of objects, one of them is an object that has an List attribute like this class:
import java.util.List;
public class PruebaJSON {
private String nombre;
private List atributos;
private String descripcion;
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public List getAtributos() {
return atributos;
}
public void setAtributos(List atributos) {
this.atributos = atributos;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
}
Then all what im doing on my rest service method is this:
#POST
#Path("/prueba")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public PruebaJSON prueba(String data) {
try {
JSONObject json = new JSONObject(data);
Gson convertir = new GsonBuilder().create();
PruebaJSON pruebaJson = convertir.fromJson(json.toString(), PruebaJSON.class);
return pruebaJson;
} catch (Exception e) {
System.out.println("error " + e);
return null;
}
}
Then in POSTMAN I pass this:
{
"descripcion": "Primera prueba",
"nombre": "Prueba 1",
"atributos": [
"hello",
"kek",
"lul"
]
}
And it works fine, the problem is when I try to do the same by Java, for example:
List atributos = new ArrayList<>();
atributos.add("hello");
atributos.add("kek");
atributos.add("lul");
System.out.println(bus.prueba("Prueba 1", "Primera Prueba", atributos));
bus.prueba just executes the service but then in console I get this error:
14:16:56,567 INFO [stdout] (default task-2) error com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 66 path $.atributos
I did the search of the error and found this:
Gson: Expected begin_array but was STRING
I understand the error but whats the solution?
I can't really control how the JSON builds the arraylist can I?
This is the method prueba in my client:
public String prueba(String nombre, String descripcion, List atributos) {
HashMap map = new HashMap<>();
map.put("nombre", nombre);
map.put("descripcion", descripcion);
map.put("atributos", atributos);
String respuesta = utilidadesRestSeguridad.consumir("prueba", map);
return respuesta;
}
In my client component this is the method that builds the json:
public static JsonObject generateJSON(HashMap map) throws MalformedURLException {
JsonObject json = new JsonObject();
for (Object key : map.keySet()) {
json.addProperty(key.toString(), map.get(key).toString());
}
return json;
}
And thats it guys if you wanna see more code or me to explain something tell me I appreciate any help.
I think maybe the error is in the method generateJSON because of the .toString(), but then how I should handle that case?
Assuming that the line utilidadesRestSeguridad.consumir("prueba", map) ends up calling your generateJSON method downstream, then your issue is likely in the generateJSON() method as you suspect. Basically, you are just adding all elements as strings. If one of the elements in your map is an instance of a List, then you need to call JsonObject#add("atributos", value). For example, you will need something like the following code:
if (map.get(key) instanceof List) {
json.add(key.toString(), map.get(key);
} else {
json.addProperty(key.toString(), map.get(key).toString());
}
As I suspected, the error was in the generateJSON method, needed to add this validation that entpnerd suggested:
public static JsonObject generateJSON(HashMap map) throws MalformedURLException {
JsonObject json = new JsonObject();
for (Object key : map.keySet()) {
if (map.get(key) instanceof List) {
JsonParser parser = new JsonParser();
parser.parse((map.get(key).toString()));
json.add(key.toString(), parser.parse((map.get(key).toString())));
} else {
json.addProperty(key.toString(), map.get(key).toString());
}
}
return json;
}
Notice that I had to use JsonParser, not sure how is it working but at the end that made it work.
Source: How to parse this JSON String with GSON?
Anyways Im gonna try the solution entpnerd is suggesting and post it too.
Here is the implementation of entpnerd suggestion:
public static JsonObject generateJSON(HashMap map) throws MalformedURLException {
JsonObject json = new JsonObject();
for (Object key : map.keySet()) {
if (map.get(key) instanceof List) {
JsonArray jsonArray = new JsonArray();
for (Object object : (ArrayList<Object>) map.get(key)) {
jsonArray.add(object.toString());
}
json.add(key.toString(), jsonArray);
} else {
json.addProperty(key.toString(), map.get(key).toString());
}
}
return json;
}
it works too, you guys decide which one to use, thanks very much.
My only question is, what if the element that is an array, has more arrays inside it, what would you do?
You don't need to get that json value manually, add requestbody annotation to your method parameter
public PruebaJSON prueba(#RequestBody PruebaJSON json){
System.out.println(json);
};
Related
I have some problem fetching info from JSON. I'm confused about whether to use ArrayList or any other data type to retrieve data from JSON server.
I've tried to fetch data using
ArrayList<String>
in model.
Below is data format of JSON
[
{
"sun_timing": "{\"sun_from\":\"12:30\",\"sun_to\":\"4:30\"}",
"mon_timing": "{\"mon_from\":\"3:00\",\"mon_to\":\"4:30\"}"
},
{
"sun_timing": "{\"sun_from\":\"12:30\",\"sun_to\":\"4:30\"}",
"mon_timing": "{\"mon_from\":\"3:00\",\"mon_to\":\"4:30\"}"
}
]
I want to fetch all sun_timing data and mon_timing data.
That is sun_from,sun_to and mon_from,mon_to data.
Your Plain Old Java Object(POJO) for your json looks like this:
public class Example {
#SerializedName("sun_timing")
#Expose
private String sunTiming;
#SerializedName("mon_timing")
#Expose
private String monTiming;
public String getSunTiming() {
return sunTiming;
}
public void setSunTiming(String sunTiming) {
this.sunTiming = sunTiming;
}
public String getMonTiming() {
return monTiming;
}
public void setMonTiming(String monTiming) {
this.monTiming = monTiming;
}
}
See also: https://stackoverflow.com/a/40973753/10452701 for more details about How to get json via Rerofit2.
try this out working for me
private List<String> getSunList() {
ArrayList sunList = new ArrayList<String>()
String sun_json = your_json_string
try {
JSONObject jsonObject = new JSONObject(sun_json)
Log.d(TAG, "jsonObject: "+jsonObject)
Log.d(TAG, "jsonObject: "+sun_json)
JSONArray jsonArray = jsonObject.getJSONArray("sun_timing")
for (i in 0 until jsonArray.length())
{
JSONObject obj = jsonArray.get(i) as JSONObject
String sun_from = obj.getString("sun_from")
String sun_to = obj.getString("sun_to")
sunList.add(sun_from)
Log.d(TAG, "obj= "+obj)
}
}
catch (e: java.lang.Exception)
{
}
return sunList
}
I am trying to parse a JSON API response of historical time series data with potentially thousands of line. The response is in the following format:
{
"name": "AAPL",
"history": {
"2019-03-05": {
"open": "175.94",
"close": "175.53",
"high": "176.00",
"low": "174.54",
"volume": "19163899"
},
"2019-03-04": {
"open": "175.69",
"close": "175.85",
"high": "177.75",
"low": "173.97",
"volume": "27436203"
}
}
}
I would like to write the response to a Spring repository. I have a simple code to do this and a section is shown below:
RestTemplate restTemplate = new RestTemplate();
JsonParser jsonParser = new JsonParser();
JsonObject jsonObject = (JsonObject) jsonParser.parse(result);
JsonElement jsonElement = jsonObject.get("history");
Set<Map.Entry<String, JsonElement>> entrySet = jsonElement.getAsJsonObject().entrySet();
for (Map.Entry<String, JsonElement> entry : entrySet) {
StockHistory stockHistory = new StockHistory();
stockHistory.setSymbol(stk);
// .... Other code
}
I set the object properties as per JSON response, add the object to a list, and finally save the list to a repository. This process is very slow presumably because I am creating a new StockHistory object for every element in the JSON return. I was wondering if there is a better way of doing it.
As you cannot modify the JSON structure. I would like to add the following code that can parse the JSON that you provided in a simple class called Repo. In order to do that you need to add the library from here that I have used.
Now you need to add the following classes in your code.
public class Repo {
public String name;
public ArrayList<History> histories;
public Repo() {
histories = new ArrayList<History>();
}
}
public class History {
public String date;
public HistoryElements elements;
}
public class HistoryElements {
public String volume;
public String high;
public String low;
public String close;
public String open;
}
Hence I have written a RepoParser and tested it with your JSON String and it parses the JSON into the classes.
import com.oracle.javafx.jmx.json.JSONException;
import org.json.JSONObject;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Iterator;
public class RepoParser {
public static Repo parseRepo(String jsonString) throws ParseException, JSONException {
JSONObject jsonObject = new JSONObject(jsonString);
Iterator<?> iterator = jsonObject.keys();
Repo repo = new Repo();
while (iterator.hasNext()) {
String obj = iterator.next().toString();
if (obj.equals("name")) repo.name = obj;
else repo.histories = parseHistory((JSONObject) jsonObject.get(obj));
}
return repo;
}
public static ArrayList<History> parseHistory(JSONObject jsonObject) throws ParseException, JSONException {
Iterator<?> iterator = jsonObject.keys();
ArrayList<History> historyList = new ArrayList<>();
while (iterator.hasNext()) {
String obj = iterator.next().toString();
History history = new History();
history.date = obj;
history.elements = parseHistoryElement((JSONObject) jsonObject.get(obj));
historyList.add(history);
}
return historyList;
}
public static HistoryElements parseHistoryElement(JSONObject jsonObject) throws ParseException, JSONException {
Iterator<?> iterator = jsonObject.keys();
HistoryElements historyElements = new HistoryElements();
while (iterator.hasNext()) {
String obj = iterator.next().toString();
if (obj.equals("open")) historyElements.open = jsonObject.getString("open");
if (obj.equals("close")) historyElements.close = jsonObject.getString("close");
if (obj.equals("high")) historyElements.high = jsonObject.getString("high");
if (obj.equals("low")) historyElements.low = jsonObject.getString("low");
if (obj.equals("volume")) historyElements.volume = jsonObject.getString("volume");
}
return historyElements;
}
}
Just use the RepoParser class like the following.
try {
Repo repo = RepoParser.parseRepo(jsonString);
} catch (Exception e) {
e.printStackTrace();
}
I have created a Gist as well for convenience.
Update
You might consider adding all the Repo in a list and then save all of them into your database at once using the save method of the repository.
Hence the code should be something like the following.
try {
while(there is no repo left for parsing) {
Repo repo = RepoParser.parseRepo(jsonString);
repoList.add(repo)
}
yourRepository.save(repoList); // Save all values at once
} catch (Exception e) {
e.printStackTrace();
}
Hope that helps!
After some research, I found that the problem was with hibernate. As far as I understand it, a useful feature of hibernate is that it caches objects, but this causes a problem when a large number of objects are created for insertion. The issue can be resolved by batch processing using the spring.jpa.properties.hibernate.jdbc.batch_size property and using a sequence generator in the entity class. Now saving the list of many thousands of lines is much faster.
I've a requirement. lets say I have a JSON file as shown below.
{
"orgId": 27,
"orgType":"MotorBikes",
"orgName":"ROYAL Enfield",
"orgAddress":"Express Estate",
"orgCity":"Chennai",
"orgState":"TamilNadu"
}
So I need to do two validations. one is checking all the json fields and return true or false and second one should have methods to validate partial response like for example: isExists(jsonObject, "orgType":"MotorBikes") should return true. This comparison should be done using jax-rs libraries. So if anybody who is familiar with this please tell me. This would help me a lot.
Package javax.json should be enough.
import javax.json.JsonObject;
public static void main(String[] args){
JsonObject jsonObj = /* your json */;
boolean all = checkAll(jsonObj,new String[]{"orgId","orgType","orgName","orgAddress","orgCity","orgState"});
boolean one = isExists(jsonObj,"orgType","MotorBikes");
}
private boolean checkAll(JsonObject jsonObj, String[] keys) {
for(String key: keys) {
if(jsonObj.get(key)==null) return false;
}
return true;
}
private boolean isExists(JsonObject jsonObj, String key, String value) {
return (jsonObj.get(key)!=null && jsonObj.get(key).equals(value));
}
UPDATE:
A more focused answer using org.json library which is in your dependency and is a JSON library, not JAX-RS.
#Test
public void test() throws FileNotFoundException{
String jsonAsString = when().get("/your.get").then().contentType(ContentType.JSON).extract().response().asString();
JSONObject jsonFromResponse = new JSONObject(jsonAsString);
File file = /** Load your file ***/
FileInputStream is = new FileInputStream(file);
JSONTokener tokener = new JSONTokener(is);
while(tokener.more()) { // Iterate through Json file
JSONObject obj = (JSONObject) tokener.nextValue();
for(String key: obj.keySet()) {
boolean valid = validateField(key, jsonFromResponse);
System.out.println("'"+key+"' field "+(valid?"not ":"")+"present");
}
}
boolean v = validateValue(jsonFromResponse, "orgName", "ROYAL Enfield");
System.out.println("Validation: "+v);
}
private boolean validateValue(JSONObject json, String key, String value) {
if(validateField(key,json))
return value.equals(json.getString(key));
return false;
}
private boolean validateField(String key, JSONObject jsonFromResponse) {
Object valueFromResponse = null;
try {
valueFromResponse = jsonFromResponse.get(key);
}
catch(JSONException e){
valueFromResponse = null;
}
return valueFromResponse!=null;
}
This is the JSON array:
{
"server_response": [{
"Total": "135",
"Paid": "105",
"Rest": "30"
}]
}
So, how can i get the object names? I want to put them in separate TextView.
Thanks.
Put this out side everything. I mean outside onCreate() and all.
private <T> Iterable<T> iterate(final Iterator<T> i){
return new Iterable<T>() {
#Override
public Iterator<T> iterator() {
return i;
}
};
}
For getting the names of objects :
try
{
JSONObject jsonObject = new JSONObject("{" +"\"server_response\": [{" +"\"Total\": \"135\"," +"\"Paid\": \"105\"," +"\"Rest\": \"30\"" +"}]"+"}";);
JSONArray jsonArray = jsonObject.getJSONArray("server_response");
JSONObject object = jsonArray.getJSONObject(0);
for (String key : iterate(object.keys()))
{
// here key will be containing your OBJECT NAME YOU CAN SET IT IN TEXTVIEW.
Toast.makeText(HomeActivity.this, ""+key, Toast.LENGTH_SHORT).show();
}
} catch (JSONException e) {
e.printStackTrace();
}
Hope this helps :)
My suggestion:
Go to this website:
Json to pojo
Get your pojo classes and then use them in Android.
All you need to do is to use Gson.fromGson(params here).
One of your params is the class that you created using the online schema.
You can use jackson ObjectMapper to do this.
public class ServerResponse {
#JsonProperty("Total")
private String total;
#JsonProperty("Paid")
private String paid;
#JsonProperty("Rest")
private String rest;
//getters and setters
//toString()
}
//Now convert json into ServerResponse object
ObjectMapper mapper = new ObjectMapper();
TypeReference<ServerResponse> serverResponse = new TypeReference<ServerResponse>() { };
Object object = mapper.readValue(jsonString, serverResponse);
if (object instanceof ServerResponse) {
return (ServerResponse) object;
}
JSONObject jsonObject = new JSONObject("Your JSON");
int Total = jsonObject.getJSONArray("server_response").getJSONObject(0).getInt("Total");
int Paid = jsonObject.getJSONArray("server_response").getJSONObject(0).getInt("Paid");
int Rest = jsonObject.getJSONArray("server_response").getJSONObject(0).getInt("Rest");
I have the following json
"notes": {"note": [
{
"content": "Having wisdom teeth removed.",
"from": "employee"
},
{
"content": "Get well soon",
"from": "manager"
}
]},
the issue is that the value coud also be
"notes": "",
or
"notes": {"note": {
"content": "This is a test note.",
"from": "employee"
}},
and storing it in these
public class Notes
{
#SerializedName ("note")
public List<Note> note;
}
public class Note
{
#SerializedName ("content")
public String content;
#SerializedName ("from")
public String from;
}
I believe I solved the issue of not being an array but being an single object by doing this
public class Json {
private static Gson gson;
private static class MyNoteClassTypeAdapter implements JsonDeserializer<List<RequestsDTO.Note>> {
public List<RequestsDTO.Note> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext ctx) {
List<RequestsDTO.Note> vals = new ArrayList<RequestsDTO.Note>();
if (json.isJsonArray()) {
for (JsonElement e : json.getAsJsonArray()) {
vals.add((RequestsDTO.Note) ctx.deserialize(e, RequestsDTO.Note.class));
}
} else if (json.isJsonObject()) {
vals.add((RequestsDTO.Note) ctx.deserialize(json,RequestsDTO.Note.class));
} else {
throw new RuntimeException("Unexpected JSON type: " + json.getClass());
}
return vals;
}
}
public static Gson getGson()
{
if (gson == null)
{
Type ListType = new TypeToken<List<RequestsDTO.Note>>() {}.getType();
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(DateTime.class, new DateTimeSerializer());
builder.registerTypeAdapter(ListType, new MyNoteClassTypeAdapter());
gson = builder.create();
}
return gson;
}
}
And now I am stuck on when the whole thing just comes back as a string....
Refer the code snippet below to deserialize your json using Gson library without exceptions.
String jsonStr = "your json string ";
Gson gson = new Gson();
JsonObject jsonObj = gson.fromJson (jsonStr, JsonElement.class).getAsJsonObject();
JsonElement elem = jsonObj.get("note");
if(elem.isJsonArray()) { //**Array**
List<Note> notelist = gson.fromJson(elem.toString(), new TypeToken<List<Note>>(){}.getType());
} else if(elem.isJsonObject()) { //**Object**
Note note = gson.fromJson(elem.toString(), Note.class);
} else { //**String**
String note = elem.toString();
}
The idea is try to get "note" field (from "notes" JSONObject) as JSONArray first and if it throws exception that will mean that there is no "note" JSONArray into "notes" JSONObject and that will mean that "note" is JSONObject. The same way we can figure out situation when note field is String.
try {
//String jsonString="{\"notes\": {\"note\": [{\"content\": \"Having wisdom teeth removed.\",\"from\": \"employee\" }, {\"content\": \"Get well soon\", \"from\": \"manager\"} ] }}";
//String jsonString="{\"notes\": { \"note\": {\"content\": \"This is a test note.\",\"from\": \"employee\"}}}";
String jsonString="{\"notes\": { \"note\": \"\"}}";
JSONObject jsonObject=new JSONObject(jsonString);
JSONObject jsonObjectNotes=jsonObject.getJSONObject("notes");
try{
JSONArray jsonArrayNote=jsonObjectNotes.getJSONArray("note");
for (int i = 0; i < jsonArrayNote.length(); i++) {
JSONObject jsonObject2= jsonArrayNote.getJSONObject(i);
String stringContent=jsonObject2.getString( "content");
String stringFrom= jsonObject2.getString( "from");
Log.e(getClass().getName(), "content="+stringContent +"; from="+stringFrom);
}
}
catch(JSONException e){
//that means that jsonObjectNotes has no jsonArray with name "notes" and "notes" is jsonObject
try{
JSONObject jsonObject3=jsonObjectNotes.getJSONObject("note");
String stringContent=(String) jsonObject3.get( "content");
String stringFrom=(String) jsonObject3.get( "from");
Log.e(getClass().getName(), "content="+stringContent +"; from="+stringFrom);
}
catch(JSONException ex){
//that means that jsonObjectNotes has no jsonObject with name "notes" and "notes" is empty String
String stringNote=jsonObjectNotes.getString("note") ;
Log.e(getClass().getName(), "note is string ="+ stringNote);
}
}
} catch (JSONException e) {
e.printStackTrace();
}
In my example code another get operations can also throw jsonExceptions but I think you get the idea.
Have a look at Genson library http://code.google.com/p/genson/.
If your classes are inner classes make them static.
The following code should solve your problem.
Genson genson = new Genson.Builder().withDeserializerFactory(new NotesDeserializerFactory()).create();
Notes notes = genson.deserialize(in, Notes.class);
// Define a factory so you can delegate the deserialization to existing mechanisms for lists and beans
class NotesDeserializerFactory implements Factory<Deserializer<Notes>> {
#Override
public Deserializer<Notes> create(Type type, Genson genson) {
Converter<List<Note>> noteListConverter = genson.provideConverter(new GenericType<List<Note>>() {}.getType());
Converter<Note> noteConverter = genson.provideConverter(Note.class);
return new NotesDeserializer(noteListConverter, noteConverter);
}
}
// define an implementation for you Notes class so you can handle the different cases
class NotesDeserializer implements Deserializer<Notes> {
private final Converter<List<Note>> noteListConverter;
private final Converter<Note> noteConverter;
public NotesDeserializer(Converter<List<Note>> noteListConverter,
Converter<Note> noteConverter) {
this.noteListConverter = noteListConverter;
this.noteConverter = noteConverter;
}
#Override
public Notes deserialize(ObjectReader reader, Context ctx) throws TransformationException,
IOException {
Notes notes = new Notes();
if (reader.getValueType() == ValueType.ARRAY) notes.note = noteListConverter.deserialize(reader, ctx);
else if (reader.getValueType() == ValueType.OBJECT) notes.note = Arrays.asList(noteConverter.deserialize(reader, ctx));
else { // it is a litteral (string, numeric, boolean, null)
notes.note = new ArrayList<Note>();
}
return notes;
}
}