0
0

json

Posted at
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);
    }
}

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0