忽略JSONObject中的重复键

时间:2017-12-18 20:59:52

标签: java json

我有一个JSON格式的字符串

dependencies {

    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    implementation 'com.android.support:design:26.1.0'

    implementation 'com.google.android.gms:play-services-maps:11.6.2'
    compile 'com.google.android.gms:play-services-auth:9.0.0'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'

}

当我尝试从此字符串

创建对象时
String str = "{\"innerkey1\":\"innervalue1\",
\"innerkey2\":\"innervalue2\",\"innerkey1\":\"innervalue3\"}";

我收到错误

try  {
    JSONObject obj = new JSONObject(str); 
} catch(Exception e) {
     e.printStackTrace();
}

有没有办法可以通过忽略字符串中的最后一个键来创建JSONObject?

6 个答案:

答案 0 :(得分:1)

这是JSON规范的问题。根据使用的规范,可以解释是否允许重复密钥。在this thread中,人们普遍认为定义是一个密钥在JSONObject中必须是唯一的。

因此,您不应尝试解析可能无效的JSON对象,而应使用有效的JSON对象。

但是我对解决方案提出了一个小建议:但是你将它付诸实践,你可以使用(多个出现的)密钥并将存储在那里的所有对象作为一个数组分配给这个(现在唯一的)密钥。因此,您合并了几个相同的键,并将链接的对象合并到一个数组中。这至少会使JSON对象有效:

{
    "a" : "x",
    "a" : "y"
}
// becomes
{
    "a" : ["x", "y"]
}

答案 1 :(得分:0)

不幸的是没有。虽然它在技术上不符合JSON标准,但它并不容易使用/你很难找到一个可以解析它的库。

见这里:https://stackoverflow.com/a/21833017/8972283

如果可能,我建议您更新JSON的创建方式。

答案 2 :(得分:0)

这不是最简单的解决方案,但它允许您在输入字符串中复制并选择如何处理它们(重命名,删除等)。您应该使用Gson库并注册自己的TypeAdapter负责对象的序列化/反序列化。它看起来像这样:

class NoDuplicatesAdapter extends TypeAdapter<HashMap<String, String>> {
    @Override
    public void write(JsonWriter out, HashMap<String, String> value) throws IOException {
        out.beginObject();
        for (Map.Entry<String, String> e: value.entrySet()) {
            out.name(e.getKey()).value(e.getValue());
        }
        out.endObject();
    }
    @Override
    public HashMap<String, String> read(JsonReader in) throws IOException {
        final HashMap<String, String> map = new HashMap<>();
        in.beginObject();
        while (in.hasNext()) {
            String name = in.nextName();
            // putting value to the map only if this key is not present;
            // here you can actually find duplicate keys and decide what to do with them
            map.putIfAbsent(name, in.nextString());
        }
        in.endObject();
        return map;
    }
}

这是最复杂的部分,现在我们只需要阅读JSON字符串:

String str = "{\"innerkey1\":\"innervalue1\", \"innerkey2\":\"innervalue2\",\"innerkey1\":\"innervalue3\"}";

Type mapType = new TypeToken<Map<String, String>>() {}.getType();

Map<String, String> map = new GsonBuilder()
        .registerTypeAdapter(mapType, new NoDuplicatesAdapter())
        .create()
        .fromJson(str, mapType);

map仅包含第一个"innerkey1"

答案 3 :(得分:0)

我有一个JSON parser,它允许使用代码将重复键视为数组中的元素。请参阅&#34;文档构建器代码&#34;。

部分

答案 4 :(得分:0)

您可以使用objectMapper跳过此错误

ObjectMapper mapper = new ObjectMapper();
String json = "{\"name\":\"test\", \"name\":\"new test\"}";

Map<String, Object> map = new HashMap<String, Object>();

map = mapper.readValue(json, new TypeReference<Map<String, String>>(){});

现在您可以轻松地从地图转换为JSONObject

new JSONObject(map);

答案 5 :(得分:0)

以下代码允许您使用包含重复键的字符串创建JSONObject。仅当您具有两个键值相同但键值不同的键值时,才会引发异常。这是因为我认为随机选择这两者中的哪一个会是一个问题(例如,后面的值?)。当然,可以将其更改为您希望的方式(例如,保留多个键的最后一个值)。

您将需要一个JSONObjectIgnoreDuplicates类来扩展JSONObject:

public class JSONObjectIgnoreDuplicates extends JSONObject {

    public JSONObjectIgnoreDuplicates(JSONTokener x) throws JSONException {
        super(x);
    }

    @Override
    public JSONObject putOnce(String key, Object value) throws JSONException {
        Object storedValue;
        if (key != null && value != null) {
            if ((storedValue = this.opt(key)) != null) {
                //Only throw Exception for different values with same key
                if (!storedValue.toString().equals(value.toString())) 
                    throw new JSONException("Duplicate key \"" + key + "\"");
                else
                    return this;
            }
            this.put(key, value);
        }
        return this;
    }
}

要处理嵌套JSONObject中的重复键,您需要将JSONTokener扩展为JsonDupTokener。

public class JsonDupTokener extends JSONTokener {

    public JsonDupTokener(String s) {
        super(s);
    }

    @Override
    public Object nextValue() throws JSONException {
        char c = this.nextClean();
        switch (c) {
            case '\"':
            case '\'':
                return this.nextString(c);
            case '[':
                this.back();
                return new JSONArray(this);
            case '{':
                this.back();
                return new JSONObjectIgnoreDuplicates(this);
            default:
                StringBuffer sb;
                for (sb = new StringBuffer(); c >= 32 && ",:]}/\\\"[{;=#".indexOf(c) < 0; c = this.next()) {
                    sb.append(c);
                }

                this.back();
                String string = sb.toString().trim();
                if ("".equals(string)) {
                    throw this.syntaxError("Missing value");
                } else {
                    return JSONObject.stringToValue(string);
                }
        }
    }
}

主要方法如下:

// string "json" with duplicate keys
String json = "{\"Sign_In_Type\": \"Action\", \"Sign_In_Type\": \"Action\"}";

try {
    JSONObject json_obj = new JSONObjectIgnoreDuplicates(new JSONDupTokener(json));
    String type = json_obj.getString("Sign_In_Type");
} catch (JSONException e) {
    throw new RuntimeException(e);
}

希望这会有所帮助。如果您有任何疑问,请告诉我。祝你好运。