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 クラスを作成しました。また、応答の配列とそれぞれのカウント数を格納する必要がある形式についても少し混乱しています。応答コードの種類の総数は、Action によっても異なります。

私の調査によると、ストリーミング API を使用して JSON 解析を利用する必要があるようです。ストリーミング 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にシリアル化できますか?

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 バリアントの 1 つを使用してエントリを書き込むことができます。また、セパレーターを好きなように再定義できます。例については、プロジェクトの README を参照してください。

于 2012-10-03T20:55:44.453 に答える
1

Genson ライブラリhttp://code.google.com/p/genson/を見ることができます。Wiki ページには、使用方法の例がいくつかあります。最初のリリース以来、ストリーミング モデルを提供しており、Jackson に次いで最速のようです。ベンチマークを参照してください。

本当に効率的で小さなメモリ フット プリントで何かを実行したい場合は、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

レコードを1つずつ解析できるので、JSON構造のメモリ消費量が数キロバイトを超えることはないと思います。作成するだけ

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

各ステップで1つのレコードを読み取ります。GuavaHashMultiset<String, Integer>はそのメソッドput、、を備えておりcountsize必要なものすべてを提供します。(巨大なマルチマップのために)メモリが不足した場合は、代わりにデータベースが必要になる可能性がありますが、最初に簡単な解決策を試してみます。

出力JSONには、GSONTypeAdapterまたはが必要になる場合がありますJsonSerializer。または、ハックとして、出力を手動で簡単に生成できます。

于 2012-10-03T19:26:06.037 に答える