0

DB から Java (Android) アプリケーションに取得した JSON を解析する際に問題が発生しています。あなたが私を助けてくれることを願っています:

これは私が持っているJsonです:

<br>
[{<br>
  "ID" : "1",<br>
  "name" : "Test name",<br>
  "type" : "1",<br>
  "Desc" : "blablabla",<br>
  "minNum" : "0",<br>
  "maxNum" : "12",<br>
  "Num" : "8",<br>
  "bool1" : "0",<br>
  "bool2" : "1",<br>
  "bool3" : "1",<br>
  "date" : "2012-04-01 23:00:00",<br>
  "double1" : "39.47208",<br>
  "doubl2" : "-0.3556063",<br>
  "someText" : "ajayeah",<br>
  "number" : "15",<br>
  "anotherNumber" : "1234"<br>
}, {"ID" : "2",<br>
  "name" : "Test name",<br>
  "type" : "1",<br>
  "Desc" : "blablabla",<br>
  "minNum" : "0",<br>
  "maxNum" : "12",<br>
  "Num" : "8",<br>
  "bool1" : "0",<br>
  "bool2" : "1",<br>
  "bool3" : "1",<br>
  "date" : "2012-04-01 23:00:00",<br>
  "double1" : "39.47208",<br>
  "doubl2" : "-0.3556063",<br>
  "someText" : "ajayeah",<br>
  "number" : "15",<br>
  "anotherNumber" : "1234"<br>
}]<br>

(名前は明らかに私がここに書いたものではありません:P)

たとえば、responseString; という変数に、すべてを文字列 (チェック済み、OK) として持っています。

そして、変換を行うために可能なすべての方法を試しましたが、常に失敗します。'[' ']' を削除するオブジェクトとして、配列として取得しようとしました (私が達成したことですが、この行は失敗します):

MyClassList MyClas = new Gson().fromJson(responseString, MyClassList.class);

エラーは次のとおりです。

com.google.gson.JsonSyntaxException: 
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY 

(最初にオブジェクトまたは配列に変換する他のテストでは、エラーはBEGIN_STRINGにあります)

ブール値とカレンダー型 (Date) を使用していて、Gson ライブラリでサポートされていないためでしょうか?

ええと、私は怒っているので、あなたがここから出てくれることを願っています。

ありがとう :)


カレンダーを使用しないでください。それが私の問題でした。代わりに、Date を使用すると機能します。また、理由はわかりませんが、"0"|"1" のようなブール値は機能しません。「true」|「false」のようにはい。

4

3 に答える 3

2

最後に、これが私の 3 番目の回答であることは承知していますが、ご覧のとおり、カレンダーが原因で問題が非常に複雑になり、複数の回答が存在します。

GSON を使用しても、そのままでは機能しません。前述の理由から、カレンダーをやめて JODA に移行することを強くお勧めします。

あなたが扱っている問題は、日付のシリアル化の問題です。幸いなことに、GSON では、この目的のためにカスタム シリアライザーを登録できます (独自のパーサーを使用しないもう 1 つの理由)。カレンダー用に作成するか、既に作成されているものを探すことができます。

ありがたいことに、これは JODA DateTimes に対して既に行われており、独自に記述したい人のために設計図を提供しています。

ここを参照してください: https://sites.google.com/site/gson/gson-type-adapters-for-common-classes

私はそれを次のように実装しました:

package com.techtrip.test;

import java.lang.reflect.Type;

import org.joda.time.DateTime;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

public class DateTimeTypeConverter  implements JsonSerializer<DateTime>, JsonDeserializer<DateTime>  {
      // No need for an InstanceCreator since DateTime provides a no-args constructor
      @Override
      public JsonElement serialize(DateTime src, Type srcType, JsonSerializationContext context) {
        return new JsonPrimitive(src.toString());
      }
      @Override
      public DateTime deserialize(JsonElement json, Type type, JsonDeserializationContext context)
          throws JsonParseException {
        return new DateTime(json.getAsString());
      }
}

完璧です。GSON ビルダーに登録し、ターゲット クラス (この場合は DateTime.class) の代わりとしてドロップするだけです。したがって、これにより、このクラスを使用して各 DateTime がシリアル化されます。本当に素晴らしい。

