Skip to content

Commit 4ab8810

Browse files
committed
Fix auto adding comma case and update README
1 parent 90bce8c commit 4ab8810

File tree

3 files changed

+72
-36
lines changed

3 files changed

+72
-36
lines changed

README.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,23 @@
1-
# Agent Plugin Container
1+
# LLM JSON auto repair
22

3-
This is the plugin container for agent plugins. It is responsible for loading and managing agent plugins.
4-
Any application that wants to use agent plugins should use this package.
3+
A tiny library to repair JSON string output from LLM. It fixes most of the common issues from the LLM JSON output, eg:
4+
* Remove the ```json``` code block
5+
* Add missing commas
6+
* Add missing double quotes
7+
* Escape special characters \t \n
8+
9+
## Usage
10+
11+
```java
12+
String originalJSON = """
13+
```json
14+
{
15+
"name": "Alice",
16+
"sex": "female"
17+
"address": "123 Andrew Street,
18+
ward 3, district 10"
19+
}
20+
```
21+
""";
22+
String fixedJSON = jsonAutoRepairer.repair(originalJSON);
23+
```

src/main/java/com/cdpn/jsonautorepair/internal/EscapeProcessor.java

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,40 +25,38 @@ public String process() {
2525
private void processQuoteCharacter(char currentChar, int i) {
2626
if (!inQuotes) {
2727
inQuotes = true;
28-
if(!isPreviousCloseQuoteGood(i)) {
29-
escapedJson.append(',');
30-
}
3128
escapedJson.append(currentChar);
3229
return;
3330
}
34-
if(!isValidCloseQuote(i)) {
35-
escapedJson.append('\\');
31+
if(isValidCloseQuote(i)) {
32+
inQuotes = false;
3633
escapedJson.append(currentChar);
3734
return;
3835
}
39-
inQuotes = false;
36+
if(findNextValidChar(i + 1) == '\"') {
37+
int nextValidCharPosition = getNextValidCharPosition(i + 1);
38+
if(isValidCloseQuote(nextValidCharPosition)) {
39+
escapedJson.append('\\');
40+
escapedJson.append(currentChar);
41+
return;
42+
}
43+
inQuotes = false;
44+
escapedJson.append(currentChar);
45+
escapedJson.append(',');
46+
return;
47+
}
48+
escapedJson.append('\\');
4049
escapedJson.append(currentChar);
4150
}
4251

43-
private boolean isPreviousCloseQuoteGood(int position) {
44-
// If the quote is the first character, there is no need to check for a good close quote
45-
if(position == 0) {
46-
return true;
47-
}
48-
// The previous close quote is good when it ends with a comma, or a curly brace,
49-
// or a square bracket, or a colon
50-
for (int i = position - 1; i >= 0; i--) {
52+
private int getNextValidCharPosition(int position) {
53+
for (int i = position; i < inputString.length(); i++) {
5154
char currentChar = inputString.charAt(i);
52-
if (currentChar != ' '
53-
&& currentChar != '\n'
54-
&& currentChar != '\t') {
55-
return currentChar == ','
56-
|| currentChar == '{'
57-
|| currentChar == '['
58-
|| currentChar == ':';
55+
if (currentChar != ' ' && currentChar != '\n' && currentChar != '\t') {
56+
return i;
5957
}
6058
}
61-
return false;
59+
return -1;
6260
}
6361

6462

@@ -82,15 +80,15 @@ private String getEscapeSequence(char currentChar) {
8280
};
8381
}
8482
private boolean isValidCloseQuote(int i) {
85-
char nextValidChar = findNextValidChar(inputString, i + 1);
83+
char nextValidChar = findNextValidChar(i + 1);
8684
return nextValidChar == EOF
8785
|| nextValidChar == ','
8886
|| nextValidChar == '}'
8987
|| nextValidChar == ']'
9088
|| nextValidChar == ':';
9189
}
9290

93-
private char findNextValidChar(String inputString, int position) {
91+
private char findNextValidChar(int position) {
9492
for (int i = position; i < inputString.length(); i++) {
9593
char currentChar = inputString.charAt(i);
9694
if (currentChar != ' ' && currentChar != '\n' && currentChar != '\t') {

src/test/java/com/cdpn/jsonautorepair/JSONAutoRepairerTest.java

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
import org.junit.jupiter.api.BeforeEach;
66
import org.junit.jupiter.api.Test;
77

8-
import static org.junit.jupiter.api.Assertions.assertEquals;
9-
import static org.junit.jupiter.api.Assertions.assertNull;
8+
import static org.junit.jupiter.api.Assertions.*;
109

1110
public class JSONAutoRepairerTest {
1211
private JSONAutoRepairer jsonAutoRepairer;
@@ -78,17 +77,17 @@ public void repair_should_add_quotes_around_unquoted_keys() {
7877
}
7978

8079
@Test
81-
public void repair_should_escape_internal_quote_for_both_key_and_value() {
80+
public void repair_should_escape_internal_quote() {
8281
String originalJSON = """
8382
{
84-
""sentence"": "Alice said: "hello"",
83+
"sentence": "Alice said: "hello"",
8584
"sentiment": "normal"
8685
}
8786
""";
8887

8988
assertEquals(JsonParser.parseString("""
9089
{
91-
"\\"sentence\\"": "Alice said: \\"hello\\"",
90+
"sentence": "Alice said: \\"hello\\"",
9291
"sentiment": "normal"
9392
}
9493
""").toString(),
@@ -115,23 +114,43 @@ public void repair_should_auto_add_comma_before_a_new_start_quote_to_fix_missing
115114
String originalJSON = """
116115
{
117116
"name": "Alice",
118-
"age": 30
119-
"address": "123 Andrew Street,\\n \\tward 3"
117+
"sex": "female"
118+
"address": "123 Andrew Street"
120119
}
121120
""";
122121
assertEquals(JsonParser.parseString("""
123122
{
124123
"name": "Alice",
125-
"age": 30,
126-
"address": "123 Andrew Street,\\n \\tward 3"
124+
"sex": "female",
125+
"address": "123 Andrew Street"
127126
}
128127
""").toString() , jsonAutoRepairer.repair(originalJSON));
129128

129+
assertEquals(JsonParser.parseString("""
130+
["apple", "banana", "cherry"]
131+
""").toString() , jsonAutoRepairer.repair("[\"apple\" \"banana\" \"cherry\"]"));
132+
130133
}
131134

132135
@Test
133136
public void repair_should_return_null_when_cannot_fix_the_JSON() {
134137
assertNull(jsonAutoRepairer.repair("This is not a valid {} JSON"));
135138
}
136139

140+
@Test
141+
public void demo_case() {
142+
String originalJSON = """
143+
```json
144+
{
145+
"name": "Alice",
146+
"sex": "female"
147+
"address": "123 Andrew Street,
148+
ward 3, district 10"
149+
}
150+
```
151+
""";
152+
String fixedJSON = jsonAutoRepairer.repair(originalJSON);
153+
assertNotNull(fixedJSON);
154+
}
155+
137156
}

0 commit comments

Comments
 (0)