从JSON文件中读取多个元素

5

我有一个包含许多这样元素的json文件:

{ 
"code" : "hfuiew89", 
"type" : "location", 
"coordinates" : [ { "lat" : 40.9861, "lon" : 29.1046, "index" : 1 }, 
          { "lat" : 40.9976, "lon" : 29.1153, "index" : 2 }, 
          { "lat" : 40.9809, "lon" : 29.2194, "index" : 3 }] 
}
{ 
"code" : "klsdsjh", 
"type" : "location", 
"relatedTags" : [ "kolmha" ], 
"coordinates" : [ { "lat" : 40.9808, "lon" : 29.1605, "index" : 1 }, 
              { "lat" : 40.9965, "lon" : 29.1672, "index" : 2 }] 
}

我想使用Gson读取该文件,但我找到的所有示例都只适用于一个元素。因此,在读取第一个元素后,会抛出“Expected EOF”异常。我该如何克服这个问题?


2
JSON本意是作为一个单一实体(可以是对象或数组) - 而你所拥有的是多个对象。在这种情况下,你真正想要的是在顶层有一个数组,每个对象都是其中的一个元素。你能够影响生成这个JSON吗,还是你被困在这个格式中了? - Greg Kopff
2个回答

10

就我个人而言...

以下陈述是不正确的。Gson没有内置功能来简单处理此类JSON序列的反序列化。(请参见评论。)

如果可以切换JSON-to/from-Java APIs,则Jackson具有此功能,如下所示。

input.json

{
"name":"A"
}
{
"name":"B"
}

JacksonFoo.java

:JacksonFoo.java 文件名。
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
import static com.fasterxml.jackson.annotation.PropertyAccessor.FIELD;

import java.io.File;
import java.util.Iterator;

import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonFoo
{
  public static void main(String[] args) throws Exception
  {
    ObjectMapper mapper = new ObjectMapper().setVisibility(FIELD, ANY);
    Iterator<Thing> thingsIterator = mapper.reader(Thing.class).readValues(new File("input.json"));
    while (thingsIterator.hasNext())
    {
      System.out.println(thingsIterator.next());
    }
  }
}

class Thing
{
  private String name;

  @Override
  public String toString()
  {
    return String.format("Thing: name=%s", name);
  }
}

输出:

Thing: name=A
Thing: name=B

更新:使用Gson实现类似的解决方案。

GsonFoo.java

import java.io.FileReader;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonStreamParser;

public class GsonFoo
{
  public static void main(String[] args) throws Exception
  {
    Gson gson = new GsonBuilder().create();

    JsonStreamParser parser = new JsonStreamParser(new FileReader("input.json"));
    while(parser.hasNext())
    {
      System.out.println(gson.fromJson(parser.next(), Thing.class));
    }
  }
}

2
这篇文章是不正确的。Gson确实有这个(误导性的)功能。它被称为JsonStreamParser,文档在这里:http://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/JsonStreamParser.html - Jesse Wilson
啊,好的。GSON API中使用“Stream”这个词让我有些困惑。我一直以为“Stream”是指逐个解析JSON流令牌,而不是将JSON数据绑定到对象/数组。 - Programmer Bruce
这正是我一直在寻找的。当您想要高效地将多个JSON节点写入文件时,它非常有用:您只需附加新元素,而不是解析数组,附加元素并将整个内容重新写回磁盘。 - Aron Lorincz

3

Greg说得对,这不是正确的JSON格式,你应该尝试生成有效的JSON,即在开头加上"[",在结尾处加上"]",并用逗号(",")分隔每个元素,以便它成为JSON对象的JSON数组。

但是,如果你无法更改现有的格式,请将它视为“包含连接的格式良好的JSON片段的字符串”。以这种方式处理它,将大字符串分解为较小的有效json字符串,并逐个解析它们。

要将大字符串分解为单个片段,您可以简单地计数括号。使用“预解析器”将东西复制到缓冲区(StringBuilder?),每次遇到“{”时增加计数器,每次遇到“}”时减少计数器,如果计数器为零,则将缓冲区字符串传递给Gson进行解析,清除缓冲区并继续到文件结束。

您甚至可以使用该预解析器将其转换为有效的JSON,当计数器达到零时,只需附加“,”,然后将所有内容传递给Gson进行单个解析,但这可能意味着将所有内容加载到内存中,我不知道你的文件有多大。


顺着这个思路,你可以将文件创建为JSON对象数组。然而,如果数组中的实体太多,可能会遇到内存问题。否则,分离的JSON实体字符串更好,可以让你一次只加载文件的一部分。 - Drizzt321
是的,这就是我想表达的:一个单文件大json数组,以及一个单独的解析调用可能看起来更容易,但也可能意味着加载所有内容到内存中;而逐步进行则可以让你一次处理一个实体... 这又是SAX与DOM之争,但现在它被称为JSON,听起来更酷了。 - Simone Gianni
哈哈,说得太对了。虽然JSON看起来不那么冗长(例如没有闭合标签),而且它还提供了一种非常简单的方式将数据传递给Javascript,这是当今最流行的技术之一,也可能是JSON大幅上升的原因之一。 - Drizzt321
是的,没有闭合标签..只要您不必手动编辑JSON文件或具有非常简单的结构,这非常好,但是当最后几行是10个闭合括号时怎么办? :D 如果我们需要的只是比XML更好的格式进行机器对机器通信,那么JSON甚至对于该任务来说也太冗长了..但我认为我们正在离题 :D - Simone Gianni
同意。反正都是关于Avro或Thrift的:P - Drizzt321
我一开始就想到了分离的解决方案,但我想知道是否有更方便的方法。无论如何,现在更清楚了,谢谢。 - eblek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接