Android-читатель JSON - IllegalStateException; ожидаемое имя, но было строкой

Я пытаюсь использовать Json Reader для анализа файла .json для вставки записей в мою базу данных, но у меня возникают проблемы с чтением файла.

Я намеренно открыл методы для синтаксического анализа/чтения с предложением try/catch, чтобы я мог видеть, из-за чего возникла ошибка.

Вот класс json Parser/Reader:

package cybertech.productions.servicehelpers;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;

import android.content.Context;

import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;

public class JsonParser {

    public void readCardJsonStream1(String cardSet, CarddbAdapter yugiohDB,
            Context context) throws IOException {

        URL test = new URL(cardSet);
        InputStream in = new BufferedInputStream(test.openStream());

        JsonReader reader = new JsonReader(new InputStreamReader(in,
                "ISO-8859-1"));

        String s, s1, s2, levelstr, rankstr, atkstr, defstr, setnumstr, cardpasstr;

        int numTotalElements = 0;
        int elementsParsed = 0;

        reader.beginObject();
        s = reader.nextName();

        reader.beginObject();
        while (reader.hasNext()) {

            s = reader.nextName();
            if (s.equalsIgnoreCase("p")) { // cards

                reader.beginObject();
                while (reader.hasNext()) {
                    s1 = reader.nextName();
                    if (s1.equalsIgnoreCase("o")) { // card
                        YugiohCard c;
                        reader.beginArray();
                        while (reader.hasNext()) {

                            reader.beginObject();
                            c = new YugiohCard();
                            while (reader.hasNext()) {
                                s2 = reader.nextName();
                                if (s2.equalsIgnoreCase("a")) { // name
                                    c.name = reader.nextString();
                                } else if (s2.equalsIgnoreCase("b")) { // type
                                    c.type = reader.nextString();
                                } else if (s2.equalsIgnoreCase("c")) { // attribute_type
                                    c.attributeType = reader.nextString();
                                } else if (s2.equalsIgnoreCase("d")) { // summon_requirements
                                    c.summonRequirements = reader.nextString();
                                } else if (s2.equalsIgnoreCase("e")) { // description
                                    c.description = reader.nextString();
                                } else if (s2.equalsIgnoreCase("f")) { // spell
                                                                        // speed
                                    c.spellSpeed = reader.nextString();

                                } else if (s2.equalsIgnoreCase("g")) { // level_stars
                                    levelstr = reader.nextString();
                                    c.levelStars = Integer.parseInt(levelstr);
                                } else if (s2.equalsIgnoreCase("h")) { // rank_stars
                                    rankstr = reader.nextString();
                                    c.rankStars = Integer.parseInt(rankstr);
                                } else if (s2.equalsIgnoreCase("i")) { // atk_points
                                    atkstr = reader.nextString();
                                    c.atk_stat = Integer.parseInt(atkstr);
                                } else if (s2.equalsIgnoreCase("j")) { // def_points
                                    defstr = reader.nextString();
                                    c.def_stat = Integer.parseInt(defstr);
                                } else if (s2.equalsIgnoreCase("n")) { // set
                                                                        // initials
                                    c.setinit = reader.nextString();
                                } else if (s2.equalsIgnoreCase("k")) { // set_number
                                    setnumstr = reader.nextString();
                                    c.setNumber = Integer.parseInt(setnumstr);
                                } else if (s2.equalsIgnoreCase("l")) { // card_number_pass
                                    cardpasstr = reader.nextString();
                                    c.cardnumberPass = Integer
                                            .parseInt(cardpasstr);
                                }
                            }
                            yugiohDB.createDBCard(c);
                            reader.endObject();
                        }
                        reader.endArray();
                    }
                }
                reader.endObject();
            }
            if (s.equalsIgnoreCase("w")) { // num_cards
                numTotalElements = reader.nextInt();
            }
        }
        reader.endObject();
        reader.close();
    }
}

Это мой файл Json:

