diff --git a/CHANGELOG.md b/CHANGELOG.md
index 47c23039..c93b3bdd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,24 @@
+# 0.9.22
+
+* fix #167 parse Object.class follow jackson convention. fixed the case of 1.0 parsed as int not double.
+* fix #154 support map integer key
+* fix #152
+
+# 0.9.21
+
+breaking changes
+
+* fix #149 parse Object.class follow jackson convention
+
+bug fixes
+
+* fix #145 add Any.registerEncoders
+* merge #143
+
+# 0.9.20
+
+* fix #136, field with only getter is also considered as java bean property, so that @JsonIgnore on the field should be propagated to getter
+
# 0.9.19
* changed cfg class name to hashcode based
* fix static codegen
diff --git a/pom.xml b/pom.xml
index 5a936199..4840503f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
com.jsoniter
- 0.9.21-SNAPSHOT
+ 0.9.24-SNAPSHOT
jsoniter
json iterator
jsoniter (json-iterator) is fast and flexible JSON parser available in Java and Go
@@ -46,38 +46,38 @@
org.javassist
javassist
- 3.21.0-GA
+ 3.22.0-GA
true
com.fasterxml.jackson.core
jackson-annotations
- 2.8.5
+ 2.9.5
true
com.fasterxml.jackson.core
jackson-databind
- 2.8.5
+ 2.9.5
true
com.google.code.gson
gson
- 2.2.4
+ 2.8.3
true
org.openjdk.jmh
jmh-core
- 1.17.3
+ 1.20
test
org.openjdk.jmh
jmh-generator-annprocess
- 1.17.3
+ 1.20
test
@@ -119,7 +119,7 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.6.0
+ 3.7.0
1.6
1.6
@@ -129,7 +129,7 @@
org.apache.maven.plugins
maven-source-plugin
- 2.2.1
+ 3.0.1
attach-sources
@@ -142,7 +142,7 @@
org.apache.maven.plugins
maven-javadoc-plugin
- 2.9.1
+ 3.0.0
attach-javadocs
@@ -158,7 +158,7 @@
org.apache.maven.plugins
maven-gpg-plugin
- 1.5
+ 1.6
sign-artifacts
@@ -172,7 +172,7 @@
org.sonatype.plugins
nexus-staging-maven-plugin
- 1.6.7
+ 1.6.8
true
ossrh
@@ -194,7 +194,7 @@
org.apache.maven.plugins
maven-surefire-plugin
- 2.19.1
+ 2.21.0
methods
1
diff --git a/src/main/java/com/jsoniter/Codegen.java b/src/main/java/com/jsoniter/Codegen.java
index 0b2922b9..7cf7318d 100644
--- a/src/main/java/com/jsoniter/Codegen.java
+++ b/src/main/java/com/jsoniter/Codegen.java
@@ -96,16 +96,12 @@ private static void addPlaceholderDecoderToSupportRecursiveStructure(final Strin
public Object decode(JsonIterator iter) throws IOException {
Decoder decoder = JsoniterSpi.getDecoder(cacheKey);
if (this == decoder) {
- for(int i = 0; i < 30; i++) {
+ for(int i = 0; (i < 30) && (this == decoder); i++) {
decoder = JsoniterSpi.getDecoder(cacheKey);
- if (this == decoder) {
- try {
- Thread.sleep(1000);
+ try {
+ Thread.sleep(1000);
} catch (InterruptedException e) {
throw new JsonException(e);
- }
- } else {
- break;
}
}
if (this == decoder) {
@@ -171,7 +167,7 @@ private static Type chooseImpl(Type type) {
if (keyType == Object.class) {
keyType = String.class;
}
- DefaultMapKeyDecoder.registerOrGetExisting(keyType);
+ MapKeyDecoders.registerOrGetExisting(keyType);
return GenericsHelper.createParameterizedType(new Type[]{keyType, valueType}, null, clazz);
}
if (implClazz != null) {
diff --git a/src/main/java/com/jsoniter/CodegenAccess.java b/src/main/java/com/jsoniter/CodegenAccess.java
index 5bb972d1..bc4f3cb7 100644
--- a/src/main/java/com/jsoniter/CodegenAccess.java
+++ b/src/main/java/com/jsoniter/CodegenAccess.java
@@ -142,9 +142,12 @@ public static final Slice readSlice(JsonIterator iter) throws IOException {
}
public static final Object readMapKey(String cacheKey, JsonIterator iter) throws IOException {
- Slice encodedMapKey = readObjectFieldAsSlice(iter);
- MapKeyDecoder mapKeyDecoder = JsoniterSpi.getMapKeyDecoder(cacheKey);
- return mapKeyDecoder.decode(encodedMapKey);
+ Decoder mapKeyDecoder = JsoniterSpi.getMapKeyDecoder(cacheKey);
+ Object key = mapKeyDecoder.decode(iter);
+ if (IterImpl.nextToken(iter) != ':') {
+ throw iter.reportError("readMapKey", "expect :");
+ }
+ return key;
}
final static boolean skipWhitespacesWithoutLoadMore(JsonIterator iter) throws IOException {
diff --git a/src/main/java/com/jsoniter/DefaultMapKeyDecoder.java b/src/main/java/com/jsoniter/DefaultMapKeyDecoder.java
deleted file mode 100644
index 786a2270..00000000
--- a/src/main/java/com/jsoniter/DefaultMapKeyDecoder.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.jsoniter;
-
-import com.jsoniter.spi.*;
-
-import java.io.IOException;
-import java.lang.reflect.Type;
-
-class DefaultMapKeyDecoder implements MapKeyDecoder {
-
- public static MapKeyDecoder registerOrGetExisting(Type mapKeyType) {
- String cacheKey = JsoniterSpi.getMapKeyDecoderCacheKey(mapKeyType);
- MapKeyDecoder mapKeyDecoder = JsoniterSpi.getMapKeyDecoder(cacheKey);
- if (null != mapKeyDecoder) {
- return mapKeyDecoder;
- }
- mapKeyDecoder = new DefaultMapKeyDecoder(TypeLiteral.create(mapKeyType));
- JsoniterSpi.addNewMapDecoder(cacheKey, mapKeyDecoder);
- return mapKeyDecoder;
- }
-
- private final TypeLiteral mapKeyTypeLiteral;
-
- private DefaultMapKeyDecoder(TypeLiteral mapKeyTypeLiteral) {
- this.mapKeyTypeLiteral = mapKeyTypeLiteral;
- }
-
- @Override
- public Object decode(Slice encodedMapKey) {
- JsonIterator iter = JsonIteratorPool.borrowJsonIterator();
- iter.reset(encodedMapKey);
- try {
- return iter.read(mapKeyTypeLiteral);
- } catch (IOException e) {
- throw new JsonException(e);
- } finally {
- JsonIteratorPool.returnJsonIterator(iter);
- }
- }
-}
diff --git a/src/main/java/com/jsoniter/IterImpl.java b/src/main/java/com/jsoniter/IterImpl.java
index 8a5071b6..ad779fd8 100644
--- a/src/main/java/com/jsoniter/IterImpl.java
+++ b/src/main/java/com/jsoniter/IterImpl.java
@@ -5,9 +5,15 @@
import com.jsoniter.spi.Slice;
import java.io.IOException;
+import java.math.BigInteger;
class IterImpl {
+ private static BigInteger maxLong = BigInteger.valueOf(Long.MAX_VALUE);
+ private static BigInteger minLong = BigInteger.valueOf(Long.MIN_VALUE);
+ private static BigInteger maxInt = BigInteger.valueOf(Integer.MAX_VALUE);
+ private static BigInteger minInt = BigInteger.valueOf(Integer.MIN_VALUE);
+
public static final int readObjectFieldAsHash(JsonIterator iter) throws IOException {
if (readByte(iter) != '"') {
if (nextToken(iter) != '"') {
@@ -53,7 +59,7 @@ final static void skipArray(JsonIterator iter) throws IOException {
case '[': // If open symbol, increase level
level++;
break;
- case ']': // If close symbol, increase level
+ case ']': // If close symbol, decrease level
level--;
// If we have returned to the original level, we're done
@@ -79,7 +85,7 @@ final static void skipObject(JsonIterator iter) throws IOException {
case '{': // If open symbol, increase level
level++;
break;
- case '}': // If close symbol, increase level
+ case '}': // If close symbol, decrease level
level--;
// If we have returned to the original level, we're done
@@ -119,7 +125,7 @@ final static boolean skipNumber(JsonIterator iter) throws IOException {
boolean dotFound = false;
for (int i = iter.head; i < iter.tail; i++) {
byte c = iter.buf[i];
- if (c == '.') {
+ if (c == '.' || c == 'e' || c == 'E') {
dotFound = true;
continue;
}
@@ -387,10 +393,6 @@ static final int readInt(final JsonIterator iter, final byte c) throws IOExcepti
static final long readLong(final JsonIterator iter, final byte c) throws IOException {
long ind = IterImplNumber.intDigits[c];
- if (ind == 0) {
- IterImplForStreaming.assertNotLeadingZero(iter);
- return 0;
- }
if (ind == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
throw iter.reportError("readLong", "expect 0~9");
}
diff --git a/src/main/java/com/jsoniter/IterImplForStreaming.java b/src/main/java/com/jsoniter/IterImplForStreaming.java
index 9cbf7485..2cef3a16 100644
--- a/src/main/java/com/jsoniter/IterImplForStreaming.java
+++ b/src/main/java/com/jsoniter/IterImplForStreaming.java
@@ -71,7 +71,7 @@ final static void skipArray(JsonIterator iter) throws IOException {
case '[': // If open symbol, increase level
level++;
break;
- case ']': // If close symbol, increase level
+ case ']': // If close symbol, decrease level
level--;
// If we have returned to the original level, we're done
@@ -101,7 +101,7 @@ final static void skipObject(JsonIterator iter) throws IOException {
case '{': // If open symbol, increase level
level++;
break;
- case '}': // If close symbol, increase level
+ case '}': // If close symbol, decrease level
level--;
// If we have returned to the original level, we're done
@@ -147,7 +147,8 @@ final static void skipString(JsonIterator iter) throws IOException {
throw iter.reportError("skipString", "incomplete string");
}
if (escaped) {
- iter.head = 1; // skip the first char as last char is \
+ // TODO add unit test to prove/verify bug
+ iter.head += 1; // skip the first char as last char is \
}
} else {
iter.head = end;
@@ -179,7 +180,7 @@ final static boolean skipNumber(JsonIterator iter) throws IOException {
for (; ; ) {
for (int i = iter.head; i < iter.tail; i++) {
byte c = iter.buf[i];
- if (c == '.') {
+ if (c == '.' || c == 'e' || c == 'E') {
dotFound = true;
continue;
}
@@ -274,19 +275,19 @@ public final static boolean loadMore(JsonIterator iter) throws IOException {
}
private static boolean keepSkippedBytesThenRead(JsonIterator iter) throws IOException {
- int n;
- int offset;
- if (iter.skipStartedAt == 0 || iter.skipStartedAt < iter.tail / 2) {
- byte[] newBuf = new byte[iter.buf.length * 2];
- offset = iter.tail - iter.skipStartedAt;
- System.arraycopy(iter.buf, iter.skipStartedAt, newBuf, 0, offset);
- iter.buf = newBuf;
- n = iter.in.read(iter.buf, offset, iter.buf.length - offset);
- } else {
- offset = iter.tail - iter.skipStartedAt;
- System.arraycopy(iter.buf, iter.skipStartedAt, iter.buf, 0, offset);
- n = iter.in.read(iter.buf, offset, iter.buf.length - offset);
- }
+ int offset = iter.tail - iter.skipStartedAt;
+ byte[] srcBuffer = iter.buf;
+ // Check there is no unused buffer capacity
+ if ((getUnusedBufferByteCount(iter)) == 0) {
+ // If auto expand buffer enabled, then create larger buffer
+ if (iter.autoExpandBufferStep > 0) {
+ iter.buf = new byte[iter.buf.length + iter.autoExpandBufferStep];
+ } else {
+ throw iter.reportError("loadMore", String.format("buffer is full and autoexpansion is disabled. tail: [%s] skipStartedAt: [%s]", iter.tail, iter.skipStartedAt));
+ }
+ }
+ System.arraycopy(srcBuffer, iter.skipStartedAt, iter.buf, 0, offset);
+ int n = iter.in.read(iter.buf, offset, iter.buf.length - offset);
iter.skipStartedAt = 0;
if (n < 1) {
if (n == -1) {
@@ -301,6 +302,11 @@ private static boolean keepSkippedBytesThenRead(JsonIterator iter) throws IOExce
return true;
}
+ private static int getUnusedBufferByteCount(JsonIterator iter) {
+ // Get bytes from 0 to skipStart + from tail till end
+ return iter.buf.length - iter.tail + iter.skipStartedAt;
+ }
+
final static byte readByte(JsonIterator iter) throws IOException {
if (iter.head == iter.tail) {
if (!loadMore(iter)) {
@@ -539,15 +545,32 @@ static int readIntSlowPath(final JsonIterator iter, int value) throws IOExceptio
public static final double readDoubleSlowPath(final JsonIterator iter) throws IOException {
try {
- String numberAsStr = readNumber(iter);
- return Double.valueOf(numberAsStr);
+ numberChars numberChars = readNumber(iter);
+ if (numberChars.charsLength == 0 && iter.whatIsNext() == ValueType.STRING) {
+ String possibleInf = iter.readString();
+ if ("infinity".equals(possibleInf)) {
+ return Double.POSITIVE_INFINITY;
+ }
+ if ("-infinity".equals(possibleInf)) {
+ return Double.NEGATIVE_INFINITY;
+ }
+ throw iter.reportError("readDoubleSlowPath", "expect number but found string: " + possibleInf);
+ }
+ return Double.valueOf(new String(numberChars.chars, 0, numberChars.charsLength));
} catch (NumberFormatException e) {
throw iter.reportError("readDoubleSlowPath", e.toString());
}
}
- public static final String readNumber(final JsonIterator iter) throws IOException {
+ static class numberChars {
+ char[] chars;
+ int charsLength;
+ boolean dotFound;
+ }
+
+ public static final numberChars readNumber(final JsonIterator iter) throws IOException {
int j = 0;
+ boolean dotFound = false;
for (; ; ) {
for (int i = iter.head; i < iter.tail; i++) {
if (j == iter.reusableChars.length) {
@@ -557,11 +580,13 @@ public static final String readNumber(final JsonIterator iter) throws IOExceptio
}
byte c = iter.buf[i];
switch (c) {
- case '-':
- case '+':
case '.':
case 'e':
case 'E':
+ dotFound = true;
+ // fallthrough
+ case '-':
+ case '+':
case '0':
case '1':
case '2':
@@ -576,12 +601,20 @@ public static final String readNumber(final JsonIterator iter) throws IOExceptio
break;
default:
iter.head = i;
- return new String(iter.reusableChars, 0, j);
+ numberChars numberChars = new numberChars();
+ numberChars.chars = iter.reusableChars;
+ numberChars.charsLength = j;
+ numberChars.dotFound = dotFound;
+ return numberChars;
}
}
if (!IterImpl.loadMore(iter)) {
iter.head = iter.tail;
- return new String(iter.reusableChars, 0, j);
+ numberChars numberChars = new numberChars();
+ numberChars.chars = iter.reusableChars;
+ numberChars.charsLength = j;
+ numberChars.dotFound = dotFound;
+ return numberChars;
}
}
}
@@ -616,8 +649,7 @@ static final int readInt(final JsonIterator iter, final byte c) throws IOExcepti
static void assertNotLeadingZero(JsonIterator iter) throws IOException {
try {
- byte nextByte = IterImpl.readByte(iter);
- iter.unreadByte();
+ byte nextByte = iter.buf[iter.head];
int ind2 = IterImplNumber.intDigits[nextByte];
if (ind2 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
return;
diff --git a/src/main/java/com/jsoniter/IterImplNumber.java b/src/main/java/com/jsoniter/IterImplNumber.java
index adc2bf69..c521cdd5 100644
--- a/src/main/java/com/jsoniter/IterImplNumber.java
+++ b/src/main/java/com/jsoniter/IterImplNumber.java
@@ -91,8 +91,17 @@ public static final int readInt(final JsonIterator iter) throws IOException {
public static final long readLong(JsonIterator iter) throws IOException {
byte c = IterImpl.nextToken(iter);
if (c == '-') {
- return IterImpl.readLong(iter, IterImpl.readByte(iter));
+ c = IterImpl.readByte(iter);
+ if (IterImplNumber.intDigits[c] == 0) {
+ IterImplForStreaming.assertNotLeadingZero(iter);
+ return 0;
+ }
+ return IterImpl.readLong(iter, c);
} else {
+ if (IterImplNumber.intDigits[c] == 0) {
+ IterImplForStreaming.assertNotLeadingZero(iter);
+ return 0;
+ }
long val = IterImpl.readLong(iter, c);
if (val == Long.MIN_VALUE) {
throw iter.reportError("readLong", "value is too large for long");
diff --git a/src/main/java/com/jsoniter/IterImplString.java b/src/main/java/com/jsoniter/IterImplString.java
index 573cd2d1..c6a0b452 100644
--- a/src/main/java/com/jsoniter/IterImplString.java
+++ b/src/main/java/com/jsoniter/IterImplString.java
@@ -59,7 +59,7 @@ public static final String readString(JsonIterator iter) throws IOException {
IterImpl.skipFixedBytes(iter, 3);
return null;
}
- iter.reportError("readString", "expect string or null, but " + (char) c);
+ throw iter.reportError("readString", "expect string or null, but " + (char) c);
}
int j = parse(iter);
return new String(iter.reusableChars, 0, j);
diff --git a/src/main/java/com/jsoniter/JsonIterator.java b/src/main/java/com/jsoniter/JsonIterator.java
index 0f93c4e2..c198540b 100644
--- a/src/main/java/com/jsoniter/JsonIterator.java
+++ b/src/main/java/com/jsoniter/JsonIterator.java
@@ -21,6 +21,9 @@ public class JsonIterator implements Closeable {
final static ValueType[] valueTypes = new ValueType[256];
InputStream in;
byte[] buf;
+ // Whenever buf is not large enough new one is created with size of
+ // buf.length + autoExpandBufferStep. Set to < 1 to disable auto expanding.
+ int autoExpandBufferStep;
int head;
int tail;
int skipStartedAt = -1; // skip should keep bytes starting at this pos
@@ -60,13 +63,22 @@ private JsonIterator(InputStream in, byte[] buf, int head, int tail) {
this.tail = tail;
}
+ private JsonIterator(InputStream in, byte[] buf, int autoExpandBufferStep) {
+ this(in, buf, 0, 0);
+ this.autoExpandBufferStep = autoExpandBufferStep;
+ }
+
public JsonIterator() {
this(null, new byte[0], 0, 0);
}
public static JsonIterator parse(InputStream in, int bufSize) {
+ return parse(in, bufSize, bufSize);
+ }
+
+ public static JsonIterator parse(InputStream in, int bufSize, int autoExpandBufferStep) {
enableStreamingSupport();
- return new JsonIterator(in, new byte[bufSize], 0, 0);
+ return new JsonIterator(in, new byte[bufSize], autoExpandBufferStep);
}
public static JsonIterator parse(byte[] buf) {
@@ -190,7 +202,8 @@ public final boolean readArray() throws IOException {
}
public String readNumberAsString() throws IOException {
- return IterImplForStreaming.readNumber(this);
+ IterImplForStreaming.numberChars numberChars = IterImplForStreaming.readNumber(this);
+ return new String(numberChars.chars, 0, numberChars.charsLength);
}
public static interface ReadArrayCallback {
@@ -239,7 +252,8 @@ public final BigDecimal readBigDecimal() throws IOException {
if (valueType != ValueType.NUMBER) {
throw reportError("readBigDecimal", "not number");
}
- return new BigDecimal(IterImplForStreaming.readNumber(this));
+ IterImplForStreaming.numberChars numberChars = IterImplForStreaming.readNumber(this);
+ return new BigDecimal(numberChars.chars, 0, numberChars.charsLength);
}
public final BigInteger readBigInteger() throws IOException {
@@ -252,7 +266,8 @@ public final BigInteger readBigInteger() throws IOException {
if (valueType != ValueType.NUMBER) {
throw reportError("readBigDecimal", "not number");
}
- return new BigInteger(IterImplForStreaming.readNumber(this));
+ IterImplForStreaming.numberChars numberChars = IterImplForStreaming.readNumber(this);
+ return new BigInteger(new String(numberChars.chars, 0, numberChars.charsLength));
}
public final Any readAny() throws IOException {
@@ -288,7 +303,21 @@ public final Object read() throws IOException {
case STRING:
return readString();
case NUMBER:
- return readDouble();
+ IterImplForStreaming.numberChars numberChars = IterImplForStreaming.readNumber(this);
+ String numberStr = new String(numberChars.chars, 0, numberChars.charsLength);
+ Double number = Double.valueOf(numberStr);
+ if (numberChars.dotFound) {
+ return number;
+ }
+ double doubleNumber = number;
+ if (doubleNumber == Math.floor(doubleNumber) && !Double.isInfinite(doubleNumber)) {
+ long longNumber = Long.valueOf(numberStr);
+ if (longNumber <= Integer.MAX_VALUE && longNumber >= Integer.MIN_VALUE) {
+ return (int) longNumber;
+ }
+ return longNumber;
+ }
+ return number;
case NULL:
IterImpl.skipFixedBytes(this, 4);
return null;
diff --git a/src/main/java/com/jsoniter/JsonIteratorPool.java b/src/main/java/com/jsoniter/JsonIteratorPool.java
index 00f88e63..f0324d02 100644
--- a/src/main/java/com/jsoniter/JsonIteratorPool.java
+++ b/src/main/java/com/jsoniter/JsonIteratorPool.java
@@ -22,6 +22,7 @@ public static JsonIterator borrowJsonIterator() {
public static void returnJsonIterator(JsonIterator iter) {
iter.configCache = null;
+ iter.existingObject = null;
if (slot1.get() == null) {
slot1.set(iter);
return;
diff --git a/src/main/java/com/jsoniter/MapKeyDecoders.java b/src/main/java/com/jsoniter/MapKeyDecoders.java
new file mode 100644
index 00000000..0bb75a30
--- /dev/null
+++ b/src/main/java/com/jsoniter/MapKeyDecoders.java
@@ -0,0 +1,77 @@
+package com.jsoniter;
+
+import com.jsoniter.spi.*;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+class MapKeyDecoders {
+
+ public static Decoder registerOrGetExisting(Type mapKeyType) {
+ String cacheKey = JsoniterSpi.getMapKeyDecoderCacheKey(mapKeyType);
+ Decoder mapKeyDecoder = JsoniterSpi.getMapKeyDecoder(cacheKey);
+ if (null != mapKeyDecoder) {
+ return mapKeyDecoder;
+ }
+ mapKeyDecoder = createMapKeyDecoder(mapKeyType);
+ JsoniterSpi.addNewMapDecoder(cacheKey, mapKeyDecoder);
+ return mapKeyDecoder;
+ }
+
+ private static Decoder createMapKeyDecoder(Type mapKeyType) {
+ if (String.class == mapKeyType) {
+ return new StringKeyDecoder();
+ }
+ if (mapKeyType instanceof Class && ((Class) mapKeyType).isEnum()) {
+ return new EnumKeyDecoder((Class) mapKeyType);
+ }
+ Decoder decoder = CodegenImplNative.NATIVE_DECODERS.get(mapKeyType);
+ if (decoder != null) {
+ return new NumberKeyDecoder(decoder);
+ }
+ throw new JsonException("can not decode map key type: " + mapKeyType);
+ }
+
+ private static class StringKeyDecoder implements Decoder {
+
+ @Override
+ public Object decode(JsonIterator iter) throws IOException {
+ return iter.readString();
+ }
+ }
+
+ private static class EnumKeyDecoder implements Decoder {
+
+ private final Class enumClass;
+
+ private EnumKeyDecoder(Class enumClass) {
+ this.enumClass = enumClass;
+ }
+
+ @Override
+ public Object decode(JsonIterator iter) throws IOException {
+ return iter.read(enumClass);
+ }
+ }
+
+ private static class NumberKeyDecoder implements Decoder {
+
+ private final Decoder decoder;
+
+ private NumberKeyDecoder(Decoder decoder) {
+ this.decoder = decoder;
+ }
+
+ @Override
+ public Object decode(JsonIterator iter) throws IOException {
+ if (IterImpl.nextToken(iter) != '"') {
+ throw iter.reportError("decode number map key", "expect \"");
+ }
+ Object key = decoder.decode(iter);
+ if (IterImpl.nextToken(iter) != '"') {
+ throw iter.reportError("decode number map key", "expect \"");
+ }
+ return key;
+ }
+ }
+}
diff --git a/src/main/java/com/jsoniter/ReflectionMapDecoder.java b/src/main/java/com/jsoniter/ReflectionMapDecoder.java
index 5010371c..7e5f220a 100644
--- a/src/main/java/com/jsoniter/ReflectionMapDecoder.java
+++ b/src/main/java/com/jsoniter/ReflectionMapDecoder.java
@@ -11,7 +11,7 @@ class ReflectionMapDecoder implements Decoder {
private final Constructor ctor;
private final Decoder valueTypeDecoder;
- private final MapKeyDecoder mapKeyDecoder;
+ private final Decoder mapKeyDecoder;
public ReflectionMapDecoder(Class clazz, Type[] typeArgs) {
try {
@@ -20,11 +20,7 @@ public ReflectionMapDecoder(Class clazz, Type[] typeArgs) {
throw new JsonException(e);
}
Type keyType = typeArgs[0];
- if (keyType == String.class) {
- mapKeyDecoder = null;
- } else {
- mapKeyDecoder = DefaultMapKeyDecoder.registerOrGetExisting(keyType);
- }
+ mapKeyDecoder = MapKeyDecoders.registerOrGetExisting(keyType);
TypeLiteral valueTypeLiteral = TypeLiteral.create(typeArgs[1]);
valueTypeDecoder = Codegen.getDecoder(valueTypeLiteral.getDecoderCacheKey(), typeArgs[1]);
}
@@ -59,11 +55,10 @@ private Object decode_(JsonIterator iter) throws Exception {
}
private Object readMapKey(JsonIterator iter) throws IOException {
- if (mapKeyDecoder == null) {
- return CodegenAccess.readObjectFieldAsString(iter);
- } else {
- Slice mapKey = CodegenAccess.readObjectFieldAsSlice(iter);
- return mapKeyDecoder.decode(mapKey);
+ Object key = mapKeyDecoder.decode(iter);
+ if (':' != IterImpl.nextToken(iter)) {
+ throw iter.reportError("readMapKey", "expect :");
}
+ return key;
}
}
diff --git a/src/main/java/com/jsoniter/any/Any.java b/src/main/java/com/jsoniter/any/Any.java
index e9dfe90e..8159f1c2 100644
--- a/src/main/java/com/jsoniter/any/Any.java
+++ b/src/main/java/com/jsoniter/any/Any.java
@@ -8,6 +8,8 @@
import com.jsoniter.spi.TypeLiteral;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.*;
public abstract class Any implements Iterable {
@@ -174,6 +176,14 @@ public final double toDouble(Object... keys) {
public abstract double toDouble();
+ public final BigInteger toBigInteger(Object ...keys) { return get(keys).toBigInteger(); }
+
+ public abstract BigInteger toBigInteger();
+
+ public final BigDecimal toBigDecimal(Object ...keys) { return get(keys).toBigDecimal(); }
+
+ public abstract BigDecimal toBigDecimal();
+
public final String toString(Object... keys) {
return get(keys).toString();
}
diff --git a/src/main/java/com/jsoniter/any/ArrayAny.java b/src/main/java/com/jsoniter/any/ArrayAny.java
index 55704b89..50d5c174 100644
--- a/src/main/java/com/jsoniter/any/ArrayAny.java
+++ b/src/main/java/com/jsoniter/any/ArrayAny.java
@@ -4,6 +4,8 @@
import com.jsoniter.output.JsonStream;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -115,4 +117,14 @@ public float toFloat() {
public double toDouble() {
return val.size();
}
+
+ @Override
+ public BigInteger toBigInteger() {
+ return BigInteger.valueOf(val.size());
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return BigDecimal.valueOf(val.size());
+ }
}
diff --git a/src/main/java/com/jsoniter/any/ArrayLazyAny.java b/src/main/java/com/jsoniter/any/ArrayLazyAny.java
index cc452968..13983641 100644
--- a/src/main/java/com/jsoniter/any/ArrayLazyAny.java
+++ b/src/main/java/com/jsoniter/any/ArrayLazyAny.java
@@ -6,6 +6,8 @@
import com.jsoniter.spi.TypeLiteral;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -65,6 +67,16 @@ public double toDouble() {
return size();
}
+ @Override
+ public BigInteger toBigInteger() {
+ return BigInteger.valueOf(size());
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return BigDecimal.valueOf(size());
+ }
+
@Override
public int size() {
fillCache();
@@ -194,7 +206,11 @@ private class LazyIterator implements Iterator {
public LazyIterator() {
index = 0;
- next = fillCacheUntil(index);
+ try {
+ next = fillCacheUntil(index);
+ } catch (IndexOutOfBoundsException e) {
+ next = null;
+ }
}
@Override
diff --git a/src/main/java/com/jsoniter/any/ArrayWrapperAny.java b/src/main/java/com/jsoniter/any/ArrayWrapperAny.java
index fa2fb99b..f5693663 100644
--- a/src/main/java/com/jsoniter/any/ArrayWrapperAny.java
+++ b/src/main/java/com/jsoniter/any/ArrayWrapperAny.java
@@ -5,6 +5,8 @@
import java.io.IOException;
import java.lang.reflect.Array;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -54,6 +56,16 @@ public double toDouble() {
return size();
}
+ @Override
+ public BigInteger toBigInteger() {
+ return BigInteger.valueOf(size());
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return BigDecimal.valueOf(size());
+ }
+
@Override
public String toString() {
if (cache == null) {
diff --git a/src/main/java/com/jsoniter/any/DoubleAny.java b/src/main/java/com/jsoniter/any/DoubleAny.java
index 9cd4c31f..64bcb88d 100644
--- a/src/main/java/com/jsoniter/any/DoubleAny.java
+++ b/src/main/java/com/jsoniter/any/DoubleAny.java
@@ -4,6 +4,8 @@
import com.jsoniter.output.JsonStream;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
class DoubleAny extends Any {
@@ -48,6 +50,16 @@ public double toDouble() {
return val;
}
+ @Override
+ public BigInteger toBigInteger() {
+ return BigInteger.valueOf((long) val);
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return BigDecimal.valueOf(val);
+ }
+
@Override
public String toString() {
return String.valueOf(val);
diff --git a/src/main/java/com/jsoniter/any/DoubleLazyAny.java b/src/main/java/com/jsoniter/any/DoubleLazyAny.java
index 1cc496cf..edc32774 100644
--- a/src/main/java/com/jsoniter/any/DoubleLazyAny.java
+++ b/src/main/java/com/jsoniter/any/DoubleLazyAny.java
@@ -6,6 +6,8 @@
import com.jsoniter.ValueType;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
class DoubleLazyAny extends LazyAny {
@@ -57,6 +59,16 @@ public double toDouble() {
return cache;
}
+ @Override
+ public BigInteger toBigInteger() {
+ return new BigInteger(toString());
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return new BigDecimal(toString());
+ }
+
private void fillCache() {
if (!isCached) {
JsonIterator iter = parse();
diff --git a/src/main/java/com/jsoniter/any/FalseAny.java b/src/main/java/com/jsoniter/any/FalseAny.java
index a516e96e..6d7a7288 100644
--- a/src/main/java/com/jsoniter/any/FalseAny.java
+++ b/src/main/java/com/jsoniter/any/FalseAny.java
@@ -4,6 +4,8 @@
import com.jsoniter.output.JsonStream;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
class FalseAny extends Any {
@@ -44,6 +46,16 @@ public double toDouble() {
return 0;
}
+ @Override
+ public BigInteger toBigInteger() {
+ return BigInteger.ZERO;
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return BigDecimal.ZERO;
+ }
+
@Override
public String toString() {
return "false";
diff --git a/src/main/java/com/jsoniter/any/FloatAny.java b/src/main/java/com/jsoniter/any/FloatAny.java
index b6d29f03..7063f321 100644
--- a/src/main/java/com/jsoniter/any/FloatAny.java
+++ b/src/main/java/com/jsoniter/any/FloatAny.java
@@ -5,6 +5,8 @@
import com.jsoniter.spi.TypeLiteral;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
class FloatAny extends Any {
@@ -49,6 +51,16 @@ public double toDouble() {
return val;
}
+ @Override
+ public BigInteger toBigInteger() {
+ return BigInteger.valueOf((long) val);
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return BigDecimal.valueOf(val);
+ }
+
@Override
public String toString() {
return String.valueOf(val);
diff --git a/src/main/java/com/jsoniter/any/IntAny.java b/src/main/java/com/jsoniter/any/IntAny.java
index ff408443..fbb5f074 100644
--- a/src/main/java/com/jsoniter/any/IntAny.java
+++ b/src/main/java/com/jsoniter/any/IntAny.java
@@ -4,6 +4,8 @@
import com.jsoniter.output.JsonStream;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
class IntAny extends Any {
@@ -48,6 +50,16 @@ public double toDouble() {
return val;
}
+ @Override
+ public BigInteger toBigInteger() {
+ return BigInteger.valueOf(val);
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return BigDecimal.valueOf(val);
+ }
+
@Override
public String toString() {
return String.valueOf(val);
diff --git a/src/main/java/com/jsoniter/any/LazyAny.java b/src/main/java/com/jsoniter/any/LazyAny.java
index 246fca76..d088241e 100644
--- a/src/main/java/com/jsoniter/any/LazyAny.java
+++ b/src/main/java/com/jsoniter/any/LazyAny.java
@@ -68,7 +68,7 @@ public final T as(TypeLiteral typeLiteral) {
}
public String toString() {
- return new String(data, head, tail - head);
+ return new String(data, head, tail - head).trim();
}
protected final JsonIterator parse() {
diff --git a/src/main/java/com/jsoniter/any/ListWrapperAny.java b/src/main/java/com/jsoniter/any/ListWrapperAny.java
index 7e0e9ca9..44345148 100644
--- a/src/main/java/com/jsoniter/any/ListWrapperAny.java
+++ b/src/main/java/com/jsoniter/any/ListWrapperAny.java
@@ -4,6 +4,8 @@
import com.jsoniter.output.JsonStream;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -53,6 +55,16 @@ public double toDouble() {
return size();
}
+ @Override
+ public BigInteger toBigInteger() {
+ return BigInteger.valueOf(size());
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return BigDecimal.valueOf(size());
+ }
+
@Override
public String toString() {
if (cache == null) {
diff --git a/src/main/java/com/jsoniter/any/LongAny.java b/src/main/java/com/jsoniter/any/LongAny.java
index 3245b93e..76f683eb 100644
--- a/src/main/java/com/jsoniter/any/LongAny.java
+++ b/src/main/java/com/jsoniter/any/LongAny.java
@@ -4,6 +4,8 @@
import com.jsoniter.output.JsonStream;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
class LongAny extends Any {
@@ -48,6 +50,16 @@ public double toDouble() {
return val;
}
+ @Override
+ public BigInteger toBigInteger() {
+ return BigInteger.valueOf(val);
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return BigDecimal.valueOf(val);
+ }
+
@Override
public String toString() {
return String.valueOf(val);
diff --git a/src/main/java/com/jsoniter/any/LongLazyAny.java b/src/main/java/com/jsoniter/any/LongLazyAny.java
index 3ec38134..3a60dc74 100644
--- a/src/main/java/com/jsoniter/any/LongLazyAny.java
+++ b/src/main/java/com/jsoniter/any/LongLazyAny.java
@@ -6,6 +6,8 @@
import com.jsoniter.ValueType;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
class LongLazyAny extends LazyAny {
@@ -57,6 +59,16 @@ public double toDouble() {
return cache;
}
+ @Override
+ public BigInteger toBigInteger() {
+ return new BigInteger(toString());
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return new BigDecimal(toString());
+ }
+
private void fillCache() {
if (!isCached) {
JsonIterator iter = parse();
diff --git a/src/main/java/com/jsoniter/any/MapWrapperAny.java b/src/main/java/com/jsoniter/any/MapWrapperAny.java
index 92a8cc98..5897ba1e 100644
--- a/src/main/java/com/jsoniter/any/MapWrapperAny.java
+++ b/src/main/java/com/jsoniter/any/MapWrapperAny.java
@@ -4,6 +4,8 @@
import com.jsoniter.output.JsonStream;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -54,6 +56,16 @@ public double toDouble() {
return size();
}
+ @Override
+ public BigInteger toBigInteger() {
+ return BigInteger.valueOf(size());
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return BigDecimal.valueOf(size());
+ }
+
@Override
public String toString() {
if (cache == null) {
diff --git a/src/main/java/com/jsoniter/any/NotFoundAny.java b/src/main/java/com/jsoniter/any/NotFoundAny.java
index 0722aec6..1a5439ea 100644
--- a/src/main/java/com/jsoniter/any/NotFoundAny.java
+++ b/src/main/java/com/jsoniter/any/NotFoundAny.java
@@ -5,6 +5,8 @@
import com.jsoniter.spi.JsonException;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.Arrays;
class NotFoundAny extends Any {
@@ -81,6 +83,16 @@ public double toDouble() {
return 0;
}
+ @Override
+ public BigInteger toBigInteger() {
+ return BigInteger.ZERO;
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return BigDecimal.ZERO;
+ }
+
@Override
public String toString() {
return "";
diff --git a/src/main/java/com/jsoniter/any/NullAny.java b/src/main/java/com/jsoniter/any/NullAny.java
index d17e3bd2..e086a34b 100644
--- a/src/main/java/com/jsoniter/any/NullAny.java
+++ b/src/main/java/com/jsoniter/any/NullAny.java
@@ -4,6 +4,8 @@
import com.jsoniter.output.JsonStream;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
class NullAny extends Any {
@@ -44,6 +46,16 @@ public double toDouble() {
return 0;
}
+ @Override
+ public BigInteger toBigInteger() {
+ return BigInteger.ZERO;
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return BigDecimal.ZERO;
+ }
+
@Override
public void writeTo(JsonStream stream) throws IOException {
stream.writeNull();
diff --git a/src/main/java/com/jsoniter/any/ObjectAny.java b/src/main/java/com/jsoniter/any/ObjectAny.java
index 4532c6f6..009a4f13 100644
--- a/src/main/java/com/jsoniter/any/ObjectAny.java
+++ b/src/main/java/com/jsoniter/any/ObjectAny.java
@@ -4,6 +4,8 @@
import com.jsoniter.output.JsonStream;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -67,6 +69,16 @@ public double toDouble() {
return size();
}
+ @Override
+ public BigInteger toBigInteger() {
+ return BigInteger.valueOf(size());
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return BigDecimal.valueOf(size());
+ }
+
@Override
public String toString() {
return JsonStream.serialize(this);
diff --git a/src/main/java/com/jsoniter/any/ObjectLazyAny.java b/src/main/java/com/jsoniter/any/ObjectLazyAny.java
index 3aaf1b28..1fb389b6 100644
--- a/src/main/java/com/jsoniter/any/ObjectLazyAny.java
+++ b/src/main/java/com/jsoniter/any/ObjectLazyAny.java
@@ -6,6 +6,8 @@
import com.jsoniter.spi.TypeLiteral;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -67,6 +69,16 @@ public double toDouble() {
return size();
}
+ @Override
+ public BigInteger toBigInteger() {
+ return BigInteger.valueOf(size());
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return BigDecimal.valueOf(size());
+ }
+
@Override
public int size() {
fillCache();
diff --git a/src/main/java/com/jsoniter/any/StringAny.java b/src/main/java/com/jsoniter/any/StringAny.java
index db0412b1..ccd9c090 100644
--- a/src/main/java/com/jsoniter/any/StringAny.java
+++ b/src/main/java/com/jsoniter/any/StringAny.java
@@ -4,6 +4,8 @@
import com.jsoniter.output.JsonStream;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
class StringAny extends Any {
@@ -77,6 +79,16 @@ public double toDouble() {
return Double.valueOf(val);
}
+ @Override
+ public BigInteger toBigInteger() {
+ return new BigInteger(val);
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return new BigDecimal(val);
+ }
+
@Override
public String toString() {
return val;
diff --git a/src/main/java/com/jsoniter/any/StringLazyAny.java b/src/main/java/com/jsoniter/any/StringLazyAny.java
index 79d33f88..4e9f3bab 100644
--- a/src/main/java/com/jsoniter/any/StringLazyAny.java
+++ b/src/main/java/com/jsoniter/any/StringLazyAny.java
@@ -7,6 +7,8 @@
import com.jsoniter.spi.JsonException;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
class StringLazyAny extends LazyAny {
private final static String FALSE = "false";
@@ -103,6 +105,16 @@ public double toDouble() {
}
}
+ @Override
+ public BigInteger toBigInteger() {
+ return new BigInteger(toString());
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return new BigDecimal(toString());
+ }
+
@Override
public String toString() {
fillCache();
diff --git a/src/main/java/com/jsoniter/any/TrueAny.java b/src/main/java/com/jsoniter/any/TrueAny.java
index 6163d0cd..2511f4f2 100644
--- a/src/main/java/com/jsoniter/any/TrueAny.java
+++ b/src/main/java/com/jsoniter/any/TrueAny.java
@@ -4,6 +4,8 @@
import com.jsoniter.output.JsonStream;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
class TrueAny extends Any {
@@ -44,6 +46,16 @@ public double toDouble() {
return 1;
}
+ @Override
+ public BigInteger toBigInteger() {
+ return BigInteger.ONE;
+ }
+
+ @Override
+ public BigDecimal toBigDecimal() {
+ return BigDecimal.ONE;
+ }
+
@Override
public String toString() {
return "true";
diff --git a/src/main/java/com/jsoniter/extra/Base64Support.java b/src/main/java/com/jsoniter/extra/Base64Support.java
index 16c60dfb..676178f7 100644
--- a/src/main/java/com/jsoniter/extra/Base64Support.java
+++ b/src/main/java/com/jsoniter/extra/Base64Support.java
@@ -11,7 +11,7 @@
import java.io.IOException;
/**
- * byte[] <=> base64
+ * byte[] <=> base64
*/
public class Base64Support {
private static boolean enabled;
diff --git a/src/main/java/com/jsoniter/output/Codegen.java b/src/main/java/com/jsoniter/output/Codegen.java
index 569e8feb..7680244d 100644
--- a/src/main/java/com/jsoniter/output/Codegen.java
+++ b/src/main/java/com/jsoniter/output/Codegen.java
@@ -79,7 +79,7 @@ private static synchronized Encoder gen(final String cacheKey, Type type) {
}
ClassInfo classInfo = new ClassInfo(type);
if (Map.class.isAssignableFrom(classInfo.clazz) && classInfo.typeArgs.length > 1) {
- DefaultMapKeyEncoder.registerOrGetExisting(classInfo.typeArgs[0]);
+ MapKeyEncoders.registerOrGetExisting(classInfo.typeArgs[0]);
}
if (mode == EncodingMode.REFLECTION_MODE) {
encoder = ReflectionEncoderFactory.create(classInfo);
diff --git a/src/main/java/com/jsoniter/output/CodegenAccess.java b/src/main/java/com/jsoniter/output/CodegenAccess.java
index 8b9ebfce..913df649 100644
--- a/src/main/java/com/jsoniter/output/CodegenAccess.java
+++ b/src/main/java/com/jsoniter/output/CodegenAccess.java
@@ -53,8 +53,8 @@ public static void writeVal(String cacheKey, double obj, JsonStream stream) thro
}
public static void writeMapKey(String cacheKey, Object mapKey, JsonStream stream) throws IOException {
- String encodedMapKey = JsoniterSpi.getMapKeyEncoder(cacheKey).encode(mapKey);
- stream.writeVal(encodedMapKey);
+ Encoder mapKeyEncoder = JsoniterSpi.getMapKeyEncoder(cacheKey);
+ mapKeyEncoder.encode(mapKey, stream);
}
public static void writeStringWithoutQuote(String obj, JsonStream stream) throws IOException {
diff --git a/src/main/java/com/jsoniter/output/CodegenImplMap.java b/src/main/java/com/jsoniter/output/CodegenImplMap.java
index 245d27c7..006817d3 100644
--- a/src/main/java/com/jsoniter/output/CodegenImplMap.java
+++ b/src/main/java/com/jsoniter/output/CodegenImplMap.java
@@ -13,13 +13,12 @@ public static CodegenResult genMap(String cacheKey, ClassInfo classInfo) {
if (cacheKey.endsWith("__value_not_nullable")) {
isCollectionValueNullable = false;
}
- Type keyType = String.class;
+ Type keyType = Object.class;
Type valueType = Object.class;
if (typeArgs.length == 2) {
keyType = typeArgs[0];
valueType = typeArgs[1];
}
- String mapCacheKey = JsoniterSpi.getMapKeyEncoderCacheKey(keyType);
CodegenResult ctx = new CodegenResult();
ctx.append("public static void encode_(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException {");
ctx.append("if (obj == null) { stream.writeNull(); return; }");
@@ -36,16 +35,7 @@ public static CodegenResult genMap(String cacheKey, ClassInfo classInfo) {
} else {
ctx.append("stream.writeObjectStart(); stream.writeIndention();");
}
- if (keyType == String.class) {
- ctx.append("stream.writeVal((java.lang.String)entry.getKey());");
- } else {
- ctx.append(String.format("com.jsoniter.output.CodegenAccess.writeMapKey(\"%s\", entry.getKey(), stream);", mapCacheKey));
- }
- if (noIndention) {
- ctx.append("stream.write(':');");
- } else {
- ctx.append("stream.write((byte)':', (byte)' ');");
- }
+ genWriteMapKey(ctx, keyType, noIndention);
if (isCollectionValueNullable) {
ctx.append("if (entry.getValue() == null) { stream.writeNull(); } else {");
CodegenImplNative.genWriteOp(ctx, "entry.getValue()", valueType, true);
@@ -60,16 +50,7 @@ public static CodegenResult genMap(String cacheKey, ClassInfo classInfo) {
} else {
ctx.append("stream.writeMore();");
}
- if (keyType == String.class) {
- ctx.append("stream.writeVal((java.lang.String)entry.getKey());");
- } else {
- ctx.append(String.format("com.jsoniter.output.CodegenAccess.writeMapKey(\"%s\", entry.getKey(), stream);", mapCacheKey));
- }
- if (noIndention) {
- ctx.append("stream.write(':');");
- } else {
- ctx.append("stream.write((byte)':', (byte)' ');");
- }
+ genWriteMapKey(ctx, keyType, noIndention);
if (isCollectionValueNullable) {
ctx.append("if (entry.getValue() == null) { stream.writeNull(); } else {");
CodegenImplNative.genWriteOp(ctx, "entry.getValue()", valueType, true);
@@ -86,4 +67,26 @@ public static CodegenResult genMap(String cacheKey, ClassInfo classInfo) {
ctx.append("}");
return ctx;
}
+
+ private static void genWriteMapKey(CodegenResult ctx, Type keyType, boolean noIndention) {
+ if (keyType == Object.class) {
+ ctx.append("stream.writeObjectField(entry.getKey());");
+ return;
+ }
+ if (keyType == String.class) {
+ ctx.append("stream.writeVal((java.lang.String)entry.getKey());");
+ } else if (CodegenImplNative.NATIVE_ENCODERS.containsKey(keyType)) {
+ ctx.append("stream.write('\"');");
+ ctx.append(String.format("stream.writeVal((%s)entry.getKey());", CodegenImplNative.getTypeName(keyType)));
+ ctx.append("stream.write('\"');");
+ } else {
+ String mapCacheKey = JsoniterSpi.getMapKeyEncoderCacheKey(keyType);
+ ctx.append(String.format("com.jsoniter.output.CodegenAccess.writeMapKey(\"%s\", entry.getKey(), stream);", mapCacheKey));
+ }
+ if (noIndention) {
+ ctx.append("stream.write(':');");
+ } else {
+ ctx.append("stream.write((byte)':', (byte)' ');");
+ }
+ }
}
diff --git a/src/main/java/com/jsoniter/output/DefaultMapKeyEncoder.java b/src/main/java/com/jsoniter/output/DefaultMapKeyEncoder.java
deleted file mode 100644
index 52357495..00000000
--- a/src/main/java/com/jsoniter/output/DefaultMapKeyEncoder.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.jsoniter.output;
-
-import com.jsoniter.spi.*;
-
-import java.lang.reflect.Type;
-
-class DefaultMapKeyEncoder implements MapKeyEncoder {
-
- public static MapKeyEncoder registerOrGetExisting(Type mapKeyType) {
- String cacheKey = JsoniterSpi.getMapKeyEncoderCacheKey(mapKeyType);
- MapKeyEncoder mapKeyEncoder = JsoniterSpi.getMapKeyEncoder(cacheKey);
- if (null != mapKeyEncoder) {
- return mapKeyEncoder;
- }
- mapKeyEncoder = new DefaultMapKeyEncoder();
- JsoniterSpi.addNewMapEncoder(cacheKey, mapKeyEncoder);
- return mapKeyEncoder;
- }
-
- @Override
- public String encode(Object mapKey) {
- return mapKey.toString();
- }
-}
diff --git a/src/main/java/com/jsoniter/output/JsonStream.java b/src/main/java/com/jsoniter/output/JsonStream.java
index d1e33670..7886bc05 100644
--- a/src/main/java/com/jsoniter/output/JsonStream.java
+++ b/src/main/java/com/jsoniter/output/JsonStream.java
@@ -331,6 +331,20 @@ public final void writeObjectField(String field) throws IOException {
}
}
+ public final void writeObjectField(Object key) throws IOException {
+ Encoder encoder = MapKeyEncoders.registerOrGetExisting(key.getClass());
+ writeObjectField(key, encoder);
+ }
+
+ public final void writeObjectField(Object key, Encoder keyEncoder) throws IOException {
+ keyEncoder.encode(key, this);
+ if (indention > 0) {
+ write((byte) ':', (byte) ' ');
+ } else {
+ write(':');
+ }
+ }
+
public final void writeObjectEnd() throws IOException {
int indentionStep = currentConfig().indentionStep();
writeIndention(indentionStep);
@@ -428,52 +442,59 @@ public static void serialize(TypeLiteral typeLiteral, Object obj, OutputStream o
}
public static void serialize(Type type, Object obj, OutputStream out) {
- JsonStream stream = JsonStreamPool.borrowJsonStream();
- try {
- try {
- stream.reset(out);
- stream.writeVal(type, obj);
- } finally {
- stream.close();
- }
- } catch (IOException e) {
- throw new JsonException(e);
- } finally {
- JsonStreamPool.returnJsonStream(stream);
- }
+ serialize(type, obj, out, false);
}
public static String serialize(Config config, Object obj) {
- JsoniterSpi.setCurrentConfig(config);
- try {
- return serialize(config.escapeUnicode(), obj.getClass(), obj);
- } finally {
- JsoniterSpi.clearCurrentConfig();
- }
+ return serialize(config, obj.getClass(), obj);
}
public static String serialize(Object obj) {
- return serialize(JsoniterSpi.getCurrentConfig().escapeUnicode(), obj.getClass(), obj);
+ return serialize(obj.getClass(), obj);
}
public static String serialize(Config config, TypeLiteral typeLiteral, Object obj) {
+ return serialize(config, typeLiteral.getType(), obj);
+ }
+
+ private static String serialize(Config config, Type type, Object obj) {
+ final Config configBackup = JsoniterSpi.getCurrentConfig();
+ // Set temporary config
JsoniterSpi.setCurrentConfig(config);
try {
- return serialize(config.escapeUnicode(), typeLiteral.getType(), obj);
+ return serialize(type, obj);
} finally {
- JsoniterSpi.clearCurrentConfig();
+ // Revert old config
+ JsoniterSpi.setCurrentConfig(configBackup);
}
}
public static String serialize(TypeLiteral typeLiteral, Object obj) {
- return serialize(JsoniterSpi.getCurrentConfig().escapeUnicode(), typeLiteral.getType(), obj);
+ return serialize(typeLiteral.getType(), obj);
}
public static String serialize(boolean escapeUnicode, Type type, Object obj) {
- JsonStream stream = JsonStreamPool.borrowJsonStream();
+ final Config currentConfig = JsoniterSpi.getCurrentConfig();
+ return serialize(currentConfig.copyBuilder().escapeUnicode(escapeUnicode).build(), type, obj);
+ }
+
+ private static String serialize(Type type, Object obj) {
+ return serialize(type, obj, null, true);
+ }
+
+ private static String serialize(Type type, Object obj, OutputStream out, boolean returnObjAsString) {
+ final JsonStream stream = JsonStreamPool.borrowJsonStream();
+ final boolean escapeUnicode = JsoniterSpi.getCurrentConfig().escapeUnicode();
try {
- stream.reset(null);
- stream.writeVal(type, obj);
+ try {
+ stream.reset(out);
+ stream.writeVal(type, obj);
+ } finally {
+ stream.close();
+ }
+ if (!returnObjAsString) {
+ return "";
+ }
if (escapeUnicode) {
return new String(stream.buf, 0, stream.count);
} else {
diff --git a/src/main/java/com/jsoniter/output/MapKeyEncoders.java b/src/main/java/com/jsoniter/output/MapKeyEncoders.java
new file mode 100644
index 00000000..401ebfbe
--- /dev/null
+++ b/src/main/java/com/jsoniter/output/MapKeyEncoders.java
@@ -0,0 +1,78 @@
+package com.jsoniter.output;
+
+import com.jsoniter.spi.*;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
+
+class MapKeyEncoders {
+
+ public static Encoder registerOrGetExisting(Type mapKeyType) {
+ String cacheKey = JsoniterSpi.getMapKeyEncoderCacheKey(mapKeyType);
+ Encoder mapKeyEncoder = JsoniterSpi.getMapKeyEncoder(cacheKey);
+ if (null != mapKeyEncoder) {
+ return mapKeyEncoder;
+ }
+ mapKeyEncoder = createDefaultEncoder(mapKeyType);
+ JsoniterSpi.addNewMapEncoder(cacheKey, mapKeyEncoder);
+ return mapKeyEncoder;
+ }
+
+ private static Encoder createDefaultEncoder(Type mapKeyType) {
+ if (mapKeyType == String.class) {
+ return new StringKeyEncoder();
+ }
+ if (mapKeyType == Object.class) {
+ return new DynamicKeyEncoder();
+ }
+ if (mapKeyType instanceof WildcardType) {
+ return new DynamicKeyEncoder();
+ }
+ if (mapKeyType instanceof Class && ((Class) mapKeyType).isEnum()) {
+ return new StringKeyEncoder();
+ }
+ Encoder.ReflectionEncoder encoder = CodegenImplNative.NATIVE_ENCODERS.get(mapKeyType);
+ if (encoder != null) {
+ return new NumberKeyEncoder(encoder);
+ }
+ throw new JsonException("can not encode map key type: " + mapKeyType);
+ }
+
+ private static class StringKeyEncoder implements Encoder {
+
+ @Override
+ public void encode(Object obj, JsonStream stream) throws IOException {
+ stream.writeVal(obj);
+ }
+ }
+
+ private static class NumberKeyEncoder implements Encoder {
+
+ private final Encoder encoder;
+
+ private NumberKeyEncoder(Encoder encoder) {
+ this.encoder = encoder;
+ }
+
+ @Override
+ public void encode(Object obj, JsonStream stream) throws IOException {
+ stream.write('"');
+ encoder.encode(obj, stream);
+ stream.write('"');
+ }
+ }
+
+ private static class DynamicKeyEncoder implements Encoder {
+
+ @Override
+ public void encode(Object obj, JsonStream stream) throws IOException {
+ Class> clazz = obj.getClass();
+ if (clazz == Object.class) {
+ throw new JsonException("map key type is Object.class, can not be encoded");
+ }
+ Encoder mapKeyEncoder = registerOrGetExisting(clazz);
+ mapKeyEncoder.encode(obj, stream);
+ }
+ }
+}
diff --git a/src/main/java/com/jsoniter/output/ReflectionMapEncoder.java b/src/main/java/com/jsoniter/output/ReflectionMapEncoder.java
index a4380737..ddf27d65 100644
--- a/src/main/java/com/jsoniter/output/ReflectionMapEncoder.java
+++ b/src/main/java/com/jsoniter/output/ReflectionMapEncoder.java
@@ -11,20 +11,16 @@
class ReflectionMapEncoder implements Encoder.ReflectionEncoder {
private final TypeLiteral valueTypeLiteral;
- private final MapKeyEncoder mapKeyEncoder;
+ private final Encoder mapKeyEncoder;
public ReflectionMapEncoder(Class clazz, Type[] typeArgs) {
- Type keyType = String.class;
+ Type keyType = Object.class;
Type valueType = Object.class;
if (typeArgs.length == 2) {
keyType = typeArgs[0];
valueType = typeArgs[1];
}
- if (keyType == String.class) {
- mapKeyEncoder = null;
- } else {
- mapKeyEncoder = DefaultMapKeyEncoder.registerOrGetExisting(keyType);
- }
+ mapKeyEncoder = MapKeyEncoders.registerOrGetExisting(keyType);
valueTypeLiteral = TypeLiteral.create(valueType);
}
@@ -58,11 +54,7 @@ private boolean writeEntry(JsonStream stream, boolean notFirst, Map.Entry