如何使用Java中的Streaming API解析JSON日志文件,然后输出列表日志文件

我有一个问题,我试图解析以JSON格式存储的大型日志文件,然后将数据列表并输出为另一个JSON文件。 以下是我正在解析的日志文件的格式:

{ "timestamp": "2012-10-01TO1:00:00.000", "id": "someone@somewhere.net", "action": "Some_Action" "responsecode": "1000" } 

此处的操作是某些用户执行的操作,响应代码是该操作的结果。

时间戳和id实际上与我的制表无关,我只对动作/代码字段感兴趣。 在任何给定的日志文件中可能有成千上万的这些条目,我想要做的是跟踪所有类型的操作响应代码及其各自的出现次数。

下面是我想要生成的输出示例。

 {"actionName": "Some_User_Action", "responses": [{"code": "1000", "count": "36"}, {"code": "1001", "count": "6"}, {"code": "1002", "count": "3"}, {"code": "1003", "count": "36"}, {"code": "1004", "count": "2"}], "totalActionCount": "83"} 

所以基本上,对于每个Action,我想跟踪它生成的所有不同响应以及每个响应发生的次数。 最后,我想跟踪该行动的总回复总数。

目前,我已经为输出对象创建了一个Java类,我计划在其中存储输出数据。 我也对我应该存储响应数组的格式和它们各自的计数数字有点困惑。 响应代码类型的总数也因Action而异。

根据我的研究,我似乎需要使用Streaming API来使用JSON解析。 使用Streaming API的原因主要是由于使用非流API需要的内存开销量,这可能与这些日志文件的大小无法实现。 我目前正在考虑使用Jackson或GSON,但我无法找到任何具体的示例或教程来帮助我入门。 有没有人知道我可以学习的一个很好的例子,或者对我如何解决这个问题有任何暗示? 谢谢!

编辑:我的class级定义。

 public class Action { public static class Response { private int _resultCode; private int _count = 0; public Response() {} public int getResultCode() { return _resultCode; } public int getCount() { return _count; } public void setResultCode(int rc) { _resultCode = rc; } public void setCount(int c) { _count = c; } } private List responses = new ArrayList(); private String _name; // I've left out the getters/setters and helper functions that I will add in after. } 

如果我正在使用Jackson,并希望最终能够轻松地将此对象序列化回JSON,那么我是否有关于如何定义此类的建议? 目前我在main()方法中创建了另一个Action类型的ArrayList:List actions = new ArrayList(); 使用HashMaps或其他替代品是更好的选择吗? 此外,它是否允许我使用Jackson后轻松将其序列化为JSON?

您可以查看Genson库http://code.google.com/p/genson/ ,在Wiki页面上,您将找到有关如何使用它的一些示例。 自首次发布以来,它提供了流媒体模型,并且似乎是jackson之后最快的,见基准 。

如果你想做一些真正有效的事情并且内存占用量很小,可以通过实例化JsonReader直接使用流式api,然后使用它来读取记录的结构并增加你的计数器。

否则你可以使用Genson实例直接将文件解析为java对象,但在你的情况下,我不认为它是正确的解决方案,因为它需要你将所有对象存储在内存中!

以下是直接使用流式api的快速示例。 它不会精确打印您期望的结构,因为它需要更多代码来有效地计算您的结构:

 public static void main(String[] args) throws IOException, TransformationException { Map> actions = new HashMap>(); Genson genson = new Genson(); ObjectReader reader = genson.createReader(new FileReader("path/to/the/file")); while(reader.hasNext()) { reader.next(); reader.beginObject(); String action = readUntil("action", reader); // assuming the next name/value pair is responsecode reader.next(); String responseCode = reader.valueAsString(); Map countMap = actions.get(action); if (countMap == null) { countMap = new HashMap(); actions.put(action, countMap); } Integer count = countMap.get(responseCode); if (count == null) { count = 0; } count++; countMap.put(responseCode, count); reader.endObject(); } // for example if you had 2 different response codes for same action it will print // {"Some_Action":{"1001":1,"1000":1}} String json = genson.serialize(actions); } static String readUntil(String name, ObjectReader reader) throws IOException { while(reader.hasNext()) { reader.next(); if (name.equals(reader.name())) { return reader.valueAsString(); } } throw new IllegalStateException(); } 

好的,首先,jackson可以将数据绑定与流媒体相结合。 您只需要一个JsonParser (使用JsonFactory创建,其实例可以从ObjectMapper ,或直接构造)。 然后,您可以将流提前到第一个条目,并从那里开始使用数据绑定( ObjectMapper.readValue(...) )。 这只会读取获取所需单个值实例所需的最小值。

甚至更好,一旦到达arrays,使用“readValues()”方法

 ObjectMapper mapper = new ObjectMapper(); JsonParser jp = mapper.getJsonFactory().createJsonParser(sourceFile); while (jp.nextToken() != JsonToken.START_ARRAY) { } MappingIterator it = mapper.readValues(jp, Entry.class); while (it.hasNextValue()) { Response value = it.nextValue(); // process it; keep count, whatever } 

要输出,您可能需要考虑Jackson CSV模块 :它可以使用CSV变体之一来写入条目; 你可以将分隔符重新定义为你喜欢的任何东西。 有关示例,请参阅项目自述文件

您可以逐个解析您的记录,因此我不认为JSON结构的内存消耗超过几千字节。 只是创造

 class Something { String action; int responsecode; // do not include the fields you don't need } 

并在每一步中读取一条记录。 Guava的HashMultiset及其方法putcountsize为您提供所需的一切。 如果你的内存耗尽(因为巨大的Multimap),你可能需要一个数据库,但我会首先尝试简单的解决方案。

对于输出JSON,您可能需要GSON的TypeAdapterJsonSerializer 。 或者作为黑客,您可以轻松地手动生成输出。