1

我手头有一个问题,我试图解析以 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"}

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

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

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

编辑:我的班级定义。

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?

4

3 回答 3

2

好的,首先,使用 Jackson,您可以将数据绑定与流结合起来。您所需要的只是一个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 模块:它可以使用 CSV 变体之一编写条目;您可以将分隔符重新定义为您喜欢的任何内容。有关示例,请参阅项目 README。

于 2012-10-03T20:55:44.453 回答
1

您可以查看 Genson 库http://code.google.com/p/genson/,在 wiki 页面上您会找到一些有关如何使用它的示例。自首次发布以来,它提供了流模型,并且似乎是继杰克逊之后最快的,请参阅基准

如果您想做一些真正有效的事情并且占用很小的内存空间,请通过实例化 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();
}
于 2012-10-03T19:50:19.530 回答
0

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

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

并在每个步骤中读取一条记录。Guava 的HashMultiset<String, Integer>方法put, count, 并size为您提供所需的一切。如果你的内存用完了(因为 Multimap 很大),你可能需要一个数据库,但我会先尝试简单的解决方案。

对于输出 JSON,您可能需要 GSONTypeAdapterJsonSerializer. 或者作为 hack,您可以轻松地手动生成输出。

于 2012-10-03T19:26:06.037 回答