使用方法は次のとおりです。

public static void main(String[] args) {
    // TODO Auto-generated method stub

    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(DateTime.class, new DateTimeTypeConverter());

    Gson gson = gsonBuilder.create();
    String jsonStr = gson.toJson(target);

    logger.info(String.format("Target As String\n: %s", jsonStr));

    // This will work as well --> ToSerialize test[] =
    // gson.fromJson(jsonStr, target.getClass());
    ToSerialize test[] = gson.fromJson(jsonStr, ToSerialize[].class);

    for (ToSerialize deserialized : test) {
        logger.info(String.format("From JSON\n: %s",
                deserialized.toString()));
    }

    String testString = "[{\"ID\" : \"1\",\"name\" : \"Test name\",\"type\" : \"1\",\"Desc\" : \"blablabla\",\"minNum\" : \"0\",\"maxNum\" : \"12\",\"Num\" : \"8\",\"bool1\" : \"0\",\"bool2\" : \"1\",\"bool3\" : \"1\",\"date\" : \"2012-04-08T07:50:01.600-05:00\",\"double1\" : \"39.47208\",\"doubl2\" : \"-0.3556063\",\"someText\" : \"ajayeah\",\"number\" : \"15\",\"anotherNumber\" : \"1234\"}, {\"ID\" : \"2\",\"name\" : \"Test name\",\"type\" : \"1\",\"Desc\" : \"blablabla\",\"minNum\" : \"0\",\"maxNum\" : \"12\",\"Num\" : \"8\",\"bool1\" : \"0\",\"bool2\" : \"1\",\"bool3\" : \"1\",\"date\" : \"2012-04-08T07:50:01.600-05:00\",\"double1\" : \"39.47208\",\"doubl2\" : \"-0.3556063\",\"someText\" : \"ajayeah\",\"number\" : \"15\",\"anotherNumber\" : \"1234\"}]";

    ToSerialize test2[] = gson.fromJson(testString, ToSerialize[].class);

    for (ToSerialize deserialized : test2) {
        logger.info(String.format("From JSON\n: %s",
                deserialized.toString()));
    }
}

いくつかの注意点があり、これを理解する必要があります。最初に、 などの完全修飾日時形式を含める必要があります2012-04-08T07:50:01.600-05:00

DateTimeFormatter次に、これらを印刷するには、JODA が提供する多くのようなものが必要です。必要に応じて、JODA からカレンダーを入手することもできます。

JavaのCalendarでもこれを行うことができると確信しています。誰かがすでにそれに取り組んでいる可能性があります。幸運を!

于 2012-04-08T13:05:56.557 に答える
1

問題は、配列を配列ではないクラスに逆シリアル化しようとしていて、明らかな理由で機能しないことです。配列型がわかっているので、MyClass[] の任意のインスタンスを使用してクラスを取得します。いつでも作成できます。交互に渡すMyClass[].class(つまりMyClass.class、 と同じではないMyClass[].class)

以下は、log4j.properties ファイルを作成した場合 (またはロガーをシステム出力に変換した場合) にそのまま実行される例です。

package com.techtrip.test;

import java.io.Serializable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;

public class GsonTest {

    private static Logger logger = LoggerFactory.getLogger(GsonTest.class);

    static ToSerialize t1 = new ToSerialize("1", "Test 1");
    static ToSerialize t2 = new ToSerialize("2", "Test 2");

    static ToSerialize target[] = {t1,t2} ;


    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Gson gson = new Gson(); 

        String jsonStr = gson.toJson(target);

        logger.info(String.format("Target As String\n: %s", jsonStr));

        // This will work as well --> ToSerialize test[] = gson.fromJson(jsonStr, target.getClass());
        ToSerialize test[] = gson.fromJson(jsonStr, ToSerialize[].class);

        for (ToSerialize deserialized: test){
            logger.info(String.format("From JSON\n: %s", deserialized.toString()));
        }
    }

}

