001package gudusoft.gsqlparser.util.json;
002
003public class JSONUtil {
004
005    /**
006     * Efficiently extracts a top-level string field value from a JSON object
007     * without full parsing. Only scans the top-level keys of the JSON structure,
008     * skipping nested objects and arrays by tracking brace/bracket depth.
009     * Returns {@code null} if the field is not found or its value is not a string.
010     * <p>
011     * This avoids the cost of building a full object tree via
012     * {@code JSON.parseObject()} when only a few top-level fields are needed.
013     */
014    public static String extractTopLevelStringField(String json, String fieldName) {
015        if (json == null || json.isEmpty()) {
016            return null;
017        }
018        String trimmed = json.trim();
019        int idx = 0;
020        int len = trimmed.length();
021        int braceDepth = 0;
022        int bracketDepth = 0;
023
024        while (idx < len && trimmed.charAt(idx) != '{') {
025            idx++;
026        }
027        if (idx >= len) {
028            return null;
029        }
030        idx++;
031        braceDepth = 1;
032
033        while (idx < len && braceDepth > 0) {
034            char c = trimmed.charAt(idx);
035
036            if (c == '"') {
037                int stringStart = idx;
038                idx++;
039                while (idx < len) {
040                    c = trimmed.charAt(idx);
041                    if (c == '\\') {
042                        idx += 2;
043                    } else if (c == '"') {
044                        idx++;
045                        break;
046                    } else {
047                        idx++;
048                    }
049                }
050
051                if (braceDepth == 1 && bracketDepth == 0) {
052                    String key = trimmed.substring(stringStart + 1, idx - 1);
053                    if (key.equals(fieldName)) {
054                        while (idx < len && (trimmed.charAt(idx) == ':' || Character.isWhitespace(trimmed.charAt(idx)))) {
055                            idx++;
056                        }
057                        if (idx < len && trimmed.charAt(idx) == '"') {
058                            int valStart = idx + 1;
059                            idx++;
060                            while (idx < len) {
061                                c = trimmed.charAt(idx);
062                                if (c == '\\') {
063                                    idx += 2;
064                                } else if (c == '"') {
065                                    return trimmed.substring(valStart, idx);
066                                } else {
067                                    idx++;
068                                }
069                            }
070                        }
071                        return null;
072                    }
073                }
074            } else if (c == '{') {
075                braceDepth++;
076                idx++;
077            } else if (c == '}') {
078                braceDepth--;
079                idx++;
080            } else if (c == '[') {
081                bracketDepth++;
082                idx++;
083            } else if (c == ']') {
084                bracketDepth--;
085                idx++;
086            } else {
087                idx++;
088            }
089        }
090        return null;
091    }
092
093    /**
094     * Efficiently extracts the raw text value of a top-level field from a JSON object
095     * without full parsing. Works for any JSON value type: string, number, boolean,
096     * and null. Only scans the top-level keys of the JSON structure, skipping nested
097     * objects and arrays by tracking brace/bracket depth.
098     * Returns {@code null} if the field is not found.
099     * <p>
100     * For string values, the surrounding quotes are stripped. For all other value
101     * types (number, boolean, null), the raw text is returned as-is.
102     */
103    public static String extractTopLevelFieldValue(String json, String fieldName) {
104        if (json == null || json.isEmpty()) {
105            return null;
106        }
107        String trimmed = json.trim();
108        int idx = 0;
109        int len = trimmed.length();
110        int braceDepth = 0;
111        int bracketDepth = 0;
112
113        while (idx < len && trimmed.charAt(idx) != '{') {
114            idx++;
115        }
116        if (idx >= len) {
117            return null;
118        }
119        idx++;
120        braceDepth = 1;
121
122        while (idx < len && braceDepth > 0) {
123            char c = trimmed.charAt(idx);
124
125            if (c == '"') {
126                int stringStart = idx;
127                idx++;
128                while (idx < len) {
129                    c = trimmed.charAt(idx);
130                    if (c == '\\') {
131                        idx += 2;
132                    } else if (c == '"') {
133                        idx++;
134                        break;
135                    } else {
136                        idx++;
137                    }
138                }
139
140                if (braceDepth == 1 && bracketDepth == 0) {
141                    String key = trimmed.substring(stringStart + 1, idx - 1);
142                    if (key.equals(fieldName)) {
143                        while (idx < len && (trimmed.charAt(idx) == ':' || Character.isWhitespace(trimmed.charAt(idx)))) {
144                            idx++;
145                        }
146                        if (idx >= len) {
147                            return null;
148                        }
149                        if (trimmed.charAt(idx) == '"') {
150                            int valStart = idx + 1;
151                            idx++;
152                            while (idx < len) {
153                                c = trimmed.charAt(idx);
154                                if (c == '\\') {
155                                    idx += 2;
156                                } else if (c == '"') {
157                                    return trimmed.substring(valStart, idx);
158                                } else {
159                                    idx++;
160                                }
161                            }
162                            return null;
163                        }
164                        int valStart = idx;
165                        while (idx < len) {
166                            c = trimmed.charAt(idx);
167                            if (c == ',' || c == '}' || c == ']' || Character.isWhitespace(c)) {
168                                break;
169                            }
170                            idx++;
171                        }
172                        String raw = trimmed.substring(valStart, idx);
173                        return raw.isEmpty() ? null : raw;
174                    }
175                }
176            } else if (c == '{') {
177                braceDepth++;
178                idx++;
179            } else if (c == '}') {
180                braceDepth--;
181                idx++;
182            } else if (c == '[') {
183                bracketDepth++;
184                idx++;
185            } else if (c == ']') {
186                bracketDepth--;
187                idx++;
188            } else {
189                idx++;
190            }
191        }
192        return null;
193    }
194}