简体   繁体   English

从Java中的Json字符串中删除重复项?

[英]Remove duplicates from a Json String in Java?

I have a Json String with duplicate values: 我有一个重复值的Json字符串:

String json = "{\"Sign_In_Type\":\"Action\",\"Sign_In_Type\":\"Action\"}";

that correctly throws an exception when I try to create a JSONObject: 在我尝试创建JSONObject时正确抛出异常:

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

Error: 错误:

Exception in thread "main" java.lang.RuntimeException: org.json.JSONException: Duplicate key "Sign_In_Type"
    at com.campanja.app.Upload.main(Upload.java:52)
Caused by: org.json.JSONException: Duplicate key "Sign_In_Type"
    at org.json.JSONObject.putOnce(JSONObject.java:1076)
    at org.json.JSONObject.(JSONObject.java:205)
    at org.json.JSONObject.(JSONObject.java:402)
    at com.campanja.app.Upload.main(Upload.java:49)

Is there a smart way of removing or checking for duplicates before I convert it to a JSONOBject? 在将其转换为JSONOBject之前,是否有一种智能方法可以删除或检查重复项? I have tried to create: 我试图创建:

Set set = new HashSet(Arrays.asList(json));

but that gives me: 但这给了我:

[{"Sign_In_Type":"Action","Sign_In_Type":"Action"}]

Any suggesstions welcome, thanks! 欢迎任何建议,谢谢!

Two options I can think of right off the bat: 我可以想到两个选项:

  • Parse the string using wither regex or tokens, add each key-value pair to a hashmap, and in the end recreate your JSON document with the duplicates removed. 使用wither regex或tokens解析字符串,将每个键值对添加到hashmap,最后重新创建删除了重复项的JSON文档。 In this case though I would only remove key-value pairs that are exactly the same. 在这种情况下,虽然我只会删除完全相同的键值对。
  • Download the source code for org.json.JSONObject , and make a slight modification to the code to automatically leave out duplicates. 下载org.json.JSONObject的源代码, org.json.JSONObject代码稍作修改,以自动省去重复项。 This is a bit dangerous though. 这有点危险。 Another option is to create a modified version that simply validates and modifies. 另一种选择是创建一个简单验证和修改的修改版本。

Extending JSONObject Working Example 扩展JSONObject工作示例

The below code allows you to create a JSONOBbject with a string containing duplicate keys. 下面的代码允许您使用包含重复键的字符串创建JSONOBbject。 Exceptions are thrown only when you have two key-values that have the same key, but different values. 仅当您有两个具有相同键但值不同的键值时,才会抛出异常。 This was because I think it would be a problem to choose at random which of the two should be assigned (eg the later value?). 这是因为我认为随机选择应该分配哪两个(例如后面的​​值?)是个问题。 Of course this can be changed to work as you wish (eg keep last value for multiple keys). 当然,这可以根据需要更改为工作(例如,保留多个键的最后一个值)。

Modified Class 修改后的类

import org.json.JSONException;
import org.json.JSONObject;


public class JSONObjectIgnoreDuplicates extends JSONObject {

     public JSONObjectIgnoreDuplicates(String json) {
        super(json);
    }

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

Main method 主要方法

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

Assuming that String json = "{\\"Sign_In_Type\\":\\"Action\\",\\"Sign_In_Type\\":\\"Action\\"}"; 假设String json =“{\\”Sign_In_Type \\“:\\”Action \\“,\\”Sign_In_Type \\“:\\”Action \\“}”; is a fiction for testing, can I ask whether creating the data as a String is the best choice in the first place? 是一个测试的小说,我可以问一下,创建数据作为字符串是否是最好的选择? Why not a HashMap, or some other structure that either overwrites the subsequent reuses of a name or ignores them or throws an error when you add them? 为什么不使用HashMap或其他一些结构来覆盖名称的后续重用或忽略它们或在添加它们时抛出错误? Don't wait until the conversion to JSON to make your data valid. 不要等到转换为JSON才能使数据有效。

You can make use of the Jackson library to parse JSON. 您可以使用Jackson库来解析JSON。 I'd problems doing the same task as you with org.json's package, but I turned to Jackson and I solved it: http://wiki.fasterxml.com/JacksonHome 我在使用org.json的软件包时遇到了同样的问题,但我转向杰克逊并解决了它: http ://wiki.fasterxml.com/JacksonHome

I expanded Menelaos Bakopoulos answer, so that if inner values are also with duplicates, it won't create issues. 我扩展了Menelaos Bakopoulos的答案,因此如果内部值也有重复,那么它不会产生问题。 the former solution worked on the first level only. 前一种解决方案仅适用于第一级。

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) {
                if (!storedValue.toString().equals(value.toString())) //Only throw Exception for different values with same key
                    throw new JSONException("Duplicate key \"" + key + "\"");
                else
                    return this;
            }
            this.put(key, value);
        }
        return this;
    }
}

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

Sorry I can't comment on Menelaos Bakopoulos' response due to reputation<50... Stupid system 对不起,由于声誉<50 ...愚蠢的系统,我无法评论Menelaos Bakopoulos的回应

Your solution unfortunately does not work here: 遗憾的是,您的解决方案无效:

SEVERE: ERROR converting JSON to XML org.json.JSONException: Duplicate key "id"
org.json.JSONObject.putOnce(JSONObject.java:1076)
org.json.JSONObject.<init>(JSONObject.java:205)
org.json.JSONTokener.nextValue(JSONTokener.java:344)
org.json.JSONArray.<init>(JSONArray.java:125)
org.json.JSONTokener.nextValue(JSONTokener.java:348)
org.json.JSONObject.<init>(JSONObject.java:205)
JSONUtilities.JSONObjectIgnoreDuplicates.<init>(JSONUtilities.java:38)

It seems that calling super(json) in JSONObjectIgnoreDuplicates 's constructor sends the code into a loop inside JSONObject , not JSONObjectIgnoreDuplicates ;{ 似乎在JSONObjectIgnoreDuplicates的构造函数中调用super(json)会将代码发送到JSONObject内部的循环中,而不是JSONObjectIgnoreDuplicates ; {

I'm currently trying Asaf Bartov's solution, but there's no call from JSONObjectIgnoreDuplicates to JsonDupTokener , so appart from overloading the constructor of JSONObjectIgnoreDuplicates as follows, I don't see how it could work: 我目前正在尝试Asaf Bartov的解决方案,但是没有从JSONObjectIgnoreDuplicatesJsonDupTokener调用,所以JsonDupTokener重载JSONObjectIgnoreDuplicates的构造函数,如下所示,我看不出它是如何工作的:

    public JSONObjectIgnoreDuplicates(String json) throws JSONException {
        this(new JSONDupTokener(json));
    }

EDIT: I can confirm this works :)))) 编辑:我可以确认这是有效的:))))

Thanks everybody!!!! 谢谢大家!!!!

With Google Gson you can decide what to do with duplicates in the input string. 使用Google Gson,您可以决定如何处理输入字符串中的重复项。 You need to register your own TypeAdapter responsible for serialization/deserialization of objects. 您需要注册自己的TypeAdapter负责对象的序列化/反序列化。 It would look like this: 它看起来像这样:

// this implementation converts the json string to a Map<String, String>,
// saving only the first duplicate key and dropping the rest
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;
    }
}

Then you can parse your string: 然后你可以解析你的字符串:

String json = "{\"Sign_In_Type\":\"Action\",\"Sign_In_Type\":\"Action\"}";

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

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

The map will contain only the first "Sign_In_Type" . 地图将仅包含第一个"Sign_In_Type"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM