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

时间:2012-10-03 17:46:41

标签: java json jackson gson

我手头有一个问题,我试图解析以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,但我无法找到任何具体的示例或教程来帮助我入门。有谁知道一个很好的例子,我可以学习或有任何关于如何解决这个问题的提示?谢谢你!

编辑:我的班级定义。

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<Response> responses = new ArrayList<Response>();
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?

3 个答案:

答案 0 :(得分:2)

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

甚至更好,一旦到达数组

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

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

答案 1 :(得分:1)

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

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

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

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

public static void main(String[] args) throws IOException, TransformationException {
    Map<String, Map<String, Integer>> actions = new HashMap<String, Map<String, Integer>>();
    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<String, Integer> countMap = actions.get(action);
        if (countMap == null) {
            countMap = new HashMap<String, Integer>();
            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();
}

答案 2 :(得分:0)

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

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

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

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

相关问题