簡體   English   中英

Gson 在嘗試解析 List 時拋出 IllegalStateException

[英]Gson throws IllegalStateException when attempting to parse List

我正在學習數據持久性,這是我第一次嘗試 JSON。 我已經閱讀了一些指南,從我可以看出的代碼在存儲對象的兩次嘗試中都是正確的。 我得到了使用 Gson 編寫的文件,但是當嘗試使用 fromJson() 方法解析對象時,Gson 拋出異常。 我的問題如下:

  • 如果我使用相同的類型來轉換為/從 JSON,我缺少什么會告訴 Gson 如何正確解析我的對象?

我嘗試了三種不同的方法,其中兩種包括在下面。 首先,我嘗試存儲指南建議我應該能夠執行的對象列表的包裝類:

public class JSONConverter {
    private static Path path = Paths.get("src\\json\\JSONList.json");
    private static Type stockType = new TypeToken<StocksList>(){}.getType();

    public static void convertToJSON(StocksList stocks, Path path) {
        Gson json = new Gson();     
        String storedStocks = json.toJson(stocks, stockType);// I also tried "StocksList.class" here            
        checkForFile(path);
        try (BufferedWriter writer = Files.newBufferedWriter(path)) {
            writer.write(storedStocks);
        } catch (IOException e) {
            e.printStackTrace();
            //handle later
        }
    }

    static void checkForFile(Path path) {
        if (Files.notExists(path)) {
            try {
                Files.createFile(path);
            } catch (IOException e) {
                e.printStackTrace();
                //handle later
            }
        }
    }

    public static StocksList convertFromJSON(Path path) {
        StocksList stocksList = new StocksList();
        Gson json = new Gson();
        String fromJson;
        try {
            fromJson = Files.readAllBytes(path).toString();
            stocksList = json.fromJson(fromJson, stockType);
            return stocksList;
        } catch (IOException e) {
            return stocksList;
        }
    }
}

我的第二種方法是從包裝類中取出列表並嘗試將其轉換為 JSON:

public class JSONConverter {
    private static Path path = Paths.get("src\\json\\JSONList.json");
    private static Type listType = new TypeToken<List<Stock>>(){}.getType();

    public static void convertToJSON(StocksList stocks, Path path) {
        Gson json = new Gson();     
        List<Stock> temp = stocks.getStocks();
        String storedStocks = json.toJson(temp, listType);
        checkForFile(path);
        try (BufferedWriter writer = Files.newBufferedWriter(path)) {
            writer.write(storedStocks);
        } catch (IOException e) {
            e.printStackTrace();
            //handle later
        }
    }

    static void checkForFile(Path path) {
        if (Files.notExists(path)) {
            try {
                Files.createFile(path);
            } catch (IOException e) {
                e.printStackTrace();
                //handle later
            }
        }
    }


    public static StocksList convertFromJSON(Path path) {
        StocksList stocksList = new StocksList();
        List<Stock> stocks = new ArrayList<>();
        Gson json = new Gson();
        String fromJson;
        try {
            fromJson = Files.readAllBytes(path).toString();
            stocks = json.fromJson(fromJson, listType);
            //wraps the list in the stockslist class
            stocksList.setStocks(stocks);
            return stocksList;
        } catch (IOException e) {
            e.printStackTrace();
            return stocksList;
        }
    }
}

以下是第一種方法使用第二種方法編寫的 JSON 示例。 第一個看起來像它除了添加“{“股票”:”(你在下面看到的)“}”:

