Search code examples
javaandroidjsonandroid-file

Appending JSONObjects when writing to a file


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();
        }


    }

Solution

  • 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 :)