32

バージョン管理に保存したい自動生成されたJSONファイルがたくさんあります。問題は、ファイルがシリアル化されるたびに、属性が異なる順序で出力されるため、ファイルが実際に変更されたかどうか、および/または実際の違いが何であるかを判断するのが難しいことです。

このタスクを実行する既存のオープンソースツールを知っている人はいますか?

それができない場合、(たとえば)辞書式順序で属性を持つ「きれいな」JSONを出力するように構成できるパーサーとジェネレーターを備えたJSONライブラリを知っている人はいますか?(JavaまたはRubyライブラリが理想的ですが、他のリードも歓迎します。)

4

8 に答える 8

16

PythonのJSONモジュールは、他のプログラムから非常に使いやすくなっています。

generate_json | python -mjson.tool > canonical.json
于 2012-10-26T20:39:57.643 に答える
4

あなたが電話することによって少しのオーバーヘッドを通過することをいとわないなら

gson.toJson(canonicalize(gson.toJsonTree(obj)));

次に、次のようなことを行うことができます。

protected static JsonElement canonicalize(JsonElement src) {
  if (src instanceof JsonArray) {
    // Canonicalize each element of the array
    JsonArray srcArray = (JsonArray)src;
    JsonArray result = new JsonArray();
    for (int i = 0; i < srcArray.size(); i++) {
      result.add(canonicalize(srcArray.get(i)));
    }
    return result;
  } else if (src instanceof JsonObject) {
    // Sort the attributes by name, and the canonicalize each element of the object
    JsonObject srcObject = (JsonObject)src;
    JsonObject result = new JsonObject();
    TreeSet<String> attributes = new TreeSet<>();
    for (Map.Entry<String, JsonElement> entry : srcObject.entrySet()) {
      attributes.add(entry.getKey());
    }
    for (String attribute : attributes) {
      result.add(attribute, canonicalize(srcObject.get(attribute)));
    }
    return result;
  } else {
    return src;
  }
}
于 2016-02-06T14:23:23.260 に答える
1

これはジャクソンでサポートされています:

@JsonPropertyOrder(alphabetic = true)

http://fasterxml.github.io/jackson-annotations/javadoc/2.3.0/com/fasterxml/jackson/annotation/JsonPropertyOrder.html

于 2016-10-09T05:09:03.700 に答える
0

私は多くの組み合わせを試していませんが、google-gsonが属性の順序を保持しているようJSONです。

関連性がなくなったため、ここで例を削除しました

以前のプロジェクトの経験から、非常にカスタマイズ可能であることがわかっています。たとえば、ベースオブジェクトが十分でない場合は、 GsonBuilderを使用してより複雑なアダプターを作成できます。

ただし、これをユースケースで広範囲にテストしたことはありませんが、期待される出力があるかどうかを簡単に確認できるはずです。

アップデート

SVN / CVSを使用してファイルが変更されたかどうかを確認するのではなく、GSONには、ドキュメントから、問題に対処できる場合とできない場合があるバージョン管理サポートが組み込まれていることがわかりました。

@Sinceアノテーションを使用すると、同じオブジェクトの複数のバージョンを維持できます。このアノテーションは、クラス、フィールド、および将来のリリースではメソッドで使用できます。この機能を利用するには、バージョン番号より大きいフィールド/オブジェクトを無視するようにGsonインスタンスを構成する必要があります。Gsonインスタンスにバージョンが設定されていない場合、バージョンに関係なく、すべてのフィールドとクラスがシリアル化および逆シリアル化されます。

アップデート

私が考えることができる他の唯一のことは、rhinoを使用JSON.stringifyして外部ファイルを解析し、解析を文字列に変換するために使用するJSONことです。そうすれば、単一の「パーサー」を実行し、出力に違いがないことを確認できます。

その後、考えられる変更を検出できます。

于 2012-09-25T14:16:54.910 に答える
0

オープンソースのJavaライブラリJacksonは、セットアップに多少の労力を要する可能性がありますが、きれいに印刷でき、@JsonPropertyOrderアルファベットまたは手動で指定された出力順序をサポートするかなりきれいな注釈が付いています。

于 2012-09-25T14:20:32.000 に答える
0

