JSON In JavaのJSONObjectのカスタマイズ

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

```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

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

実行例

```java
// 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);       
```

```sh
{"b":2,"a":1,"c":3}
<b>2</b><a>1</a><c>3</c>
```