我手头有一个问题,我试图解析以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?
答案 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>
及其方法put
,count
和size
为您提供所需的一切。如果你的内存耗尽(因为巨大的Multimap),你可能需要一个数据库,但我会首先尝试这个简单的解决方案。
对于输出JSON,您可能需要GSON的TypeAdapter
或JsonSerializer
。或者作为黑客,您可以轻松地手动生成输出。