Ruby 1.9以降はハッシュの挿入順序を維持し、1.9以降のJSONはそれを尊重します。

asdf = {'a' => 1, 'b' => 2}
asdf.to_json # => "{\"a\":1,\"b\":2}"

asdf = {'b' => 1, 'a' => 2}
asdf.to_json # => "{\"b\":1,\"a\":2}"

「きれいな」フォーマットを生成する方法は次のとおりです。

asdf = {'a' => 1, 'b' => 2}
puts JSON.pretty_generate(asdf)
{
  "a": 1,
  "b": 2
}

asdf = {'b' => 1, 'a' => 2}
irb(main):022:0> puts JSON.pretty_generate(asdf)
{
  "b": 1,
  "a": 2
}

...同じ属性が異なる順序で挿入されています...

これは私にはあまり意味がありませんが、私はショットを撮るつもりです。

Rubyは挿入順序を維持するため、特定の順序でハッシュを作成する場合、データの順序はそれほど重要ではありません。キーを並べ替えてハッシュを再生成し、それをJSONに渡すことで、順序を強制します。

require 'json'

puts Hash[{'a' => 1, 'b' => 2}.sort_by{ |a| a }].to_json
=> {"a":1,"b":2}

puts Hash[{'b' => 2, 'a' => 1}.sort_by{ |a| a }].to_json
=> {"a":1,"b":2}

puts Hash[{'b' => 2, 'c' => 3, 'a' => 1}.sort_by{ |a| a }].to_json
=> {"a":1,"b":2,"c":3}

puts Hash[{'b' => 2, 'c' => 3, 'a' => 1}.sort_by{ |a| a }].to_json
=> {"a":1,"b":2,"c":3}

puts Hash[{'a' => 1, 'c' => 3, 'b' => 2}.sort_by{ |a| a }].to_json
=> {"a":1,"b":2,"c":3}
于 2012-09-25T15:27:36.590 に答える
0

Qtの単純なJSONエンコーダーは次のとおりです。Javaへの再キャストは比較的簡単です。あなたが本当にする必要があるのは、書き出すときにキーがソートされていることを確認することです-別のJSONパッケージで読み込むことができます。

QString QvJson::encodeJson(const QVariant& jsonObject) {
    QVariant::Type type = jsonObject.type();
    switch (type) {
        case QVariant::Map: 
            return encodeObject(jsonObject);
        case QVariant::List:
            return encodeArray(jsonObject);
        case QVariant::String:
            return encodeString(jsonObject);
        case QVariant::Int:
        case QVariant::Double:
            return encodeNumeric(jsonObject);
        case QVariant::Bool:
            return encodeBool(jsonObject);
        case QVariant::Invalid:
            return encodeNull(jsonObject);
        default:
            return encodingError("encodeJson", jsonObject, ErrorUnrecognizedObject);
    }
}

QString QvJson::encodeObject(const QVariant& jsonObject) {
    QString result("{ ");
    QMap<QString, QVariant> map = jsonObject.toMap();
    QMapIterator<QString, QVariant> i(map);
    while (i.hasNext()) {
        i.next();
        result.append(encodeString(i.key()));

        result.append(" : ");

        result.append(encodeJson(i.value()));

        if (i.hasNext()) {
            result.append(", ");
        }
    }
    result.append(" }");
    return result;
}

QString QvJson::encodeArray(const QVariant& jsonObject) {
    QString result("[ ");
    QList<QVariant> list = jsonObject.toList();
    for (int i = 0; i < list.count(); i++) {
        result.append(encodeJson(list.at(i)));
        if (i+1 < list.count()) {
            result.append(", ");
        }
    }
    result.append(" ]");
    return result;
}

QString QvJson::encodeString(const QVariant &jsonObject) {
    return encodeString(jsonObject.toString());
}