{
    "t": {
        "p": {
            "o": [
                {
                    "a": "Elemental HERO Absolute Zero",
                    "b": "Monster/Fusion/Effect",
                    "c": "WATER",
                    "d": "1 'HERO' monster + 1 WATER monster",
                    "e": "Blows up your field, sucks for you",
                    "f": "NULL",
                    "g": "8",
                    "h": "0",
                    "i": "2500",
                    "j": "2000",
                    "n": "YG04",
                    "k": "001",
                    "l": "40854197"
                },
                {
                    "a": "Elemental HERO Air Neos",
                    "b": "Monster/Fusion/Effect",
                    "c": "WIND",
                    "d": "Hummingbird + Neos",
                    "e": "Returns to extra at end phase",
                    "f": "NULL",
                    "g": "7",
                    "h": "0",
                    "i": "2500",
                    "j": "2000",
                    "n": "STON",
                    "k": "034",
                    "l": "11502550"
                },
                {
                    "a": "Cyber Jar",
                    "b": "Monster/Effect",
                    "c": "DARK",
                    "d": "NULL",
                    "e": "destroys all monsters on the field",
                    "f": "NULL",
                    "g": "3",
                    "h": "0",
                    "i": "900",
                    "j": "900",
                    "n": "MRL",
                    "k": "077",
                    "l": "34124316"
                },
                {
                    "a": "Wind-Up Carrier Zenmaity",
                    "b": "MACHINE/Xyz/Effect",
                    "c": "WATER",
                    "d": "2 Level 3 monsters",
                    "e": "Special summon a wind-up from your hand or deck.",
                    "f": "NULL",
                    "g": "0",
                    "h": "3",
                    "i": "1500",
                    "j": "1500",
                    "n": "ORCS",
                    "k": "044",
                    "l": "81122844"
                },
                {
                    "a": "BRAIN CONTROL",
                    "b": "Spell",
                    "c": "Spell",
                    "d": "NULL",
                    "e": "Pay 800 Life Points to target 1 face-up monster your opponent controls; take control of that target until the End Phase.",
                    "f": "Normal",
                    "g": "0",
                    "h": "0",
                    "i": "0",
                    "j": "0",
                    "n": "LCYW",
                    "k": "074",
                    "l": "87910978"
                },
                {
                    "a": "Ring of Destruction",
                    "b": "Trap",
                    "c": "Trap",
                    "d": "NULL",
                    "e": "Target 1 face-up monster on the field; destroy that target, and if you do, inflict damage to both players equal to that target's ATK.",
                    "f": "Normal",
                    "g": "0",
                    "h": "0",
                    "i": "0",
                    "j": "0",
                    "n": "YG04",
                    "k": "001",
                    "l": "40854197"
                }
            ]
        },
        "v": "2/3/2013"
    }
}

Вот метод, который я создал, чтобы узнать, где находится файл json на моем сайте:

public void CreateYUGIsets() throws MalformedURLException, IOException{
        yugiohDB.dropDB();
        JsonParser parser = new JsonParser();
        String cardSetList = "https://sites.google.com/site/yugiohlibrary/patches/test.json";
        parser.readCardJsonStream1(cardSetList, yugiohDB, this);
    }

Вот ошибка Logcat, которую я тоже получаю:

05-15 22:48:49.362: E/AndroidRuntime(476): FATAL EXCEPTION: main
05-15 22:48:49.362: E/AndroidRuntime(476): java.lang.RuntimeException: Unable to start activity ComponentInfo{cybertech.productions.yugiohlibrary/cybertech.productions.yugiohlibrary.LoadingScreen}: java.lang.IllegalStateException: Expected a name but was STRING
05-15 22:48:49.362: E/AndroidRuntime(476):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1647)
05-15 22:48:49.362: E/AndroidRuntime(476):  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
05-15 22:48:49.362: E/AndroidRuntime(476):  at android.app.ActivityThread.access$1500(ActivityThread.java:117)
05-15 22:48:49.362: E/AndroidRuntime(476):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
05-15 22:48:49.362: E/AndroidRuntime(476):  at android.os.Handler.dispatchMessage(Handler.java:99)
05-15 22:48:49.362: E/AndroidRuntime(476):  at android.os.Looper.loop(Looper.java:123)
05-15 22:48:49.362: E/AndroidRuntime(476):  at android.app.ActivityThread.main(ActivityThread.java:3683)
05-15 22:48:49.362: E/AndroidRuntime(476):  at java.lang.reflect.Method.invokeNative(Native Method)
05-15 22:48:49.362: E/AndroidRuntime(476):  at java.lang.reflect.Method.invoke(Method.java:507)
05-15 22:48:49.362: E/AndroidRuntime(476):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
05-15 22:48:49.362: E/AndroidRuntime(476):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
05-15 22:48:49.362: E/AndroidRuntime(476):  at dalvik.system.NativeStart.main(Native Method)
05-15 22:48:49.362: E/AndroidRuntime(476): Caused by: java.lang.IllegalStateException: Expected a name but was STRING
05-15 22:48:49.362: E/AndroidRuntime(476):  at com.google.gson.stream.JsonReader.nextName(JsonReader.java:444)
05-15 22:48:49.362: E/AndroidRuntime(476):  at cybertech.productions.servicehelpers.JsonParser.readCardJsonStream1(JsonParser.java:36)
05-15 22:48:49.362: E/AndroidRuntime(476):  at cybertech.productions.yugiohlibrary.LoadingScreen.CreateYUGIsets(LoadingScreen.java:107)
05-15 22:48:49.362: E/AndroidRuntime(476):  at cybertech.productions.yugiohlibrary.LoadingScreen.onCreate(LoadingScreen.java:41)
05-15 22:48:49.362: E/AndroidRuntime(476):  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
05-15 22:48:49.362: E/AndroidRuntime(476):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
05-15 22:48:49.362: E/AndroidRuntime(476):  ... 11 more

Для этой конкретной строки в логарифме: Cybertech.productions.servicehelpers.JsonParser.readCardJsonStream1(JsonParser.java:36) 05-15 22:48:49.362: E/AndroidRuntime(476)

Строка 36 в классе JsonParser: s = reader.nextName();

Эта строка находится сразу после начала первого цикла while().

Любая помощь будет оценена по достоинству.


person Matthew White    schedule 15.05.2013    source источник


