I'm trying to append JSONObjects inside a JSONArray that is called Records .
The first time I save it it saves it this way that is ok
{
"Records": [
{
"travelTime": 2,
"totalDistance": 0,
"pace": 0,
"kCalBurned": 0,
"latlng": "[lat\/lng: (-32.1521234,-63.66412321)]"
}
]
}
But when I try to append again a new jsonobject inside Records, it creates a new JSONArray for it, and I just want to append a new object inside records
{
"Records": [
{
"travelTime": 2,
"totalDistance": 0,
"pace": 0,
"kCalBurned": 0,
"latlng": "[lat\/lng: (-31.6432292,-63.3667462)]"
}
]
}{
"Records": [
{
"travelTime": 1,
"totalDistance": 0,
"pace": 0,
"kCalBurned": 0,
"latlng": "[lat\/lng: (-31.9522431,-64.3461241)]"
}
]
}
This is the code I use to save the Records
private void writeJsonData(long travelTime,float totalDistance, float pace, float kCalBurned, LinkedList<LatLng> latlng){
String jsonStr = "";
JSONObject records = new JSONObject();
try {
records.put("travelTime", travelTime);
records.put("totalDistance", totalDistance);
records.put("pace", pace);
records.put("kCalBurned", kCalBurned);
records.put("latlng", latlng);
JSONArray jsonArray = new JSONArray();
jsonArray.put(records);
JSONObject recordsObj = new JSONObject();
recordsObj.put("Records", jsonArray);
jsonStr = recordsObj.toString();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String file_name = "records.json";
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(new File(mContext.getFilesDir(),file_name),true);
fileOutputStream.write(jsonStr.getBytes());
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
You need a JSON parser so that you can locate the "Records" array inside the file and place the new data there. I used the "json simple" library (jar can be found here: https://code.google.com/archive/p/json-simple/downloads).
First you parse the file:
JSONParser parser = new JSONParser();
JSONObject records = null;
try {
records = (JSONObject) parser.parse(new FileReader("records.json"));
} catch (ParseException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
Then you locate the Records JSONArray. In there you want to append the new record:
JSONArray r = (JSONArray) records.get("Records");
Create the new record:
JSONObject NewObj = new JSONObject();
NewObj.put("travelTime", travelTime);
NewObj.put("totalDistance", totalDistance);
NewObj.put("pace", pace);
NewObj.put("kCalBurned", kCalBurned);
NewObj.put("latlng", latlng);
Add the new record to the "Records" JSONArray:
r.add(NewObj);
Write to file:
try (FileWriter file = new FileWriter("records.json")) {
file.write(records.toJSONString());
} catch (IOException ex) {
ex.printStackTrace();
}
Passing 2nd parameter to true in FileOutputStream constructor will
append jsonObject at the end of file.
To append it with JSON array inside Records object, you've to read the file first, append the new JSON object and write it back to file.
Use GSON library for conversion between java class & jSON. So you don't have to create JSON object manually each time by putting each key-pair.
Create a Java class to hold whole Records object
public class Record
{
#SerializedName("Records")
private List<Object> recordsList;
public Record()
{
this. recordsList = new ArrayList<>();
}
public List<Object> getRecordsList()
{
return recordsList;
}
}
Now create JAVA Model class to hold travel info
public class Travel {
private Integer travelTime;
private Integer totalDistance;
private Integer pace;
private Integer kCalBurned;
private LinkedList<LatLng> latlng;
public Integer getTravelTime() {
return travelTime;
}
public void setTravelTime(Integer travelTime) {
this.travelTime = travelTime;
}
public Integer getTotalDistance() {
return totalDistance;
}
public void setTotalDistance(Integer totalDistance) {
this.totalDistance = totalDistance;
}
public Integer getPace() {
return pace;
}
public void setPace(Integer pace) {
this.pace = pace;
}
public Integer getKCalBurned() {
return kCalBurned;
}
public void setKCalBurned(Integer kCalBurned) {
this.kCalBurned = kCalBurned;
}
public LinkedList<LatLng> getLatlng() {
return latlng;
}
public void setLatlng(LinkedList<LatLng> latlng) {
this.latlng = latlng;
}
}
Here is utility class with a function to append new JSON inside Records object. It will check if directory & file are created otherwise will create both.If file exist, it will read the file, append the new JSON object to list and write it back into the same file. You can change the directory & file name with yours.
Note: This class is written in Kotlin. Here is reference how to setup Android Studio for Kotlin
class Logger {
companion object {
private const val LOG_FILE_FOLDER = "Logs"
private const val LOG_FILE_NAME = "transaction"
private const val DATE_FORMAT = "yyyy-MM-dd"
private val logFileName: String
#SuppressLint("SimpleDateFormat")
get() {
var fileName = LOG_FILE_NAME
val dateFormat = SimpleDateFormat(DATE_FORMAT)
fileName += "_" + dateFormat.format(Date()) + ".json"
return fileName
}
fun logFile(json: Any) {
try {
val directoryPath = Environment.getExternalStorageDirectory().path + "/" + LOG_FILE_FOLDER
val loggingDirectoryPath = File(directoryPath)
var loggingFile = File("$directoryPath/$logFileName")
if (loggingDirectoryPath.mkdirs() || loggingDirectoryPath.isDirectory) {
var isFileReady = true
var isNewFile = false
if (!loggingFile.exists()) {
isFileReady = false
try {
loggingFile.createNewFile()
isNewFile = true
isFileReady = true
} catch (e: Exception) {
e.printStackTrace()
}
} else {
val lastFile = getLastFile(loggingFile.name, directoryPath)
loggingFile = File("$directoryPath/$lastFile")
val fileSize = getFileSize(loggingFile)
}
if (isFileReady) {
var jsonString: String? = null
if (!isNewFile) {
//Get already stored JsonObject
val stream = FileInputStream(loggingFile)
try {
val fileChannel = stream.channel
val mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size())
jsonString = Charset.defaultCharset().decode(mappedByteBuffer).toString()
} catch (e: Exception) {
e.printStackTrace()
} finally {
stream.close()
}
}
//Create record object
val record = if (!jsonString.isNullOrEmpty()) {
Gson().fromJson(jsonString, Record::class.java)
} else {
Record()
}
//Append the current json
record.recordList.add(json)
//create json to save
val jsonToSave = Gson().toJson(record)
val bufferedOutputStream: BufferedOutputStream
try {
bufferedOutputStream = BufferedOutputStream(FileOutputStream(loggingFile))
bufferedOutputStream.write(jsonToSave.toByteArray())
bufferedOutputStream.flush()
bufferedOutputStream.close()
} catch (e4: FileNotFoundException) {
e4.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
} finally {
System.gc()
}
}
}
} catch (ex: Exception) {
ex.printStackTrace()
}
}
}
}
At the end, you can log the file withlogFile method
Logger.Companion.logFile(travel);
Cheers :)
Related
I have a json with many attributes.
I want some of them to be inline. Meaning with no new break lines.
Here is my code today:
private String serializeToJson(T item) {
String json;
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
try {
json = ow.writeValueAsString(item);
} catch (IOException e) {
e.printStackTrace();
json = "";
}
return json;
}
how can I set it to be not "pretty print" for attribute path like this { "path" : { "segmentId" : 31788674, "nodeId" : 26161441, "x" : -74.236605 ...
I serialized this specific property to inline json string separately.
public ResultUiShort(ResultLong result) {
this.path = stringUtils.toInlineJson(result.path);
...
}
and
public <T> String toInlineJson(T item) {
String json;
ObjectWriter ow = new ObjectMapper().writer();
try {
json = ow.writeValueAsString(item);
} catch (IOException e) {
e.printStackTrace();
json = "";
}
return json;
}
I cant change the mapping. Can anybody help me to find the bug in my code?
I have found this standard way to change the mapping according to several tutorials. But when i'm try to call the mapping structure there just appear a blank mapping structure after manuall mapping creation.
But after inserting some data there appear the mapping specification because ES is using of course the default one. To be more specific see the code below.
public class ElasticTest {
private String dbname = "ElasticSearch";
private String index = "indextest";
private String type = "table";
private Client client = null;
private Node node = null;
public ElasticTest(){
this.node = nodeBuilder().local(true).node();
this.client = node.client();
if(isIndexExist(index)){
deleteIndex(this.client, index);
createIndex(index);
}
else{
createIndex(index);
}
System.out.println("mapping structure before data insertion");
getMappings();
System.out.println("----------------------------------------");
createData();
System.out.println("mapping structure after data insertion");
getMappings();
}
public void getMappings() {
ClusterState clusterState = client.admin().cluster().prepareState()
.setFilterIndices(index).execute().actionGet().getState();
IndexMetaData inMetaData = clusterState.getMetaData().index(index);
MappingMetaData metad = inMetaData.mapping(type);
if (metad != null) {
try {
String structure = metad.getSourceAsMap().toString();
System.out.println(structure);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void createIndex(String index) {
XContentBuilder typemapping = buildJsonMappings();
String mappingstring = null;
try {
mappingstring = buildJsonMappings().string();
} catch (IOException e1) {
e1.printStackTrace();
}
client.admin().indices().create(new CreateIndexRequest(index)
.mapping(type, typemapping)).actionGet();
//try put mapping after index creation
/*
* PutMappingResponse response = null; try { response =
* client.admin().indices() .preparePutMapping(index) .setType(type)
* .setSource(typemapping.string()) .execute().actionGet(); } catch
* (ElasticSearchException e) { e.printStackTrace(); } catch
* (IOException e) { e.printStackTrace(); }
*/
}
private void deleteIndex(Client client, String index) {
try {
DeleteIndexResponse delete = client.admin().indices()
.delete(new DeleteIndexRequest(index)).actionGet();
if (!delete.isAcknowledged()) {
} else {
}
} catch (Exception e) {
}
}
private XContentBuilder buildJsonMappings(){
XContentBuilder builder = null;
try {
builder = XContentFactory.jsonBuilder();
builder.startObject()
.startObject("properties")
.startObject("ATTR1")
.field("type", "string")
.field("store", "yes")
.field("index", "analyzed")
.endObject()
.endObject()
.endObject();
} catch (IOException e) {
e.printStackTrace();
}
return builder;
}
private boolean isIndexExist(String index) {
ActionFuture<IndicesExistsResponse> exists = client.admin().indices()
.exists(new IndicesExistsRequest(index));
IndicesExistsResponse actionGet = exists.actionGet();
return actionGet.isExists();
}
private void createData(){
System.out.println("Data creation");
IndexResponse response=null;
for (int i=0;i<10;i++){
Map<String, Object> json = new HashMap<String, Object>();
json.put("ATTR1", "new value" + i);
response = this.client.prepareIndex(index, type)
.setSource(json)
.setOperationThreaded(false)
.execute()
.actionGet();
}
String _index = response.getIndex();
String _type = response.getType();
long _version = response.getVersion();
System.out.println("Index : "+_index+" Type : "+_type+" Version : "+_version);
System.out.println("----------------------------------");
}
public static void main(String[] args)
{
new ElasticTest();
}
}
I just wanna change the property of ATTR1 field to analyzed to ensure fast queries.
What im doing wrong? I also tried to create the mapping after index creation but it leads to the same affect.
Ok i found the answer by my own. On the type level i had to wrap the "properties" with the type name. E.g:
"type1" : {
"properties" : {
.....
}
}
See the following code:
private XContentBuilder getMappingsByJson(){
XContentBuilder builder = null;
try {
builder = XContentFactory.jsonBuilder().startObject().startObject(type).startObject("properties");
for(int i = 1; i<5; i++){
builder.startObject("ATTR" + i)
.field("type", "integer")
.field("store", "yes")
.field("index", "analyzed")
.endObject();
}
builder.endObject().endObject().endObject();
}
catch (IOException e) {
e.printStackTrace();
}
return builder;
}
It creates mappings for the attributes ATTR1 - ATTR4. Now it is possible to define mapping for Example a list of different attributes dynamically. Hope it helps someone else.
Once i have parsed a JSON String into a GSON provided JsonObject class, (assume that i do not wish to parse it into any meaningful data objects, but strictly want to use JsonObject), how am i able to modify a field / value of a key directly?
I don't see an API that may help me.
https://static.javadoc.io/com.google.code.gson/gson/2.6.2/com/google/gson/JsonObject.html
Strangely, the answer is to keep adding back the property. I was half expecting a setter method. :S
System.out.println("Before: " + obj.get("DebugLogId")); // original "02352"
obj.addProperty("DebugLogId", "YYY");
System.out.println("After: " + obj.get("DebugLogId")); // now "YYY"
This works for modifying childkey value using JSONObject.
import used is
import org.json.JSONObject;
ex json:(convert json file to string while giving as input)
{
"parentkey1": "name",
"parentkey2": {
"childkey": "test"
},
}
Code
JSONObject jObject = new JSONObject(String jsoninputfileasstring);
jObject.getJSONObject("parentkey2").put("childkey","data1");
System.out.println(jObject);
output:
{
"parentkey1": "name",
"parentkey2": {
"childkey": "data1"
},
}
Since 2.3 version of Gson library the JsonArray class have a 'set' method.
Here's an simple example:
JsonArray array = new JsonArray();
array.add(new JsonPrimitive("Red"));
array.add(new JsonPrimitive("Green"));
array.add(new JsonPrimitive("Blue"));
array.remove(2);
array.set(0, new JsonPrimitive("Yelow"));
Another approach would be to deserialize into a java.util.Map, and then just modify the Java Map as wanted. This separates the Java-side data handling from the data transport mechanism (JSON), which is how I prefer to organize my code: using JSON for data transport, not as a replacement data structure.
It's actually all in the documentation.
JSONObject and JSONArray can both be used to replace the standard data structure.
To implement a setter simply call a remove(String name) before a put(String name, Object value).
Here's an simple example:
public class BasicDB {
private JSONObject jData = new JSONObject;
public BasicDB(String username, String tagline) {
try {
jData.put("username", username);
jData.put("tagline" , tagline);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public String getUsername () {
String ret = null;
try {
ret = jData.getString("username");
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return ret;
}
public void setUsername (String username) {
try {
jData.remove("username");
jData.put("username" , username);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public String getTagline () {
String ret = null;
try {
ret = jData.getString("tagline");
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return ret;
}
public static JSONObject convertFileToJSON(String fileName, String username, List<String> list)
throws FileNotFoundException, IOException, org.json.simple.parser.ParseException {
JSONObject json = new JSONObject();
String jsonStr = new String(Files.readAllBytes(Paths.get(fileName)));
json = new JSONObject(jsonStr);
System.out.println(json);
JSONArray jsonArray = json.getJSONArray("users");
JSONArray finalJsonArray = new JSONArray();
/**
* Get User form setNewUser method
*/
//finalJsonArray.put(setNewUserPreference());
boolean has = true;
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
finalJsonArray.put(jsonObject);
String username2 = jsonObject.getString("userName");
if (username2.equals(username)) {
has = true;
}
System.out.println("user name are :" + username2);
JSONObject jsonObject2 = jsonObject.getJSONObject("languages");
String eng = jsonObject2.getString("Eng");
String fin = jsonObject2.getString("Fin");
String ger = jsonObject2.getString("Ger");
jsonObject2.put("Eng", "ChangeEnglishValueCheckForLongValue");
System.out.println(" Eng : " + eng + " Fin " + fin + " ger : " + ger);
}
System.out.println("Final JSON Array \n" + json);
jsonArray.put(setNewUserPreference());
return json;
}
I have used httpclient to call a restapi written in django. It returned the json output. My httpresponse variable stored it and later convert the reponse to string and then to json object, i think its lengthy though it is working . I am really new to java , can anybody advise me , what is the best alternative logic to the code below
public void onClick(View v) {
// TODO Auto-generated method stub
HttpClient httpclient = new DefaultHttpClient();
HttpGet httppost = new HttpGet("http://10.0.2.2:8000/api/ca/entry/?
format=json&username=pragya");
try {
// Add your data
//List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
//nameValuePairs.add(new BasicNameValuePair("username", un.getText().toString()));
//nameValuePairs.add(new BasicNameValuePair("username", pw.getText().toString()));
//httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append((line + "\n"));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
JSONObject jsonObject = new JSONObject(sb.toString());
JSONObject meta = jsonObject.getJSONObject("meta");
String limit = meta.getString("limit");
Toast.makeText(HelloWorldActivity.this, limit, Toast.LENGTH_SHORT).show();
JSONArray array = jsonObject.getJSONArray("objects");
String key = array.getJSONObject(0).getString("api_key");
String uname = array.getJSONObject(0).getString("username");
Toast.makeText(HelloWorldActivity.this, uname + " " + key,
Toast.LENGTH_SHORT).show();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Toast.makeText(HelloWorldActivity.this, sb.toString(), Toast.LENGTH_SHORT).show();
} catch (ClientProtocolException e) {
Toast.makeText(HelloWorldActivity.this, e.toString(), Toast.LENGTH_SHORT).show();
// TODO Auto-generated catch block
} catch (IOException e) {
// TODO Auto-generated catch block
Toast.makeText(HelloWorldActivity.this, e.toString(), Toast.LENGTH_SHORT).show();
}
}
});
the json is as follows
{"meta": {"limit": 20, "next": null, "offset": 0, "previous": null, "total_count": 1}, "objects": [{"api_key": "c87391754b522d0c83b2c8b5e4c8cfd614559632deee70fdf1b48d470307e40e", "homeAddress": "kathmandu", "resource_uri": "/api/ca/entry/1/", "username": "sumit"}]}
Use Gson library from google, it is perfect for these kind of tasks.
All you need to do is define a new class that contains fields with the names of the keys in the json object and then use Gson to parse the Json string directly into the object or vice versa.
So for example:
Json looks like this: "limit": 20, "next": null, "offset": 0, "previous": null, "total_count": 1.
Java Class will be:
public class MyClass {
private int limit;
private int next;
private int offset;
private int previous;
private int total_count;
public int getLimit() {
return limit;
}
public void setLimit(int limit) {
this.limit = limit;
}
public int getNext() {
return next;
}
public void setNext(int next) {
this.next = next;
}
public int getOffset() {
return offset;
}
public void setOffset(int offset) {
this.offset = offset;
}
public int getPrevious() {
return previous;
}
public void setPrevious(int previous) {
this.previous = previous;
}
public int getTotal_count() {
return total_count;
}
public void setTotal_count(int total_count) {
this.total_count = total_count;
}
}
And use Gson code like that:
Gson gson = new Gson(); // Or use new GsonBuilder().create();
MyClass myClass = gson.fromJson(json, MyClass.class); // deserializes json into MyClass
Please note that the name of the class fields have to match exactly the name of the keys in the json string.
Always perform lengthy non-UI task using AsyncTask. All the operations you described, fetching of json and parsing them, can be performed in AsyncTask. Write the entire code which you have currently written in onClick event and write it doInBackground() of an AsyncTask.
Check the following for more details:
http://developer.android.com/reference/android/os/AsyncTask.html
I would still need some help with my dynamic selectlist.
I have the sript:
function getMains(element) {
var subjectgroup = element.value;
var select_element;
select_element = '#mains';
$(select_element).html('');
$(select_element).append($("<option></option>").attr("value","none").html(""));
// $(select_element).append($("<option></option>").attr("value","all").html(""));
if (element.value==''||element.value=='none'||element.value=='all')
return;
$.ajax({
type: 'GET',
url: 'getmainsubjects.html',
dataType: 'json',
data: ({id:data}),
success: function(data) {
$.each(function(data) {
if (!subjectgroup) {
$(select_element).append($(" <option>").attr("value",data.id,"items",data).html(data.description));
} else {
$(select_element).append($("<option>").attr("value",data.id,"items",data).html(data.description));
}
});
},
error: function(data) {
//alert("This failed!");
}
});
}
$('select#subjectgroups').ready(function(){
$("select#subjectgroups").find("option").each(function(i) {
if ($(this).val()!='all'&&$(this).val()!='none') {
$(this).append( " " + $(this).val() );
}
});
});
$('select#mains').ready(function(){
$("select#mains").find("option").each(function(i) {
if ($(this).val()!='all'&&$(this).val()!='none') {
$(this).append( " " + $(this).val() );
}
});
});
And the method:
#RequestMapping(method = RequestMethod.GET, params="id", value = "/getmainsubjects")
#ResponseBody
public String getMainSubjects( #RequestParam("id") int id) {
List<MainSubjectsSimple> mains = database.getMainSubjectsSimple(id, Localization.getLanguage());
//System.out.println(mains.size());
HashMap hm = new HashMap();
for (MainSubjectsSimple mss: mains) {
try {
hm.put("id",mss.getId());
hm.put("description", mss.getDescription());
} catch (NoSuchMessageException e) {
//hm.add(Integer.valueOf(mss.getId().toString(), translate(mss.getTranslationCode(),new Locale("fi")));
}
}
String json = null;
String _json = null;
try {
_json = HtmlEntityEncoder.encode(JsonUtils.javaToStr(hm));
} catch (Exception e) {
}
return _json;
}
I think I'm not looping the right values. Mains selectlist should be populated based on other selectlist so that the object's id is the value and description the label. Right now calling the url written in script returns only first object as json, not all of them, and the objects are not shown in mains selectlist.
You are putting the same keys over and over again to the Map hm:
HashMap hm = new HashMap();
for (MainSubjectsSimple mss: mains) {
try {
hm.put("id",mss.getId());
hm.put("description", mss.getDescription());
} catch (NoSuchMessageException e) {
//hm.add(Integer.valueOf(mss.getId().toString(),
translate(mss.getTranslationCode(),new Locale("fi")));
}
}
You need to use different keys for each entry in mains or use a collection (e.g. ArrayList) of Maps. An example of the latter:
List hms = new ArrayList();
for (MainSubjectsSimple mss: mains) {
try {
HashMap hm = new HashMap();
hm.put("id",mss.getId());
hm.put("description", mss.getDescription());
hms.add(hm);
} catch (NoSuchMessageException e) {
//hm.add(Integer.valueOf(mss.getId().toString(), translate(mss.getTranslationCode(),new Locale("fi")));
}
}
...
try {
_json = HtmlEntityEncoder.encode(JsonUtils.javaToStr(hms));
} catch (Exception e) {
}
I'm not familiar with the utils (JsonUtils) you are using so this might not work directly but the principle is the same anyways.