QString QvJson::encodeString(const QString& value) {
    QString result = "\"";
    for (int i = 0; i < value.count(); i++) {
        ushort chr = value.at(i).unicode();
        if (chr < 32) {
            switch (chr) {
                case '\b':
                    result.append("\\b");
                    break;
                case '\f':
                    result.append("\\f");
                    break;
                case '\n':
                    result.append("\\n");
                    break;
                case '\r':
                    result.append("\\r");
                    break;
                case '\t':
                    result.append("\\t");
                    break;
                default:
                    result.append("\\u");
                    result.append(QString::number(chr, 16).rightJustified(4, '0'));
            }  // End switch
        }
        else if (chr > 255) {
            result.append("\\u");
            result.append(QString::number(chr, 16).rightJustified(4, '0'));
        }
        else {
            result.append(value.at(i));
        }
    }
    result.append('"');
    QString displayResult = result;  // For debug, since "result" often doesn't show
    Q_UNUSED(displayResult);
    return result;
}

QString QvJson::encodeNumeric(const QVariant& jsonObject) {
    return jsonObject.toString();
}

QString QvJson::encodeBool(const QVariant& jsonObject) {
    return jsonObject.toString();
}

QString QvJson::encodeNull(const QVariant& jsonObject) {
    return "null";
}

QString QvJson::encodingError(const QString& method, const QVariant& jsonObject, Error error) {
    QString text;
    switch (error) {
        case ErrorUnrecognizedObject: 
            text = QObject::tr("Unrecognized object type");
            break;
    default:
            Q_ASSERT(false);
    }
    return QObject::tr("*** Error %1 in QvJson::%2 -- %3").arg(error).arg(method).arg(text);
}
于 2012-09-26T01:33:42.477 に答える
0

シリアル化するオブジェクトのキーを並べ替えてから出力します。Ruby 1.9では、ハッシュはデフォルトで順序付けられています。Ruby 1.8では、そうではありません。どちらの場合でも、active_supportのOrderedHashを使用して確認できます。

JSONデータを書き込むときはいつでも、キーを並べ替えます。Ruby 1.8では、シンボルを並べ替えることができないためto_s、並べ替えを呼び出す必要があることに注意してください。

require 'rubygems'
require 'json'
require 'active_support/ordered_hash'

obj = {
  :fig => false,
  :bananas => false,
  :apples => true,
  :eggplant => true,
  :cantaloupe => true,
  :dragonfruit => false
}

def sorted_hash(hsh)
  sorted_keys = hsh.keys.sort_by { |k| k.to_s }
  sorted_keys.inject(ActiveSupport::OrderedHash.new) do |o_hsh, k|
    o_hsh[k] = hsh[k]
    o_hsh
  end
end

puts JSON.pretty_generate(obj)
# Could output in any order, depending on version of Ruby
# {
#   "eggplant": true,
#   "cantaloupe": true,
#   "dragonfruit": false,
#   "fig": false,
#   "bananas": false,
#   "apples": true
# }

puts JSON.pretty_generate(sorted_hash(obj))
# Always output in the same order
# {
#   "apples": true,
#   "bananas": false,
#   "cantaloupe": true,
#   "dragonfruit": false,
#   "eggplant": true,
#   "fig": false
# }

データがオブジェクトの配列またはネストされたオブジェクトで構成されている場合は、並べ替えられたハッシュを再帰的に作成する必要があります。

nested_obj = {:a => {:d => true, :b => false}, :e => {:k => false, :f => true}, :c => {:z => false, :o => true}}

def recursive_sorted_hash(hsh)
  sorted_keys = hsh.keys.sort_by { |k| k.to_s }
  sorted_keys.inject(ActiveSupport::OrderedHash.new) do |o_hsh, k|
    o_hsh[k] = hsh[k].is_a?(Hash) ? recursive_sorted_hash(hsh[k]) : hsh[k]
    o_hsh
  end
end

puts JSON.pretty_generate(nested_obj)
# Again, could be in any order
# {
#   "a": {
#     "b": false,
#     "d": true
#   },
#   "e": {
#     "f": true,
#     "k": false
#   },
#   "c": {
#     "z": false,
#     "o": true
#   }
# }

puts JSON.pretty_generate(recursive_sorted_hash(nested_obj))
# Even nested hashes are in alphabetical order
# {
#   "a": {
#     "b": false,
#     "d": true
#   },
#   "c": {
#     "o": true,
#     "z": false
#   },
#   "e": {
#     "f": true,
#     "k": false
#   }
# }
于 2012-10-13T07:20:50.280 に答える