【问题标题】:Gson saving JSON to disk and loading back to memoryGson 将 JSON 保存到磁盘并加载回内存
【发布时间】:2019-07-22 07:25:27
【问题描述】:

我将我的 JSONObject 保存到磁盘,然后当我尝试将它加载回 JSONObject 时,我在 loadSavedScheduleFromDisk 方法的这一行得到以下异常:

JSONObject jsonObject = gson.fromJson(reader, JSONObject.class);

com.google.gson.JsonSyntaxException:com.google.gson.stream.MalformedJsonException:第 1 行第 74040 列路径 $.nameValuePairs..values[32].values[2].nameValuePairs.disciplineVersionId 的未终止字符串

这是我的代码:

private void loadSavedScheduleFromDisk(Activity act) {
    try {
        File file = new File(((Context)act).getCacheDir()+"/"+SCHED_CACHE_FILENAME);

        Gson gson = new Gson();
        InputStreamReader freader = new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8"));
        JsonReader reader = new JsonReader(freader);//new FileReader(file));
        reader.setLenient(true);
        JSONObject jsonObject = gson.fromJson(reader, JSONObject.class);
        parseSchedule(jsonObject);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

private void saveScheduleToDisk(Activity act, JSONObject jsonObject )
{
    try {
        File file = new File(((Context)act).getCacheDir()+"/"+SCHED_CACHE_FILENAME);

        //Writer writer = new FileWriter(file);
        Writer writer = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"));
        Gson gson = new GsonBuilder().create();
        gson.toJson(jsonObject, writer);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

它非常对称,我不明白为什么它不起作用 - 我想如果我使用 API 将我的 JSONObject 保存到磁盘并且保存正常,那么为什么我不能加载相同的数据呢?

【问题讨论】:

  • 一个更简单的选择是将其保存为共享首选项上的字符串,然后创建Json from string
  • 您的JSON 有效吗?您可以在此路径下找到什么:$.nameValuePairs..values[32].values[2].nameValuePairs.disciplineVersionId?
  • 我相信我的 JSON 是有效的,因为我已经在 saveScheduleToDisk() 方法中将它作为 JSONObject 保存在内存中。在保存到磁盘之前,我还从服务器加载了这个 JSON,一切正常。
  • 我没有尝试共享首选项,因为我在某处读到它用于保存小字符串。在我的情况下,这个 JSON 可以是 800Kb 甚至更多,但如果没有任何帮助,我会试一试
  • @Michal 我没有发现这些值有什么问题,我一解决这个问题就会在这里发布我的解决方案

标签: android json caching gson


【解决方案1】:

我认为问题在于您没有关闭 WRITER 或默认适配器更改了您的数据

该错误表明您的 JSON 关闭严重,可能您的对象未正确压缩或字符串转义不正确

这是我的提议,我的测试通过了我

我创建 JSONFileUtil 并使用 AutoCloseable 如果您需要关闭流,那么最佳做法是使用 try-with-resources 语句 link

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;

public class JSONFileUtil {

    public void save(File file, MyDTO myDto) {
        try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"))) {
            writer.write(new Gson().toJson(myDto));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public MyDTO load(File file) {
        try (InputStreamReader freader = new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8"))) {
            JsonReader reader = new JsonReader(freader);
            return new Gson().fromJson(reader, MyDTO.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

还有我的DTO

import java.util.Date;

public class MyDTO {

    public String valueString;
    public Long valueLong;
    public Date valueDate;
}

和我的测试

import java.io.File;
import java.util.Date;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;

public class JSONFileUtilTest {

    @Test
    public void saveAndLoad() {
        //given:
        MyDTO myDTO = new MyDTO();
        myDTO.valueString = "aaa";
        myDTO.valueLong = 4L;
        myDTO.valueDate = new Date(1551427491625L);
        File cacheFile = new File("cache" + File.separator + "name.json");
        //when:
        JSONFileUtil jsonFileUtil = new JSONFileUtil();
        jsonFileUtil.save(cacheFile, myDTO);
        MyDTO resultMyDto = jsonFileUtil.load(cacheFile);
        //then:
        assertEquals(myDTO.valueLong, resultMyDto.valueLong);  //4
        assertEquals(myDTO.valueString, resultMyDto.valueString);  // "aaa"
        assertEquals(myDTO.valueDate.toString(), resultMyDto.valueDate.toString());  // "Mar 1, 2019 9:04:51 AM" OK
        assertNotEquals(myDTO.valueDate.getTime(), resultMyDto.valueDate.getTime()); // it not 1551427491625L but 1551427491000L --> 1551427491625L because the default date format does not store milliseconds 
        //to store this you need to write your adapter

    }
}

【讨论】:

  • 感谢您的回答!是的,确实,问题是我没有关闭 WRITER。在我关闭它之后,我停止了这个异常。我没有尝试您的代码,但我可以看到您也没有关闭 WRITER...
  • @Ivan 使用 AutoCloseable 如果您需要关闭流,那么最佳实践是使用 try-with-resources 语句 link
猜你喜欢
  • 1970-01-01
  • 2012-05-13
  • 1970-01-01
  • 2018-08-03
  • 2010-11-29
  • 1970-01-01
  • 1970-01-01
  • 2012-11-02
  • 1970-01-01
相关资源
最近更新 更多