Skip to content

Commit

Permalink
Support path compression
Browse files Browse the repository at this point in the history
  • Loading branch information
jianwu committed Jul 6, 2023
1 parent 1b9febe commit 19bb640
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class JSONCoder {
public static JSONCoder get() { return getGlobal(); }

public static String stringify(Object obj) { return get().encode(obj); }
public static String stringify(Object obj, int indentFact) { return encode(obj, JSONCoderOption.ofIndentFactor(indentFact)); }
public static <T> T parse(String str, Class<T> cls) { return get().decode(str, cls); }

@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.jsonex.jsoncoder.coder.*;
import org.jsonex.jsoncoder.fieldTransformer.FieldTransformer;
import org.jsonex.jsoncoder.fieldTransformer.FieldTransformer.FieldInfo;
import org.jsonex.treedoc.TDNode;
import org.jsonex.treedoc.json.TDJSONOption;
import org.slf4j.Logger;

Expand Down Expand Up @@ -137,7 +138,7 @@ private static SimpleDateFormat buildDateFormat(String format, TimeZone timeZone
@Getter private final List<EqualsWrapper<?>> equalsWrapper = new ArrayList<>();

// JSON coder config
@Getter @Setter private TDJSONOption jsonOption = new TDJSONOption();
@Getter @Setter private TDJSONOption jsonOption = TDJSONOption.ofDefaultRootType(TDNode.Type.SIMPLE);

public enum LogLevel {
OFF { public void log(Logger log, String msg, Throwable e) { /* Noop */ }},
Expand Down
55 changes: 53 additions & 2 deletions JSONEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,13 @@ line2`,
<tr><th>Feature</th><th>JSONex</th><th>JSON</th></tr>
<tr>
<td>optional json top level braces - object</td>
<td><code>a:1</code></td>
<td><code>a:1,b:2</code></td>
<td>

```json
{
"a":1
"a": 1,
"b": 2
}
```
</td>
Expand All @@ -95,6 +96,20 @@ line2`,
```
</td>
</tr>
<tr>
<td>optional json top level braces - array of objects</td>
<td><code>{a:1},{b:2},c</code></td>
<td>

```json
[
{"a": 1},
{"b": 2},
"c"
]
```
</td>
</tr>


<tr>
Expand Down Expand Up @@ -130,6 +145,42 @@ line2`,
```
</td>
</tr>
<tr>
<td>Optional Quotes</td>
<td><code>{a: value1, b: value2}</code></td>
<td>

```json
{"a": "value1", "b": "value2"}
```
</td>
</tr>
<tr>
<td>Commas are allowed for last element</td>
<td><code>{a: 1, b: 2, }</code></td>
<td>

```json
{"a": 1, "b": 2}
```
</td>
</tr>
<tr>
<td>Multi-line value</td>
<td><pre>{
a:
`abc
line2`,
b: 2
}</pre></td>
<td>

```json
{"a": "abc\nline2", "b": 2}
```
</td>
</tr>

</table>


Expand Down
12 changes: 8 additions & 4 deletions treedoc/src/main/java/org/jsonex/treedoc/json/TDJSONOption.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.jsonex.core.type.Nullable;
import org.jsonex.treedoc.TDNode;

import java.net.URI;
Expand Down Expand Up @@ -43,8 +44,11 @@ public enum TextType {OPERATOR, KEY, STRING, NON_STRING}
URI uri;

// Used for JSONParser
/** In case there's no enclosed '[' of '{' on the root level, the default type. */
TDNode.Type defaultRootType = TDNode.Type.SIMPLE;
/**
* In case there's no enclosed '[' of '{' on the root level, the default type.
* By default, it will try to interpreter as a single value (either map, if there's ":", or a simple value.)
*/
@Nullable TDNode.Type defaultRootType;

// Used for JSONWriter
int indentFactor;
Expand Down Expand Up @@ -113,7 +117,7 @@ public TDJSONOption setDeliminatorArray(String start, String end) {
@Setter(AccessLevel.NONE) @Getter(AccessLevel.NONE) Collection<String> _termKeyStrs;

public void buildTerms() {
_termValue = "\n\r" + deliminatorObjectStart; // support tree with a type in the form of "type{attr1:val1}"
_termValue = "\n\r" + deliminatorKey + deliminatorObjectStart; // support tree with a type in the form of "type{attr1:val1}", key1:key2:type{att1:val1}
_termKey = deliminatorObjectStart + deliminatorObjectEnd + deliminatorArrayStart;
_termValueStrs = new ArrayList<>();
_termKeyStrs = new ArrayList<>();
Expand All @@ -129,7 +133,7 @@ public void buildTerms() {
else
_termKeyStrs.add(deliminatorKey);

_termValueInMap = _termValue + deliminatorObjectEnd;
_termValueInMap = _termValue + deliminatorObjectEnd + deliminatorArrayEnd; // It's possible object end is omitted for path compression. e.g [a:b:c]
_termValueInArray = _termValue + deliminatorArrayEnd;
}
}
20 changes: 16 additions & 4 deletions treedoc/src/main/java/org/jsonex/treedoc/json/TDJSONParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public TDNode parseAll(CharSource src, TDJSONOption opt) {
TreeDoc doc = TreeDoc.ofArray();
int docId = 0;
while(src.skipSpacesAndReturnsAndCommas())
TDJSONParser.get().parse(src, new TDJSONOption().setDocId(docId++), doc.getRoot().createChild());
TDJSONParser.get().parse(src, TDJSONOption.ofDefaultRootType(TDNode.Type.MAP).setDocId(docId++), doc.getRoot().createChild());
return doc.getRoot();
}

Expand All @@ -64,7 +64,7 @@ public TDNode parse(CharSource src, TDJSONOption opt, TDNode node, boolean isRoo
if (contains(opt.deliminatorArrayStart, c))
return parseArray(src, opt, node, true);

if (isRoot) {
if (isRoot && opt.defaultRootType != null) {
switch (opt.defaultRootType) {
case MAP:
return parseMap(src, opt, node, false);
Expand All @@ -82,17 +82,29 @@ public TDNode parse(CharSource src, TDJSONOption opt, TDNode node, boolean isRoo
return node.setValue(sb.toString());
}

if (isRoot && opt.defaultRootType == TDNode.Type.SIMPLE) {
return node.setValue(ClassUtil.toSimpleObject(src.readUntil("\r\n")));
}

String term = opt._termValue;
if (node.getParent() != null) // parent.type can either be ARRAY or MAP.
term = node.getParent().getType() == TDNode.Type.ARRAY ? opt._termValueInArray : opt._termValueInMap;

String str = src.readUntil(term, opt._termValueStrs).trim();
node.setValue(ClassUtil.toSimpleObject(str));
if (!src.isEof() && contains(opt.deliminatorKey, src.peek())) { // it's a path compression such as: a:b:c,d:e -> {a: {b: c}}
src.skip();
node.setType(TDNode.Type.MAP);
parse(src, opt, node.createChild(str), false);
return node;
}

if (!src.isEof() && contains(opt.deliminatorObjectStart, src.peek())) {
// A value with type in the form of `type{attr1:val1:attr2:val2}
node.createChild(opt.KEY_TYPE).setValue(node.getValue());
node.createChild(opt.KEY_TYPE).setValue(str);
return parseMap(src, opt, node, true);
}
// A simple value
node.setValue(ClassUtil.toSimpleObject(str));
return node;
} finally {
node.setEnd(src.getBookmark());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,13 @@ public class TDJsonParserTest {
}

private final static String EXPECTED_STREAM_MERGE_RESULT =
"[{a: 1, obj: {$id: '1_0'}, ref: {$ref: '#1_0'}}, {b: 2, obj: {$id: '1_1'}, ref: {$ref: '#1_1'}}, 'a:1', 'b:2']";
"[{a: 1, obj: {$id: '1_0'}, ref: {$ref: '#1_0'}}, {b: 2, obj: {$id: '1_1'}, ref: {$ref: '#1_1'}}, {a: 1, b: 2}]";
@Test public void testStream() {
ReaderCharSource reader = new ReaderCharSource(loadResource(this.getClass(), "stream.json"));
List<TDNode> nodes = new ArrayList<>();
while(reader.skipSpacesAndReturnsAndCommas())
nodes.add(TDJSONParser.get().parse(reader));
while(reader.skipSpacesAndReturnsAndCommas()) {
nodes.add(TDJSONParser.get().parse(reader, TDJSONOption.ofDefaultRootType(TDNode.Type.MAP)));
}
TDNode node = TreeDoc.merge(nodes).getRoot();
log.info("testStream=" + node.toString());
assertEquals("1", node.getChild(1).getKey());
Expand Down Expand Up @@ -215,7 +216,7 @@ public class TDJsonParserTest {
.setDeliminatorArray("<", ">");
TDNode node = TDJSONParser.get().parse(str, opt);
assertEquals("{a: 'va', c: {d: 23, strs: ['a', 'b']}}", node.toString());
assertEquals("(a=\"va\";c=(d=23;strs=<\"a\";\"b\">))", TDJSONWriter.get().writeAsString(node, opt.setAlwaysQuoteName(false)));
assertEquals("(a=`va`;c=(d=23;strs=<`a`;`b`>))", TDJSONWriter.get().writeAsString(node, opt.setAlwaysQuoteName(false).setQuoteChar('`')));
}

private static void parseWithException(String str, String expectedError) {
Expand Down Expand Up @@ -250,9 +251,24 @@ private static void parseWithException(String str, String expectedError) {
@Test public void testParseObjectToString() {
TestCls test = new TestCls("va", new TestCls1(23, new String[]{"a", "b"}));
String str = test.toString(); // TDJsonParserTest.TestCls(a=va, c=TDJsonParserTest.TestCls1(d=23, strs=[a, b]))
log.info("testParseObjectToString: str=" + str);
TDNode node = TDJSONParser.get().parse(str, new TDJSONOption().setDeliminatorObject("(", ")").setDeliminatorKey("=") );
assertEquals("{$type:\"TDJsonParserTest.TestCls\",a:\"va\",c:{$type:\"TDJsonParserTest.TestCls1\",d:23,strs:[\"a\",\"b\"]}}", TDJSONWriter.get().writeAsString(node, new TDJSONOption().setAlwaysQuoteName(false)));
TDJSONOption opt = new TDJSONOption().setDeliminatorObject("(", ")").setDeliminatorKey("=");
testParse(str, opt, "{$type:\"TDJsonParserTest.TestCls\",a:\"va\",c:{$type:\"TDJsonParserTest.TestCls1\",d:23,strs:[\"a\",\"b\"]}}");
}

@Test public void testParsePathCompression() {
TDJSONOption opt = new TDJSONOption();
testParse("a:b:123", opt, "{a:{b:123}}");
testParse("[h:i, j:k]", opt, "[{h:'i'},{j:'k'}]");
testParse("a:b:{e:123, f:g:[h:i, j:k]}", opt, "{a:{b:{e:123,f:{g:[{h:'i'},{j:'k'}]}}}}");
testParse("a:b:123,c:d:456", opt, "{a:{b:123}}"); // Default, read a single map
testParse("a:b:123,c:d:456", opt.setDefaultRootType(TDNode.Type.MAP), "{a:{b:123},c:{d:456}}");
testParse("a:b:123,c:d:456", opt.setDefaultRootType(TDNode.Type.ARRAY), "[{a:{b:123}},{c:{d:456}}]");
testParse("a:b:123,a:d:456", opt.setDefaultRootType(TDNode.Type.MAP), "{a:[{b:123},{d:456}]}");
}

private void testParse(String str, TDJSONOption opt, String expectedJson) {
TDNode node = TDJSONParser.get().parse(str, opt);
assertEquals(expectedJson, TDJSONWriter.get().writeAsString(node, new TDJSONOption().setAlwaysQuoteName(false).setQuoteChar('\'')));
}

@Data
Expand Down

0 comments on commit 19bb640

Please sign in to comment.