json

```
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.*;

public class Main {
    public static void main(String[] args) throws Exception {
        // JSON文字列の定義
        String jsonString = "{\n" +
                "\"apiVer1\":\"$$(TEST_NO1)\",\n" +
                "\"apiver2\":\"$$(TEST_NO2 HEAD:11)\",\n" +
                "\"norma\":\"testValue\",\n" +
                "\"apiver\":\"14\",\n" +
                "\"normal2\":\"testValue2\",\n" +
                "\"apiPerson\":{\n" +
                "\"apiVer3\":\"$$(TEST_NO3 HEAD:13)\",\n" +
                "\"kokyaku\":\"$$(kokyakuNo)\",\n" +
                "\"apiVer4\":\"$$(TEST_NO4 HEAD:24)\"\n" +
                "},\n" +
                "\"apiFunds\":{\n" +
                "\"apiVer5\":\"$$(TEST_NO5 HEAD:26)\",\n" +
                "\"apiVer6\":\"$$(TEST_NO6 ERA:yymmdd)\",\n" +
                "\"apiVer7\":\"$$(TEST_NO7 ERA:yymm)\",\n" +
                "\"apiVer8\":\"$$(TEST_NO8 IFNA:0/)\",\n" +
                "\"apiVer9\":\"$$(TEST_NO9 IFNA:0/*)\",\n" +
                "\"apiVer10\":\"$$(TEST_NO10 IFNA:1234567890/66666)\",\n" +
                "\"apiVer11\":\"$$(TEST_NO11 IFNA:7890123/89012345)\"\n" +
                "}\n" +
                "}";

        // テストデータのマップを定義
        Map<String, String> bindDateMap = new HashMap<>();
        bindDateMap.put("TEST_NO1", "test1Value");
        bindDateMap.put("TEST_NO2", "123456789012345");
        bindDateMap.put("TEST_NO3", "123456789012345");
        bindDateMap.put("TEST_NO4", "123456789012345678901234567");
        bindDateMap.put("TEST_NO5", "123456789012345678901234567");
        bindDateMap.put("TEST_NO6", "19940505");
        bindDateMap.put("TEST_NO7", "123456");
        bindDateMap.put("TEST_NO8", "8");  // 空文字を設定
        bindDateMap.put("TEST_NO9", "100");
        bindDateMap.put("TEST_NO10", null);
        bindDateMap.put("TEST_NO11", "200");
        bindDateMap.put("kokyakuNo", "");

        // JSON文字列からキーと対応する加工処理のパターンを抽出
        Map<String, String> extractedKeyPatternMap = extractKeyPatterns(jsonString);
        // 最終的な結果を格納するマップの定義
        Map<String, Object> req = new HashMap<>();

        // 抽出したキーを使って値を加工し、結果マップに格納
        for (Map.Entry<String, String> entry : extractedKeyPatternMap.entrySet()) {
            String jsonKey = entry.getKey();  // JSONのキー
            String patternOrValue = entry.getValue();  // $$()内の加工処理のパターンまたはそのままの値

            if (patternOrValue.startsWith("$$(")) {
                String pattern = patternOrValue.substring(3, patternOrValue.length() - 1); // $$()を取り除く
                String bindKey = pattern.split(" ")[0];  // 加工処理のパターンからテスト番号を取得

                // ①キーが存在しているかどうかチェック
                if (!bindDateMap.containsKey(bindKey)) {
                    throw new RuntimeException("500000エラー: " + bindKey + " がbindDateMapに存在しません");
                }

                // ② 加工処理のパターン内に半角スペースがあるかチェック
                if (pattern.contains(" ")) {
                    String value = bindDateMap.get(bindKey);  // テストデータマップから値を取得
                    if (value == null || value.isEmpty() || "0".equals(value)) { // 空文字チェックの追加
                        value = "";  // 値がnullまたは"0"の場合には空文字を格納
                    }
                    String processedValue = processValue(pattern, value);  // 値を加工
                    addNestedValue(req, jsonKey, processedValue);  // 結果をreqマップに追加
                } else {
                    addNestedValue(req, jsonKey, bindDateMap.get(bindKey));  // 加工処理のパターンなしで値を追加
                }
            } else {
                addNestedValue(req, jsonKey, patternOrValue);  // 値をそのままreqマップに追加
            }
        }

        // reqマップをJSON文字列に変換
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonResultString = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(req);
        System.out.println(jsonResultString);
    }

    /**
     * JSON文字列からキーと加工処理のパターンを抽出するメソッド。
     *
     * @param jsonString 処理するJSON文字列
     * @return キーと加工処理のパターンのマップ
     * @throws Exception JSONパースエラーが発生した場合
     */
    public static Map<String, String> extractKeyPatterns(String jsonString) throws Exception {
        Map<String, String> keyPatternMap = new HashMap<>();
        // JSON文字列をパース
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode rootNode = objectMapper.readTree(jsonString);

        // 再帰的にJSONノードを処理してキーと加工処理のパターンを抽出
        extractPatternsRecursive(rootNode, "", keyPatternMap);

        return keyPatternMap;
    }

    /**
     * 再帰的にJSONノードを処理してキーと加工処理のパターンを抽出するメソッド。
     *
     * @param node JSONノード
     * @param parentKey 親キー
     * @param keyPatternMap キーと加工処理のパターンのマップ
     */
    private static void extractPatternsRecursive(JsonNode node, String parentKey, Map<String, String> keyPatternMap) {
        if (node.isObject()) {
            // JSONオブジェクトの場合、各フィールドを処理
            Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
            while (fields.hasNext()) {
                Map.Entry<String, JsonNode> field = fields.next();
                String currentKey = parentKey.isEmpty() ? field.getKey() : parentKey + "." + field.getKey();
                extractPatternsRecursive(field.getValue(), currentKey, keyPatternMap);
            }
        } else if (node.isTextual()) {
            // テキストノードの場合、加工処理のパターンを抽出
            String text = node.asText();
            Pattern pattern = Pattern.compile("\\$\\$\\((.*?)\\)");
            Matcher matcher = pattern.matcher(text);
            if (matcher.find()) {
                keyPatternMap.put(parentKey, matcher.group(0));  // $$()の加工処理のパターンを含めて取得
            } else {
                keyPatternMap.put(parentKey, text);  // 加工処理のパターンが見つからない場合はそのまま取得
            }
        }
    }

    /**
     * キーに基づいて値を加工するメソッド。
     *
     * @param key 加工処理のパターン
     * @param value 元の値
     * @return 加工された値
     * @throws ParseException 日付のパースエラーが発生した場合
     */
    public static String processValue(String key, String value) throws ParseException {
        if (key.contains("HEAD:")) {
            return processHead(key, value);
        } else if (key.contains("ERA:yymmdd")) {
            // ERA:yymmddの場合、和暦に変換
            return convertToJapaneseEra(value, "yyyyMMdd");
        } else if (key.contains("ERA:yymm")) {
            // ERA:yymmの場合、和暦に変換
            return convertToJapaneseEra(value, "yyyyMM");
        } else if (key.contains("IFNA:")) {
            return processIfna(key, value);
        } else {
            throw new RuntimeException("500000エラー: " + key + " に該当しません");
        }
    }

    /**
     * HEADパターンに基づいて値を加工するメソッド。
     *
     * @param key 加工処理のパターン
     * @param value 元の値
     * @return 加工された値
     */
    public static String processHead(String key, String value) {
        String[] parts = key.split("HEAD:");
        if (parts.length != 2 || !parts[1].matches("\\d+")) {
            throw new RuntimeException("500000エラー: " + key + " のHEADパターンが不正です");
        }
        int headLength = Integer.parseInt(parts[1]);
        if (value.length() <= headLength) {
            return value;  // valueがheadLength以下の場合はそのまま返す
        }
        return value.substring(0, headLength);
    }

    /**
     * IFNAパターンに基づいて値を加工するメソッド。
     *
     * @param key 加工処理のパターン
     * @param value 元の値
     * @return 加工された値
     */
    public static String processIfna(String key, String value) {
        // keyの形式チェック
        if (!key.matches(".*IFNA:(\\d*|\\s*)/(\\d*|\\s*|\\*)")) {
            throw new RuntimeException("500000エラー: " + key + " のIFNAパターンが不正です");
        }

        String[] parts = key.split("IFNA:")[1].split("/");
        if (parts.length != 2) {
            // "IFNA:0/" のようなパターンを許容するための修正
            if (parts.length == 1 && key.endsWith("/")) {
                parts = new String[]{parts[0], ""};
            } else {
                throw new RuntimeException("500000エラー: " + key + " のIFNAパターンが不正です");
            }
        }

        // 空文字の場合には空文字を設定
        String nullValueReplacement = parts[0].isEmpty() ? "" : parts[0];
        String nonNullValueReplacement = parts[1].isEmpty() ? "" : parts[1];

        if (key.contains("/*")) {
            return (value == null || value.isEmpty()) ? nullValueReplacement : value;
        } else {
            return (value == null || value.isEmpty()) ? nullValueReplacement : nonNullValueReplacement;
        }
    }

    /**
     * 西暦を和暦に変換するメソッド。
     *
     * @param date 西暦の日付文字列
     * @param format 日付のフォーマット
     * @return 和暦の日付文字列
     * @throws ParseException 日付のパースエラーが発生した場合
     */
    public static String convertToJapaneseEra(String date, String format) throws ParseException {
        // 数字かつ、桁数が8桁または6桁かをチェック
        if (!date.matches("\\d{6}|\\d{8}")) {
            throw new RuntimeException("500000エラー: 日付の形式が不正です - " + date);
        }

        SimpleDateFormat sdf = new SimpleDateFormat(format);
        Date westernDate = sdf.parse(date);

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(westernDate);

        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH) + 1; // Calendarの月は0から始まるため+1
        int day = calendar.get(Calendar.DAY_OF_MONTH);

        String eraYear;

        if (format.equals("yyyyMMdd")) {
            if (year > 2019 || (year == 2019 && (month > 5 || (month == 5 && day >= 1)))) {
                // 令和 (2019年5月1日から)
                eraYear = String.format("%02d", year - 2018); // 年が1桁の場合に先頭に0を付ける
                return "5" + eraYear + String.format("%02d%02d", month, day);
            } else if (year > 1989 || (year == 1989 && (month > 1 || (month == 1 && day >= 8)))) {
                // 平成 (1989年1月8日から)
                eraYear = String.format("%02d", year - 1988); // 年が1桁の場合に先頭に0を付ける
                return "4" + eraYear + String.format("%02d%02d", month, day);
            } else if (year > 1926 || (year == 1926 && (month > 12 || (month == 12 && day >= 25)))) {
                // 昭和 (1926年12月25日から)
                eraYear = String.format("%02d", year - 1925); // 年が1桁の場合に先頭に0を付ける
                return "3" + eraYear + String.format("%02d%02d", month, day);
            } else if (year > 1912 || (year == 1912 && (month > 7 || (month == 7 && day >= 30)))) {
                // 大正 (1912年7月30日から)
                eraYear = String.format("%02d", year - 1911); // 年が1桁の場合に先頭に0を付ける
                return "2" + eraYear + String.format("%02d%02d", month, day);
            } else if (year > 1868 || (year == 1868 && (month > 1 || (month == 1 && day >= 25)))) {
                // 明治 (1868年1月25日から)
                eraYear = String.format("%02d", year - 1867); // 年が1桁の場合に先頭に0を付ける
                return "1" + eraYear + String.format("%02d%02d", month, day);
            } else {
                // 上記以外の場合、未("0")
                eraYear = "00"; // 年が1桁の場合に先頭に0を付ける
                return "0" + eraYear + String.format("%02d%02d", month, day);
            }
        } else if (format.equals("yyyyMM")) {
            if (year > 2019 || (year == 2019 && month >= 5)) {
                // 令和 (2019年5月から)
                eraYear = String.format("%02d", year - 2018); // 年が1桁の場合に先頭に0を付ける
                return "5" + eraYear + String.format("%02d", month);
            } else if ((year > 1989) || (year == 1989 && month >= 2) || (year == 2019 && month < 5)) {
                // 平成 (1989年2月から2019年4月まで)
                eraYear = String.format("%02d", year - 1988); // 年が1桁の場合に先頭に0を付ける
                return "4" + eraYear + String.format("%02d", month);
            } else if ((year > 1926) || (year == 1927 && month >= 1) || (year == 1989 && month < 2)) {
                // 昭和 (1927年1月から1989年1月まで)
                if (year == 1927 && month == 1) {
                    eraYear = String.format("%02d", year - 1926); // 年が1桁の場合に先頭に0を付ける
                } else {
                    eraYear = String.format("%02d", year - 1926 + 1); // 1927年1月は昭和1年1月とする
                }
                return "3" + eraYear + String.format("%02d", month);
            } else if ((year > 1912) || (year == 1912 && month >= 8) || (year == 1926 && month < 12)) {
                // 大正 (1912年8月から1926年12月まで)
                eraYear = String.format("%02d", year - 1911); // 年が1桁の場合に先頭に0を付ける
                return "2" + eraYear + String.format("%02d", month);
            } else if ((year > 1868) || (year == 1868 && month >= 2) || (year == 1912 && month < 8)) {
                // 明治 (1868年2月から1912年7月まで)
                eraYear = String.format("%02d", year - 1867); // 年が1桁の場合に先頭に0を付ける
                return "1" + eraYear + String.format("%02d", month);
            } else {
                // 上記以外の場合、未("0")
                eraYear = "00"; // 年が1桁の場合に先頭に0を付ける
                return "0" + eraYear + String.format("%02d", month);
            }
        } else {
            throw new RuntimeException("500000エラー: " + format + " は該当しません");
        }
    }

    /**
     * ネストされたキーに基づいて値を設定するメソッド。
     *
     * @param map 設定するマップ
     * @param key ドットで区切られたキー
     * @param value 設定する値
     */
    private static void addNestedValue(Map<String, Object> map, String key, Object value) {
        String[] keys = key.split("\\.");
        Map<String, Object> currentMap = map;
        for (int i = 0; i < keys.length - 1; i++) {
            String k = keys[i];
            if (!currentMap.containsKey(k) || !(currentMap.get(k) instanceof Map)) {
                currentMap.put(k, new HashMap<>());
            }
            currentMap = (Map<String, Object>) currentMap.get(k);
        }
        currentMap.put(keys[keys.length - 1], value);
    }
}

```