[
{
    "ticker": "INTC",
    "currentPrice": "45.94",
    "marginOfSafety": 0.25,
    "lastDate": "2019-12-28",
    "cashYield": "7.4",
    "MCap": "196485365760",
    "enterpriseValue": "281213850000",
    "sharesOut": "4417000000",
    "oddPercentGrowth": false,
    "newCompany": false,
    "safeValue": "51.35",
    "fairValue": "68.47",
    "evEbitda": "8.56",
    "fcf": [
        "16932000000",
        "14611750000"
    ],
    "rOnAssets": "21",
    "rOnCapital": "20",
    "croic": "16.47",
    "equityToDebt": "3.0",
    "cashOnHand": "4194000000",
    "cashToDebt": "0.17",
    "changeInDebt": "210000000",
    "capEfficiency": [
        "18",
        "7",
        "-26",
        "-21",
        "1"
    ],
    "fcfChange": [
        "18.81",
        "11.71"
    ],
    "profitMargin": [
        "46",
        "38"
    ]
},
{
    "ticker": "HCC",
    "currentPrice": "12.99",
    "marginOfSafety": 0.5,
    "lastDate": "2018-12-31",
    "cashYield": "46.1",
    "MCap": "664587904",
    "enterpriseValue": "1572623480",
    "sharesOut": "52812000",
    "oddPercentGrowth": true,
    "newCompany": true,
    "safeValue": "236.94",
    "fairValue": "473.87",
    "evEbitda": "2.59",
    "fcf": [
        "457776000",
        "306126750"
    ],
    "rOnAssets": "49",
    "rOnCapital": "59",
    "croic": "38.77",
    "equityToDebt": "1.0",
    "cashOnHand": "205577000",
    "cashToDebt": "0.44",
    "changeInDebt": "125283000",
    "capEfficiency": [
        "292",
        "798",
        "-365",
        "-397",
        "-1"
    ],
    "fcfChange": [
        "33.9",
        "33.9"
    ],
    "profitMargin": [
        "40",
        "8"
    ]
}
]

兩者都拋出:

com.google.gson.JsonSyntaxException:java.lang.IllegalStateException:預期為 BEGIN_OBJECT,但在第 1 行第 12 列處為 STRING

(當使用第一種方法時,此行更改為“預期的 BEGIN_OBJECT 但在第 1 行第 2 列處為 BEGIN_ARRAY”)。

com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:176) ...

我打算嘗試將每個對象單獨添加到 JSONArray 中,但是當我開始在那里遇到麻煩時,我想我應該問一下。 該指南提到反射很重要,我猜測我的問題在於堆棧跟蹤的第二行,但同樣,這是我第一次嘗試使用 JSON。 如果我忘記包含任何內容,請告訴我,我會在評論中發布。

謝謝您的幫助。

附錄:對象僅在寫入文件和從文件中提取時才會拋出這些異常。 它們在轉換為 JSON 字符串然后再返回時不會拋出。 無論我使用 Files.write() 還是 Files.newBufferedWriter() 都會發生這種情況。

感謝所有看過我問題的人。 我聯系了 Gson 的 github 頁面,他們對我的班級做出了以下更正:

您提供的所有代碼都可以得到極大的修復、改進和重構。

無需創建多個 Gson 實例:實例化它們的成本相對較高,但被設計為線程安全且不可變的,因此可以重用。 不需要從 java.lang.String 序列化和反序列化——這只是昂貴的,因為它必須在堆中創建多個字符串,而這只是浪費堆和時間降低性能。 為什么它在您的情況下不起作用是 Files.readAllBytes(...) 返回 byte[] 您試圖轉換為字符串。 在 Java 中,沒有任何數組具有直觀的 toString 實現(您可以通過簡單地將任何字節數組打印到 System.out 來檢查它)。 為了將其轉換為字符串(這可能是一個消耗內存的實例),new String(byte[])(甚至 new String(byte[], Charset))是一種合適的方法。 我真的不記得文件是如何工作的,但可能不需要檢查文件是否存在:它們可以在沒有任何額外檢查的情況下被覆蓋。 在這種情況下不需要類型標記:StockList.class 也是一個類型。 基本上,您只需要如下:

private static final Gson gson = new GsonBuilder()
    .disableHtmlEscaping()
    .disableInnerClassSerialization()
    .create();

public static void main(final String... args)
        throws IOException {
    final StocksList before = new StocksList(ImmutableList.of(new Stock("INTC"), new 
 Stock("HCC")));
    final Path path = Paths.get("doc.json");
    write(path, before);
    final StocksList after = read(path);
    System.out.println(after.equals(before));
}

private static void write(final Path path, final StocksList stocks)
        throws IOException {
    try ( final Writer writer = new OutputStreamWriter(new 
FileOutputStream(path.toFile())) ) {
        gson.toJson(stocks, writer);
     }
}

 private static StocksList read(final Path path)
        throws IOException {
    try ( final Reader reader = new InputStreamReader(new 
 FileInputStream(path.toFile())) ) {
         return gson.fromJson(reader, StocksList.class);
    }
}

感謝 lyubomyr-shaydariv(Gson 貢獻者)的回答。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM