programing

JSON 파일 정규화

golfzon 2023. 3. 5. 10:51
반응형

JSON 파일 정규화

자동으로 생성된 JSON 파일을 버전 관리에 저장하고 싶습니다.문제는 파일이 시리얼화될 때마다 속성이 다른 순서로 표시되기 때문에 파일이 실제로 변경되었는지, 실제 차이가 무엇인지 알 수 없다는 것입니다.

이 작업을 수행할 기존 오픈 소스 도구를 알고 있는 사람이 있습니까?

그렇지 않으면 파서와 제너레이터를 갖춘 JSON 라이브러리를 아는 사람이 있습니까? (예를 들어) 알파벳 순서로 속성을 가진 "예쁜" JSON을 출력하도록 구성할 수 있습니다. (Java 또는 Ruby 라이브러리가 이상적이지만 다른 리드도 환영입니다.)

Python의 JSON 모듈은 다른 프로그램에서도 매우 사용할 수 있습니다.

generate_json | python -mjson.tool > canonical.json

전화하셔서 약간의 오버헤드를 겪고 싶으시면

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;
  }
}

이는 잭슨에서 지원됩니다.

@JsonPropertyOrder(알파벳=true)

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

오픈 소스 Java 라이브러리 Jackson은 셋업하는 데 다소 시간이 걸릴 수 있지만 예쁜 인쇄가 가능하고 꽤 깔끔합니다.@JsonPropertyOrder알파벳 또는 수동으로 지정된 출력 순서를 지원하는 주석입니다.

많은 조합을 시도하지는 않았지만, 구글-gson이 속성의 순서를 유지하고 있는 것 같습니다.JSON.

더 이상 관련이 없기 때문에 여기서 예를 삭제했습니다.

이전 프로젝트의 경험에서 알 수 있듯이 기본 객체가 충분하지 않은 경우 Gson Builder를 사용하여 보다 복잡한 어댑터를 만들 수 있습니다.

다만, 사용 사례에서 광범위하게 테스트한 것은 아닙니다만, 예상한 출력이 있는지 확인하는 것은 간단할 것입니다.

갱신하다

SVN/CVS를 사용하여 파일이 변경되었는지 여부를 확인하는 대신 GSON에는 사용자의 문제를 해결할 수 있는 내장 버전 관리 지원이 포함되어 있습니다.

@Since 주석을 사용하여 동일한 개체의 여러 버전을 유지할 수 있습니다.이 주석은 클래스, 필드 및 향후 릴리스에서는 메서드에서 사용할 수 있습니다.이 기능을 이용하려면 버전 번호보다 큰 필드/오브젝트를 무시하도록 Gson 인스턴스를 설정해야 합니다.Gson 인스턴스에 버전이 설정되어 있지 않은 경우 버전에 관계없이 모든 필드 및 클래스가 시리얼화 및 역직렬화됩니다.

갱신하다

내가 생각할 수 있는 유일한 것은 외부 파일을 코뿔소로 해석하고 해석된 파일을 변환하는 것입니다.JSON문자열로 돌아가면 단일 '파서'를 통해 실행되며 출력은 다르지 않습니다.

그런 다음 가능한 변경을 감지할 수 있습니다.

Ruby 1.9+는 해시 삽입 순서를 유지하며, JSON for 1.9+는 이를 준수합니다.

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}

여기 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);
}

시리얼화할 개체의 키를 출력하기 전에 정렬합니다.Ruby 1.9에서는 기본적으로 해시가 주문되지만, Ruby 1.8에서는 그렇지 않습니다.active_support에서 OrderedHash를 사용하여 두 경우 모두 확인할 수 있습니다.

JSON j j j j j j j j j j j j j j j j j j j.수 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
#   }
# }

언급URL : https://stackoverflow.com/questions/12584744/canonicalizing-json-files

반응형