Java 规范化JSON文件
我有一堆自动生成的JSON文件,我想存储在版本控制中。问题是,每次序列化文件时,属性都会以不同的顺序出现,因此很难知道文件是否真的发生了更改和/或真正的差异是什么 是否有人知道现有的开源工具将执行此任务Java 规范化JSON文件,java,ruby,json,Java,Ruby,Json,我有一堆自动生成的JSON文件,我想存储在版本控制中。问题是,每次序列化文件时,属性都会以不同的顺序出现,因此很难知道文件是否真的发生了更改和/或真正的差异是什么 是否有人知道现有的开源工具将执行此任务 若做不到这一点,是否有人知道一个JSON库,它有一个解析器和一个生成器,可以配置为以(比如)词法顺序输出属性为“漂亮”的JSON?(Java或Ruby库是理想的,但也欢迎其他潜在客户。)我没有尝试过很多组合,但它似乎保持了JSON中属性的顺序 此处删除了一个示例,因为它不再相关 根据以前项目的经
若做不到这一点,是否有人知道一个JSON库,它有一个解析器和一个生成器,可以配置为以(比如)词法顺序输出属性为“漂亮”的JSON?(Java或Ruby库是理想的,但也欢迎其他潜在客户。)我没有尝试过很多组合,但它似乎保持了JSON中属性的顺序 此处删除了一个示例,因为它不再相关 根据以前项目的经验,我知道它是非常可定制的,例如,如果还不够,可以使用它来创建更复杂的适配器 然而,我没有在您的用例中对此进行广泛的测试,但是检查它是否具有预期的输出应该很简单 更新 我没有使用SVN/CVS检查您的文件是否被修改,而是从他们的文档中发现GSON内置了一个可以解决您的问题,也可以不解决您的问题: 使用注释可以维护同一对象的多个版本。此注释可用于类、字段以及在将来的版本中的方法。为了利用此功能,必须将Gson实例配置为忽略大于某个版本号的任何字段/对象。如果Gson实例上未设置任何版本,则无论版本如何,它都将序列化和反序列化所有字段和类 更新 我唯一能想到的另一件事是使用解析外部文件,并使用将解析的
JSON
转换回字符串,然后您可以确保它已通过单个“解析器”运行,并且输出不会有所不同
然后,您可以检测任何可能的更改。开源Java库可能需要一些努力来设置,但它能够进行非常漂亮的打印,并且具有非常整洁的
@JsonPropertyOrder
注释,支持字母或手动指定的输出顺序。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}"
以下是如何生成“漂亮”格式:
。。。相同的属性正在以不同的顺序插入 这对我来说没有多大意义,但我要试一试 因为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);
}
qstringqvjson::encodeJson(constqvariant&jsonObject){
QVariant::Type=jsonObject.Type();
开关(类型){
案例QVariant::映射:
返回encodeObject(jsonObject);
案例QVariant::列表:
返回编码数组(jsonObject);
大小写QVariant::字符串:
返回编码字符串(jsonObject);
案例QVariant::Int:
案例QVariant::Double:
返回encodeNumeric(jsonObject);
案例QVariant::Bool:
返回encodeBool(jsonObject);
案例QVariant::无效:
返回encodeNull(jsonObject);
违约:
返回encodingError(“encodeJson”,jsonObject,ErrorUnrecognizedObject);
}
}
qstringqvjson::encodeObject(constqvariant&jsonObject){
QString结果(“{”);
QMap=jsonObject.toMap();
qmapiteratori(map);
while(i.hasNext()){
i、 next();
append(encodeString(i.key());
结果。追加(“:”);
append(encodeJson(i.value());
if(i.hasNext()){
结果。追加(“,”);
}
}
result.append(“}”);
返回结果;
}
qstringqvjson::encodeArray(constqvariant&jsonObject){
QString结果(“[”);
QList list=jsonObject.toList();
对于(int i=0;i255){
结果。追加(“\\u”);
append(QString::number(chr,16).rightjustized(4,'0'));
}
否则{
结果.追加(值.at(i));
}
}
result.append(“”);
QString displayResult=result;//用于调试,因为“result”通常不显示
Q_未使用(显示结果);
返回结果;
}
qstringqvjson::encodeNumeric(constqvariant&jsonObject){
雷图
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);
}
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
# }
# }
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;
}
}