0

我在解析从我的数据库获得的 JSON 到我的 Java (Android) 应用程序时遇到了一些问题。我希望你能帮助我:

这是我拥有的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)

我将所有这些作为字符串(选中,OK),例如在一个名为 responseString 的变量中;

然后我尝试了所有可能的方法来进行转换,但总是失败。我试图将它作为一个对象删除'['']',作为一个数组(我实现的东西,但是这条线失败了):

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

错误是:

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

(其他测试首先将其转换为对象或数组,错误在于 BEGIN_STRING)

可能是因为我使用的是布尔值和日历类型(在日期中)并且 Gson 库不支持它吗?

好吧,我希望你能让我离开这里,因为我要疯了。

谢谢 :)


不要使用日历,那是我的问题。相反,您可以使用 Date 并且它可以工作。另外,我不知道为什么,像 "0"|"1" 这样的布尔值不起作用。像“真”|“假”是的。

4

3 回答 3

2

最后,我知道这是我的第三个答案,但是正如您所见,由于日历存在多个答案,因此问题非常复杂。

使用 GSON 不会开箱即用。出于前面所述的原因,我强烈建议放弃日历并转向 JODA。

您正在处理的问题是日期的序列化问题。幸运的是,GSON 允许您为此目的注册自定义序列化程序(不使用您自己的解析器的另一个原因)。您可以为日历编写一个或查找已经编写的一个。

值得庆幸的是,这已经为 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 的日历也可以做到这一点。可能有人已经解决了它。祝你好运!

于 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 试图获得下一个值。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;
    ......

无论如何,这应该有效。

我正在使用最新的 2.1 版本的 GSON。这可能是某个地方的错误,因为它应该对您有用。

于 2012-04-07T20:18:13.517 回答