Ответы (1)


Вы выбрали самый утомительный метод анализа вашего файла JSON. Используемая вами библиотека (Gson) предоставляет гораздо более простые способы обработки ввода. Вот пример, который все еще выполняет ручной разбор:

// Your JSON data here
final String json = "";

// Get the array element corresponding to t->p->o, in the data
final JsonElement rootElem = new JsonParser().parse(json);
final JsonArray jsonArr = rootElem.getAsJsonObject()
        .getAsJsonObject("t").getAsJsonObject("p").getAsJsonArray("o");

// Iterate through the JSON array, extracting out card info as we go
final List<Yugioh> cards = new ArrayList<Yugioh>(jsonArr.size());
for (final JsonElement objElem : jsonArr) {
    final JsonObject jsonObj = objElem.getAsJsonObject();
    final Yugioh card = new Yugioh();

    card.setName(jsonObj.get("a").getAsString());
    card.setType(jsonObj.get("b").getAsString());
    card.setAttributeType(jsonObj.get("c").getAsString());
    card.setSummonRequirements(jsonObj.get("d").getAsString());
    card.setDescription(jsonObj.get("e").getAsString());
    card.setSpellSpeed(jsonObj.get("f").getAsString());
    card.setLevelStars(Integer.parseInt(jsonObj.get("g").getAsString()));
    card.setRankStars(Integer.parseInt(jsonObj.get("h").getAsString()));
    card.setAtkStat(Integer.parseInt(jsonObj.get("i").getAsString()));
    card.setDefStat(Integer.parseInt(jsonObj.get("j").getAsString()));
    card.setNumber(Integer.parseInt(jsonObj.get("k").getAsString()));
    card.setCardNumberPass(Integer.parseInt(jsonObj.get("l")
            .getAsString()));
    card.setInit(jsonObj.get("n").getAsString());

    System.out.println(card);
    cards.add(card);
}

Обратите внимание, насколько читабельнее стал код. Я позволил себе проиллюстрировать правильное использование геттеров/сеттеров и исправил некоторые имена переменных. Вы можете еще больше сократить приведенный выше пример, указав в своем классе преобразователя сериализованные имена атрибутов JSON, из которых вы копируете. Например, с этим классом картографа:

class Yugioh implements Serializable {
    private static final long serialVersionUID = -8125568453376399843L;

    @SerializedName("a")
    private String name;

    @SerializedName("b")
    private String type;

    @SerializedName("c")
    private String attributeType;

    @SerializedName("d")
    private String summonRequirements;

    @SerializedName("e")
    private String description;

    @SerializedName("f")
    private String spellSpeed;

    @SerializedName("g")
    private Integer levelStars;

    @SerializedName("h")
    private Integer rankStars;

    @SerializedName("i")
    private Integer atkStat;

    @SerializedName("j")
    private Integer defStat;

    @SerializedName("k")
    private Integer number;

    @SerializedName("l")
    private Integer cardNumberPass;

    @SerializedName("n")
    private String init;

    public Yugioh() {
        super();
    }

    // Getters/Setters here

    @Override
    public String toString() {
        return String.format("Card %s", getName());
    }
}

Вы можете значительно сократить код десериализации:

// Your JSON data here
final String json = "";

// Deserialize JSON into a bunch of objects
final JsonElement rootElem = new JsonParser().parse(json);
final JsonArray jsonArr = rootElem.getAsJsonObject()
        .getAsJsonObject("t").getAsJsonObject("p").getAsJsonArray("o");
final List<Yugioh> cards = new Gson().fromJson(jsonArr,
        new TypeToken<List<Yugioh>>() {
        }.getType());

System.out.println(cards);

Мертвая простота. Обязательно прочтите Руководство пользователя Gson — в нем содержится масса полезной информация об использовании библиотеки.

person Perception    schedule 16.05.2013
comment
Большое спасибо за информацию и примеры, обязательно попробую. - person Matthew White; 16.05.2013
comment
@Perception Как отображать карточки List‹Yugioh› в представлении списка, прежде чем я буду использовать ручную логику для анализа JSON с библиотекой GSON, после реализации вашего кода я могу извлекать данные с помощью SOP в представлении списка, не загружая данные! ! может объяснить, как заполнять карточки List‹Yugioh› в виде списка (Yugioh Serializable!) - person LOG_TAG; 09.08.2013
comment
@Perception Я реализовал ваше решение для этого ручного синтаксического анализа, но представление списка перестало заполняться! Мне нужно десериализовать карты List‹Yugioh›? stackoverflow.com/questions/18140830/ - person LOG_TAG; 09.08.2013
comment
Это не правильный ответ на вопрос. Вопрос касается JsonReader (GSON), а не парсера JSON, который предоставляет Android. Есть веская причина использовать JsonReader от GSON: он позволяет записывать или читать потоки. Это особенно интересно, если синтаксический анализ большого JSON, поскольку Android имеет ограниченную память, и потоковый режим рекомендуется вместо загрузки строк в памяти (синтаксический анализ, подобный DOM, который выполняют JSONObject и JSONArray) - person diegosasw; 07.10.2014