class ToSerialize implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;


    private String iD;
    private String name;


    public ToSerialize() {
        // TODO Auto-generated constructor stub
    }

    public ToSerialize(String iD, String name) {
        super();
        this.iD = iD;
        this.name = name;
    }

    public String getiD() {
        return iD;
    }
    public void setiD(String iD) {
        this.iD = iD;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((iD == null) ? 0 : iD.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        ToSerialize other = (ToSerialize) obj;
        if (iD == null) {
            if (other.iD != null)
                return false;
        } else if (!iD.equals(other.iD))
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "ToSerialize [iD=" + iD + ", name=" + name + "]";
    }
}
于 2012-04-07T15:04:52.967 に答える
0

更新: 以下をコメントとして追加したかったのですが、長すぎます。わかりました、私はこれの原因を知っていると思います。例外を見ると、"Expected BEGIN_OBJECT but was BEGIN_ARRAY"これは Gson が次の値を取得するためにピークするときに 2 つの方法のいずれかで発生します。BEGIN_OBJECT は で表され{、BEGIN_ARRAYは で表され[ます。

何らかの理由で、パーサーは JSON 文字列を次のように開始するかのように処理しています。"[[{\"ID\"

これは、JavaScript や、既に JSON 形式になっている場合に、JSON としてエスケープしようとして余分な括弧を追加するものを介して入ってくる場合に発生する可能性があります。

案の定、文字列をそのように設定するように変更すると、次のようになります。

Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 3

例外全体を投稿したわけではありませんが、このような変換が発生するのを見てきました。パーサーが問題ではないのに、独自の JSON パーサーを作成するのではないかと心配しています。

================以下の元の回答===================

上記の元のクラスを変更して、JSON 文字列をミラーリングしたところ、問題なく動作しました。私は Android を使用していません。おそらく、その環境で特に問題を引き起こしている何かを見つけているのでしょう。日付が問題だった場合は、JSON 例外として解析不能な日付を取得します。これは、失敗する"2012-04-01 23:00:00"が失敗Apr 7, 2012 3:07:40 PMしない私のセットアップの場合です。

これがどのように見えるかです:

    String testString = "[{\"ID\" : \"1\",\"name\" : \"Test name\",\"type\" : \"1\",\"Desc\" : \"blablabla\",\"minNum\" : \"0\",\"maxNum\" : \"12\",\"Num\" : \"8\",\"bool1\" : \"0\",\"bool2\" : \"1\",\"bool3\" : \"1\",\"date\" : \"Apr 7, 2012 3:07:40 PM\",\"double1\" : \"39.47208\",\"doubl2\" : \"-0.3556063\",\"someText\" : \"ajayeah\",\"number\" : \"15\",\"anotherNumber\" : \"1234\"}, {\"ID\" : \"2\",\"name\" : \"Test name\",\"type\" : \"1\",\"Desc\" : \"blablabla\",\"minNum\" : \"0\",\"maxNum\" : \"12\",\"Num\" : \"8\",\"bool1\" : \"0\",\"bool2\" : \"1\",\"bool3\" : \"1\",\"date\" : \"Apr 7, 2012 3:07:40 PM\",\"double1\" : \"39.47208\",\"doubl2\" : \"-0.3556063\",\"someText\" : \"ajayeah\",\"number\" : \"15\",\"anotherNumber\" : \"1234\"}]";

    ToSerialize test2[] = gson.fromJson(testString, ToSerialize[].class);

    for (ToSerialize deserialized : test2) {
        logger.info(String.format("From JSON\n: %s",
                deserialized.toString()));
    }

出力は次のとおりです。

ToSerialize [ID=2, name=Test name, Desc=blablabla, minNum=0, maxNum=12, Num=8, bool1=false, bool2=false, bool3=false, date=Sat Apr 07 15:07:40 CDT 2012, double1=39.47208, doubl2=-0.3556063, someText=ajayeah, number=15, anotherNumber=1234]

そして、これがクラス定義です(要約)私はニヤリと笑うためにブール値を入れました)

class ToSerialize {
    int ID;
    String name;
    String Desc;
    int minNum;
    int maxNum;
    int Num;
    Boolean bool1;
    boolean bool2;
    boolean bool3;
    Date date;
    double double1;
    double doubl2;
    String someText;
    int number;
    int anotherNumber;
    ......

すべてのアカウントで、これは機能するはずです。

GSON の最新の 2.1 バージョンを使用しています。これはうまくいくはずなので、どこかのバグである可能性があります。

于 2012-04-07T20:18:13.517 に答える