从 Java 中的 Json 字符串中删除重复项?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19001238/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Remove duplicates from a Json String in Java?
提问by C.A
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? I have tried to create:
在将其转换为 JSONOBject 之前,是否有删除或检查重复项的智能方法?我试图创造:
Set set = new HashSet(Arrays.asList(json));
but that gives me:
但这给了我:
[{"Sign_In_Type":"Action","Sign_In_Type":"Action"}]
Any suggesstions welcome, thanks!
欢迎任何建议,谢谢!
回答by Menios
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. 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. This is a bit dangerous though. Another option is to create a modified version that simply validates and modifies.
- 使用 wither regex 或 tokens 解析字符串,将每个键值对添加到哈希图中,最后重新创建您的 JSON 文档并删除重复项。在这种情况下,虽然我只会删除完全相同的键值对。
- 下载 的源代码
org.json.JSONObject
,并对代码稍作修改以自动排除重复项。不过这有点危险。另一种选择是创建一个简单的验证和修改的修改版本。
Extending JSONObject Working Example
扩展 JSONObject 工作示例
The below code allows you to create a JSONOBbject with a string containing duplicate keys. 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 (e.g. the later value?). Of course this can be changed to work as you wish (e.g. keep last value for multiple keys).
下面的代码允许您使用包含重复键的字符串创建一个 JSONOBbject。仅当您有两个具有相同键但不同值的键值时才会抛出异常。这是因为我认为随机选择应该分配两个中的哪一个是一个问题(例如后面的值?)。当然,这可以更改为您希望的工作(例如,保留多个键的最后一个值)。
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);
}
回答by Dan
Assuming that 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? Don't wait until the conversion to JSON to make your data valid.
假设 String json = "{\"Sign_In_Type\":\"Action\",\"Sign_In_Type\":\"Action\"}"; 是一个测试小说,我可以问一下,首先将数据创建为 String 是否是最佳选择?为什么不是 HashMap 或其他一些结构,它们要么覆盖名称的后续重用,要么忽略它们,或者在添加它们时抛出错误?不要等到转换为 JSON 才能使您的数据有效。
回答by russellhoff
You can make use of the Hymanson library to parse JSON. I'd problems doing the same task as you with org.json's package, but I turned to Hymanson and I solved it: http://wiki.fasterxml.com/HymansonHome
您可以使用 Hymanson 库来解析 JSON。我在使用 org.json 的包执行与您相同的任务时遇到问题,但我求助于 Hymanson 并解决了它:http://wiki.fasterxml.com/HymansonHome
回答by Asaf Bartov
I expanded Menelaos Bakopoulos answer, so that if inner values are also with duplicates, it won't create issues. the former solution worked on the first level only.
我扩展了 Menelaos Bakopoulos 的答案,因此如果内部值也有重复,则不会产生问题。前一种解决方案仅适用于第一级。
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);
}
}
}
}
回答by user2115112
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
;{
似乎调用super(json)
inJSONObjectIgnoreDuplicates
的构造函数将代码发送到内部的循环中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 的解决方案,但没有来自JSONObjectIgnoreDuplicates
to的调用JsonDupTokener
,因此从重载JSONObjectIgnoreDuplicates
如下构造函数来看,我看不出它是如何工作的:
public JSONObjectIgnoreDuplicates(String json) throws JSONException {
this(new JSONDupTokener(json));
}
EDIT: I can confirm this works :))))
编辑:我可以确认这有效:))))
Thanks everybody!!!!
谢谢大家!!!!
回答by Kirill Simonov
With Google Gsonyou can decide what to do with duplicates in the input string. You need to register your own TypeAdapter
responsible for serialization/deserialization of objects. It would look like this:
使用Google Gson,您可以决定如何处理输入字符串中的重复项。您需要注册自己TypeAdapter
负责对象的序列化/反序列化。它看起来像这样:
// 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"
.