Reputation: 3276
I am using a JSONObject in order to remove a certin attribute I don't need in a JSON String:
JSONObject jsonObject = new JSONObject(jsonString);
jsonObject.remove("owner");
jsonString = jsonObject.toString();
It works ok however the problem is that the JSONObject is "an unordered collection of name/value pairs" and I want to maintain the original order the String had before it went through the JSONObject manipulation.
Any idea how to do this?
Upvotes: 19
Views: 80982
Reputation: 2974
You can use Jackson library in case to maintain the order of Json keys. It internally uses LinkedHashMap ( ordered ).
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
The code to remove a field, the removed JsonToken could itself be read if required.
String jsonString = "{\"name\":\"abc\",\"address\":\"add\",\"data\":[\"some 1\",\"some 2\",\"some3 3\"],\"age\":12,\"position\":8810.21}";
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(jsonString);
System.out.println("In original order:"+node.toString());
JsonToken removedToken = ((ObjectNode) node).remove("address").asToken();
System.out.println("Aft removal order:"+node.toString());
ObjectNode implementation uses a LinkedHashMap, which maintains the insertion order:
public ObjectNode(JsonNodeFactory nc) {
super(nc);
_children = new LinkedHashMap<String, JsonNode>();
}
Upvotes: 2
Reputation: 1183
I accomplished it by doing a:
JSONObject(json).put(key, ObjectMapper().writeValueAsString(ObjectMapper().readValue(string, whatever::class)))
So essentially I deserialize a string to an ordered class, then I serialize it again. But then I also had to format that string afterwards to remove escapes.
.replace("\\\"", "\"").replace("\"{", "{").replace("}\"", "}")
You may also have to replace null items as well if you don't want nulls.
Upvotes: -1
Reputation: 17
Go on JSONObject class Change from HashMap() to LinkedHashMap()
/**
* Construct an empty JSONObject.
*/
public JSONObject() {
this.map = new LinkedHashMap();
}
The LinkedHashMap class extends the Hashmap class. This class uses a doubly linked list containing all the entries of the hashed table, in the order in which the keys were inserted in the table: this allows the keys to be "ordered".
Upvotes: 0
Reputation: 776
try this
JSONObject jsonObject = new JSONObject(jsonString) {
/**
* changes the value of JSONObject.map to a LinkedHashMap in order to maintain
* order of keys.
*/
@Override
public JSONObject put(String key, Object value) throws JSONException {
try {
Field map = JSONObject.class.getDeclaredField("map");
map.setAccessible(true);
Object mapValue = map.get(this);
if (!(mapValue instanceof LinkedHashMap)) {
map.set(this, new LinkedHashMap<>());
}
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
return super.put(key, value);
}
};
jsonObject.remove("owner");
jsonString=jsonObject.toString();
Upvotes: 8
Reputation: 448
I have faced the same problem recently and just transitioned all our tests (which expect JSON attributes to be in the same order) to another JSON library:
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>1.3.5</version>
</dependency>
Internally it uses a LinkedHashMap
, which maintains the order of attributes. This library is functionally equivalent to the json.org
library, so I don't see any reason why not use it instead, at least for tests.
Upvotes: 7
Reputation: 127
You can go for the JsonObject provided by the com.google.gson it is nearly the same with the JSONObject by org.json but some different functions. For converting String to Json object and also maintains the order you can use:
Gson gson = new Gson();
JsonObject jsonObject = gson.fromJson(<Json String>, JsonObject.class);
For eg:-
String jsonString = "your json String";
JsonObject jsonObject = gson.fromJson(jsonString, JsonObject.class);
It just maintains the order of the JsonObject from the String.
Upvotes: 5
Reputation: 1883
In JDK 8 and above, We can do it by using nashorn engine, supported in JDK 8. Java 8 support to use js engine to evaluate:
String content = ..json content...
String name = "test";
String result = (String) engine.eval("var json = JSON.stringify("+content+");"
+ "var jsResult = JSON.parse(json);"
+ "jsResult.name = \"" + name + "\";"
+ "jsResult.version = \"1.0\";"
+ "JSON.stringify( jsResult );"
);
Upvotes: -1
Reputation: 259
From Android 20, JSONObject preserves the order as it uses LinkedHashMap to store namevaluepairs. Android 19 and below uses HashMap to store namevaluepairs. So, Android 19 and below doesn't preserve the order. If you are using 20 or above, don't worry, JSONObject will preserve the order. Or else, use JSONArray instead.
Upvotes: 0
Reputation: 54
This is not easy, the main idea is to use LinkedHashMap, either pass in to the constructor (JSONObject(Map map)), or modify bytecode to handle the String parameter (JSONObject(String source)), which is the main use case. I got a solution in oson:
public static JSONObject getJSONObject(String source) {
try {
int lineNumberToReplace = 157;
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.get("org.json.JSONObject");
if (ctClass.isFrozen() || ctClass.isModified()) {
if (source == null) {
return new JSONObject();
} else {
return new JSONObject(source);
}
}
ctClass.stopPruning(true);
CtConstructor declaredConstructor = ctClass.getDeclaredConstructor(new CtClass[] {});
CodeAttribute codeAttribute = declaredConstructor.getMethodInfo().getCodeAttribute();
LineNumberAttribute lineNumberAttribute = (LineNumberAttribute)codeAttribute.getAttribute(LineNumberAttribute.tag);
// Index in bytecode array where the instruction starts
int startPc = lineNumberAttribute.toStartPc(lineNumberToReplace);
// Index in the bytecode array where the following instruction starts
int endPc = lineNumberAttribute.toStartPc(lineNumberToReplace+1);
// Let's now get the bytecode array
byte[] code = codeAttribute.getCode();
for (int i = startPc; i < endPc; i++) {
// change byte to a no operation code
code[i] = CodeAttribute.NOP;
}
declaredConstructor.insertAt(lineNumberToReplace, true, "$0.map = new java.util.LinkedHashMap();");
ctClass.writeFile();
if (source == null) {
return (JSONObject) ctClass.toClass().getConstructor().newInstance();
} else {
return (JSONObject) ctClass.toClass().getConstructor(String.class).newInstance(source);
}
} catch (Exception e) {
//e.printStackTrace();
}
if (source == null) {
return new JSONObject();
} else {
return new JSONObject(source);
}
}
need to include jar file from using mvn
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
Upvotes: 0
Reputation: 1237
If you can edit the server repose then change it to array of JSON objects.
JSON:
[
{PropertyName:"Date of Issue:",PropertyValue:"3/21/2011"},
PropertyName:"Report No:",PropertyValue:"2131196186"},{PropertyName:"Weight:",PropertyValue:"1.00"},
{PropertyName:"Report Type:",PropertyValue:"DG"}
]
And I handled it with JSONArray in client side (Android):
String tempresult="[{PropertyName:"Date of Issue:",PropertyValue:"3/21/2011"},PropertyName:"Report No:",PropertyValue:"2131196186"},PropertyName:"Weight:",PropertyValue:"1.00"},{PropertyName:"Report Type:",PropertyValue:"DG"}]"
JSONArray array = new JSONArray(tempresult);
for (int i = 0; i < array.length(); i++)
{
String key = array.getJSONObject(i).getString("PropertyName");
String value = array.getJSONObject(i).getString("PropertyValue");
rtnObject.put(key.trim(),value.trim()); //rtnObject is LinkedHashMap but can be any other object which can keep order.
}
Upvotes: 1
Reputation: 25039
You can't.
That is why we call it an unordered collection of name/value pairs.
Why you would need to do this, I'm not sure. But if you want ordering, you'll have to use a json array.
Upvotes: 2