JSON In Java。パッケージ名が「org.json」
元ソースは
https://github.com/stleary/JSON-java/issues/822#issuecomment-2105977372
変えたところは
OrderedJSONTokenerを内部クラスではなく別クラスしたところだけ。
あとは全く同じ
修正後のメリット
可読性が向上
OrderedJSONObject は JSON の順序を保持する役割 のみに集中
OrderedJSONTokener は JSON のパースを担当
拡張性が向上
OrderedJSONTokener を OrderedJSONObject 以外のクラスでも再利用可能
保守性が向上
修正時に影響範囲が狭くなる
OrderedJSONObject の責務が明確になり、修正しやすい
OrderedJSONObject.java
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
public class OrderedJSONObject extends JSONObject {
public OrderedJSONObject() {
super();
createOrdered();
}
public OrderedJSONObject(String jsonString) {
this();
fromString(jsonString);
}
public OrderedJSONObject(OrderedJSONTokener tokener) {
this();
fromTokener(tokener);
}
/**
* Use reflection to override the underlying data structure
*/
private void createOrdered() {
try {
Field mapField = JSONObject.class.getDeclaredField("map");
mapField.setAccessible(true);
mapField.set(this, new LinkedHashMap<>());
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException("Unable to preserve JSONObject ordering. Failed to override map.");
}
}
private void fromString(String source) {
OrderedJSONTokener tokener = new OrderedJSONTokener(source);
fromTokener(tokener);
}
private void fromTokener(OrderedJSONTokener tokener) {
if (tokener.nextClean() != '{') {
throw tokener.syntaxError("A JSONObject text must begin with '{'");
}
while (true) {
char c = tokener.nextClean();
switch (c) {
case '\u0000':
throw tokener.syntaxError("A JSONObject text must end with '}'");
case '}':
return;
default:
tokener.back();
String key = tokener.nextValue().toString();
c = tokener.nextClean();
if (c != ':') {
throw tokener.syntaxError("Expected a ':' after a key");
}
this.putOnce(key, tokener.nextValue());
switch (tokener.nextClean()) {
case ',':
case ';':
if (tokener.nextClean() == '}') {
return;
}
tokener.back();
break;
case '}':
return;
default:
throw tokener.syntaxError("Expected a ',' or '}'");
}
}
}
}
@Override
public String toString(int indentSpaces) {
return super.toString(indentSpaces);
}
}
OrderedJSONTokener.java
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
public class OrderedJSONTokener extends JSONTokener {
public OrderedJSONTokener(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 OrderedJSONObject(this);
default:
StringBuilder sb = new StringBuilder();
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
sb.append(c);
c = this.next();
}
this.back();
String string = sb.toString().trim();
if ("".equals(string)) {
throw this.syntaxError("Missing value");
} else {
return JSONObject.stringToValue(string);
}
}
}
}
実行例
// Jsonにする型のString型を定義
String jsonStr = """
{"b":2,"a":1,"c":3}
""";
OrderedJSONObject obj2 = new OrderedJSONObject(jsonStr);
System.out.println(obj2.toString());
// JSONからXMLに変換
String xml = XML.toString(obj2);
System.out.println(xml);
{"b":2,"a":1,"c":3}
<b>2</b><a>1</a><c>3</c>