diff --git a/.gitignore b/.gitignore index 5581c38e..4718e79b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,9 @@ /release.properties /demo/target /.idea +/android-demo/build +/android-demo/.gradle +/android-demo/local.properties +/android-demo/gradlew +/android-demo/gradle +/android-demo/gradlew.bat diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..7581c084 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +language: java +sudo: false # faster builds + +install: true + +script: "mvn test && mvn cobertura:cobertura" + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..c93b3bdd --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,68 @@ +# 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 +* fix #133 NPE when no extra +* fix #132 MaybeEmptyArrayDecoder +* fix #130 @JsonCreator not compatible with @JsonIgnore +* fix #126 surrogate unicode + +# 0.9.18 +* fix of overflow detection for numeric primitive types +* fix of method prefix of error message +* issue #125 avoid nested JsonException +* fix #109 treat wildcard generics variable as Object + +# 0.9.17 +* fix leading zero +* fix #112 #119 +* fix of parsing zero & min values +* issue #115 better leading zero detection +* fix #144, parse max int/long +* fix #110 if @JsonProperty is marked on field, ignore getter/setter + +# 0.9.16 + +* issue #107 annotation should be marked on getter/setter if present +* fix ctor is null when encoding issue +* issue #104, JsonWrapper argument should not be mandatory +* issue #99 added mustBeValid method to Any class +* issue #97 demonstrate JsonProperty when both field and setter +* like "1.0e+10" should not fail +* issue #94 skip transient field +* issue #94 fix JsonProperty not changing fromNames and toNames +* issue #93 some control character should be esacped specially +* issue #93 fix control character serialization +* issue #92 fix generics support + +# 0.9.15 + +breaking changes + +* `null` is not omitted by default config + +new features + +* add `defaultValueToOmit` to @JsonProperty +* add `omitDefaultValue` to config +* encoder support indention in dynamic mode \ No newline at end of file diff --git a/README.md b/README.md index 7d0f77da..b621a36c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +[![Build Status](https://travis-ci.org/json-iterator/java.svg?branch=master)](https://travis-ci.org/json-iterator/java) +[![codecov](https://codecov.io/gh/json-iterator/java/branch/master/graph/badge.svg)](https://codecov.io/gh/json-iterator/java) +[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/json-iterator/java/master/LICENSE) +[![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby) + Documentation : [http://jsoniter.com/java-features.html](http://jsoniter.com/java-features.html) -[![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby) \ No newline at end of file +Scala User: https://github.com/plokhotnyuk/jsoniter-scala \ No newline at end of file diff --git a/android-demo/.gitignore b/android-demo/.gitignore new file mode 100644 index 00000000..a94ca08d --- /dev/null +++ b/android-demo/.gitignore @@ -0,0 +1,3 @@ +/build +/.idea +/gradle.properties diff --git a/android-demo/build.gradle b/android-demo/build.gradle new file mode 100644 index 00000000..ed3538b4 --- /dev/null +++ b/android-demo/build.gradle @@ -0,0 +1,71 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 27 + buildToolsVersion "27.0.2" + + defaultConfig { + applicationId "com.example.myapplication" + minSdkVersion 15 + targetSdkVersion 27 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + compile 'com.android.support:appcompat-v7:27.+' + compile 'com.android.support.constraint:constraint-layout:+' + compile 'com.jsoniter:jsoniter:0.9.19-SNAPSHOT' + testCompile 'junit:junit:4.12' +} + +buildscript { + repositories { + maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'} + google() + mavenLocal() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.3.0' //last version Jan 2016 + } +} + +allprojects { + repositories { + maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'} + google() + mavenLocal() + } +} + +afterEvaluate { + android.applicationVariants.all { variant -> + variant.javaCompiler.finalizedBy(jsoniterStaticCodgen) + } +} + +task jsoniterStaticCodgen(type:JavaExec) { + classpath configurations.getByName(android.sourceSets.main.compileConfigurationName) + classpath project.buildDir.toString() + '/intermediates/classes/release' + classpath project.buildDir.toString() + '/intermediates/classes/debug' + main = 'com.jsoniter.static_codegen.StaticCodegen' + args 'com.example.myapplication.DemoCodegenConfig' + workingDir = android.sourceSets.main.java.srcDirs[0].toString() + standardOutput = System.out + errorOutput = System.err +} + diff --git a/android-demo/proguard-rules.pro b/android-demo/proguard-rules.pro new file mode 100644 index 00000000..35c57bef --- /dev/null +++ b/android-demo/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /home/xiaoju/Android/Sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/android-demo/settings.gradle b/android-demo/settings.gradle new file mode 100644 index 00000000..b63b000f --- /dev/null +++ b/android-demo/settings.gradle @@ -0,0 +1 @@ +include ':android-demo' diff --git a/android-demo/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.java b/android-demo/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.java new file mode 100644 index 00000000..24ffb078 --- /dev/null +++ b/android-demo/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.example.myapplication; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumentation test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.example.myapplication", appContext.getPackageName()); + } +} diff --git a/android-demo/src/main/AndroidManifest.xml b/android-demo/src/main/AndroidManifest.xml new file mode 100644 index 00000000..c9d1f59f --- /dev/null +++ b/android-demo/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android-demo/src/main/java/com/example/myapplication/DemoCodegenConfig.java b/android-demo/src/main/java/com/example/myapplication/DemoCodegenConfig.java new file mode 100644 index 00000000..53e756d1 --- /dev/null +++ b/android-demo/src/main/java/com/example/myapplication/DemoCodegenConfig.java @@ -0,0 +1,49 @@ +package com.example.myapplication; + +import com.jsoniter.JsonIterator; +import com.jsoniter.output.EncodingMode; +import com.jsoniter.output.JsonStream; +import com.jsoniter.spi.Decoder; +import com.jsoniter.spi.DecodingMode; +import com.jsoniter.spi.JsoniterSpi; +import com.jsoniter.spi.TypeLiteral; +import com.jsoniter.static_codegen.StaticCodegenConfig; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +public class DemoCodegenConfig implements StaticCodegenConfig { + + @Override + public void setup() { + // register custom decoder or extensions before codegen + // so that we doing codegen, we know in which case, we need to callback + JsonIterator.setMode(DecodingMode.STATIC_MODE); + JsonStream.setMode(EncodingMode.STATIC_MODE); + JsonStream.setIndentionStep(2); + JsoniterSpi.registerPropertyDecoder(User.class, "score", new Decoder.IntDecoder() { + @Override + public int decodeInt(JsonIterator iter) throws IOException { + return Integer.valueOf(iter.readString()); + } + }); + } + + @Override + public TypeLiteral[] whatToCodegen() { + return new TypeLiteral[]{ + // generic types, need to use this syntax + new TypeLiteral>() { + }, + new TypeLiteral>() { + }, + new TypeLiteral>() { + }, + // array + TypeLiteral.create(int[].class), + // object + TypeLiteral.create(User.class) + }; + } +} diff --git a/android-demo/src/main/java/com/example/myapplication/MainActivity.java b/android-demo/src/main/java/com/example/myapplication/MainActivity.java new file mode 100644 index 00000000..c9d8fa7e --- /dev/null +++ b/android-demo/src/main/java/com/example/myapplication/MainActivity.java @@ -0,0 +1,17 @@ +package com.example.myapplication; + +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.util.Log; +import com.jsoniter.JsonIterator; + +public class MainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + User user = JsonIterator.deserialize("{\"firstName\": \"tao\", \"lastName\": \"wen\", \"score\": 1024}", User.class); + Log.d("jsoniter", user.firstName); + } +} diff --git a/android-demo/src/main/java/com/example/myapplication/User.java b/android-demo/src/main/java/com/example/myapplication/User.java new file mode 100644 index 00000000..e7fd317c --- /dev/null +++ b/android-demo/src/main/java/com/example/myapplication/User.java @@ -0,0 +1,11 @@ +package com.example.myapplication; + +import com.jsoniter.annotation.JsonProperty; + +public class User { + @JsonProperty(nullable = false) + public String firstName; + @JsonProperty(nullable = false) + public String lastName; + public int score; +} diff --git a/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/com/example/myapplication/User.java b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/com/example/myapplication/User.java new file mode 100644 index 00000000..baef2fa8 --- /dev/null +++ b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/com/example/myapplication/User.java @@ -0,0 +1,55 @@ +package jsoniter_codegen.cfg1173796797.decoder.com.example.myapplication; +public class User implements com.jsoniter.spi.Decoder { +public static java.lang.Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { java.lang.Object existingObj = com.jsoniter.CodegenAccess.resetExistingObject(iter); +byte nextToken = com.jsoniter.CodegenAccess.readByte(iter); +if (nextToken != '{') { +if (nextToken == 'n') { +com.jsoniter.CodegenAccess.skipFixedBytes(iter, 3); +return null; +} else { +nextToken = com.jsoniter.CodegenAccess.nextToken(iter); +if (nextToken == 'n') { +com.jsoniter.CodegenAccess.skipFixedBytes(iter, 3); +return null; +} +} // end of if null +} // end of if { +nextToken = com.jsoniter.CodegenAccess.readByte(iter); +if (nextToken != '"') { +if (nextToken == '}') { +return (existingObj == null ? new com.example.myapplication.User() : (com.example.myapplication.User)existingObj); +} else { +nextToken = com.jsoniter.CodegenAccess.nextToken(iter); +if (nextToken == '}') { +return (existingObj == null ? new com.example.myapplication.User() : (com.example.myapplication.User)existingObj); +} else { +com.jsoniter.CodegenAccess.unreadByte(iter); +} +} // end of if end +} else { com.jsoniter.CodegenAccess.unreadByte(iter); }// end of if not quote +java.lang.String _firstName_ = null; +java.lang.String _lastName_ = null; +int _score_ = 0; +do { +switch (com.jsoniter.CodegenAccess.readObjectFieldAsHash(iter)) { +case -1078100014: +_lastName_ = (java.lang.String)iter.readString(); +continue; +case -799547430: +_firstName_ = (java.lang.String)iter.readString(); +continue; +case -768634731: +_score_ = (int)com.jsoniter.CodegenAccess.readInt("score@jsoniter_codegen.cfg1173796797.decoder.com.example.myapplication.User", iter); +continue; +} +iter.skip(); +} while (com.jsoniter.CodegenAccess.nextTokenIsComma(iter)); +com.example.myapplication.User obj = (existingObj == null ? new com.example.myapplication.User() : (com.example.myapplication.User)existingObj); +obj.firstName = _firstName_; +obj.lastName = _lastName_; +obj.score = _score_; +return obj; +}public java.lang.Object decode(com.jsoniter.JsonIterator iter) throws java.io.IOException { +return decode_(iter); +} +} diff --git a/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/int_array.java b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/int_array.java new file mode 100644 index 00000000..9ff993e7 --- /dev/null +++ b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/int_array.java @@ -0,0 +1,60 @@ +package jsoniter_codegen.cfg1173796797.decoder; +public class int_array implements com.jsoniter.spi.Decoder { +public static java.lang.Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { com.jsoniter.CodegenAccess.resetExistingObject(iter); +byte nextToken = com.jsoniter.CodegenAccess.readByte(iter); +if (nextToken != '[') { +if (nextToken == 'n') { +com.jsoniter.CodegenAccess.skipFixedBytes(iter, 3); +com.jsoniter.CodegenAccess.resetExistingObject(iter); return null; +} else { +nextToken = com.jsoniter.CodegenAccess.nextToken(iter); +if (nextToken == 'n') { +com.jsoniter.CodegenAccess.skipFixedBytes(iter, 3); +com.jsoniter.CodegenAccess.resetExistingObject(iter); return null; +} +} +} +nextToken = com.jsoniter.CodegenAccess.nextToken(iter); +if (nextToken == ']') { +return new int[0]; +} +com.jsoniter.CodegenAccess.unreadByte(iter); +int a1 = (int)iter.readInt(); +if (!com.jsoniter.CodegenAccess.nextTokenIsComma(iter)) { +return new int[]{ a1 }; +} +int a2 = (int)iter.readInt(); +if (!com.jsoniter.CodegenAccess.nextTokenIsComma(iter)) { +return new int[]{ a1, a2 }; +} +int a3 = (int)iter.readInt(); +if (!com.jsoniter.CodegenAccess.nextTokenIsComma(iter)) { +return new int[]{ a1, a2, a3 }; +} +int a4 = (int) (int)iter.readInt(); +if (!com.jsoniter.CodegenAccess.nextTokenIsComma(iter)) { +return new int[]{ a1, a2, a3, a4 }; +} +int a5 = (int) (int)iter.readInt(); +int[] arr = new int[10]; +arr[0] = a1; +arr[1] = a2; +arr[2] = a3; +arr[3] = a4; +arr[4] = a5; +int i = 5; +while (com.jsoniter.CodegenAccess.nextTokenIsComma(iter)) { +if (i == arr.length) { +int[] newArr = new int[arr.length * 2]; +System.arraycopy(arr, 0, newArr, 0, arr.length); +arr = newArr; +} +arr[i++] = (int)iter.readInt(); +} +int[] result = new int[i]; +System.arraycopy(arr, 0, result, 0, i); +return result; +}public java.lang.Object decode(com.jsoniter.JsonIterator iter) throws java.io.IOException { +return decode_(iter); +} +} diff --git a/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/List_com/example/myapplication/User.java b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/List_com/example/myapplication/User.java new file mode 100644 index 00000000..41966727 --- /dev/null +++ b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/List_com/example/myapplication/User.java @@ -0,0 +1,42 @@ +package jsoniter_codegen.cfg1173796797.decoder.java.util.List_com.example.myapplication; +public class User implements com.jsoniter.spi.Decoder { +public static java.lang.Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { java.util.ArrayList col = (java.util.ArrayList)com.jsoniter.CodegenAccess.resetExistingObject(iter); +if (iter.readNull()) { com.jsoniter.CodegenAccess.resetExistingObject(iter); return null; } +if (!com.jsoniter.CodegenAccess.readArrayStart(iter)) { +return col == null ? new java.util.ArrayList(0): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); +} +Object a1 = (com.example.myapplication.User)jsoniter_codegen.cfg1173796797.decoder.com.example.myapplication.User.decode_(iter); +if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { +java.util.ArrayList obj = col == null ? new java.util.ArrayList(1): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); +obj.add(a1); +return obj; +} +Object a2 = (com.example.myapplication.User)jsoniter_codegen.cfg1173796797.decoder.com.example.myapplication.User.decode_(iter); +if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { +java.util.ArrayList obj = col == null ? new java.util.ArrayList(2): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); +obj.add(a1); +obj.add(a2); +return obj; +} +Object a3 = (com.example.myapplication.User)jsoniter_codegen.cfg1173796797.decoder.com.example.myapplication.User.decode_(iter); +if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { +java.util.ArrayList obj = col == null ? new java.util.ArrayList(3): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); +obj.add(a1); +obj.add(a2); +obj.add(a3); +return obj; +} +Object a4 = (com.example.myapplication.User)jsoniter_codegen.cfg1173796797.decoder.com.example.myapplication.User.decode_(iter); +java.util.ArrayList obj = col == null ? new java.util.ArrayList(8): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); +obj.add(a1); +obj.add(a2); +obj.add(a3); +obj.add(a4); +while (com.jsoniter.CodegenAccess.nextToken(iter) == ',') { +obj.add((com.example.myapplication.User)jsoniter_codegen.cfg1173796797.decoder.com.example.myapplication.User.decode_(iter)); +} +return obj; +}public java.lang.Object decode(com.jsoniter.JsonIterator iter) throws java.io.IOException { +return decode_(iter); +} +} diff --git a/demo/src/main/java/decoder/java/util/List_java/lang/Integer.java b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/List_java/lang/Integer.java similarity index 69% rename from demo/src/main/java/decoder/java/util/List_java/lang/Integer.java rename to android-demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/List_java/lang/Integer.java index cb1ebe24..419b2413 100644 --- a/demo/src/main/java/decoder/java/util/List_java/lang/Integer.java +++ b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/List_java/lang/Integer.java @@ -1,24 +1,24 @@ -package decoder.java.util.List_java.lang; +package jsoniter_codegen.cfg1173796797.decoder.java.util.List_java.lang; public class Integer implements com.jsoniter.spi.Decoder { public static java.lang.Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { java.util.ArrayList col = (java.util.ArrayList)com.jsoniter.CodegenAccess.resetExistingObject(iter); -if (iter.readNull()) { return null; } +if (iter.readNull()) { com.jsoniter.CodegenAccess.resetExistingObject(iter); return null; } if (!com.jsoniter.CodegenAccess.readArrayStart(iter)) { return col == null ? new java.util.ArrayList(0): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); } -Object a1 = java.lang.Integer.valueOf(iter.readInt()); +Object a1 = (java.lang.Integer)(iter.readNull() ? null : java.lang.Integer.valueOf(iter.readInt())); if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { java.util.ArrayList obj = col == null ? new java.util.ArrayList(1): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); obj.add(a1); return obj; } -Object a2 = java.lang.Integer.valueOf(iter.readInt()); +Object a2 = (java.lang.Integer)(iter.readNull() ? null : java.lang.Integer.valueOf(iter.readInt())); if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { java.util.ArrayList obj = col == null ? new java.util.ArrayList(2): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); obj.add(a1); obj.add(a2); return obj; } -Object a3 = java.lang.Integer.valueOf(iter.readInt()); +Object a3 = (java.lang.Integer)(iter.readNull() ? null : java.lang.Integer.valueOf(iter.readInt())); if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { java.util.ArrayList obj = col == null ? new java.util.ArrayList(3): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); obj.add(a1); @@ -26,14 +26,14 @@ public class Integer implements com.jsoniter.spi.Decoder { obj.add(a3); return obj; } -Object a4 = java.lang.Integer.valueOf(iter.readInt()); +Object a4 = (java.lang.Integer)(iter.readNull() ? null : java.lang.Integer.valueOf(iter.readInt())); java.util.ArrayList obj = col == null ? new java.util.ArrayList(8): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); obj.add(a1); obj.add(a2); obj.add(a3); obj.add(a4); while (com.jsoniter.CodegenAccess.nextToken(iter) == ',') { -obj.add(java.lang.Integer.valueOf(iter.readInt())); +obj.add((java.lang.Integer)(iter.readNull() ? null : java.lang.Integer.valueOf(iter.readInt()))); } return obj; }public java.lang.Object decode(com.jsoniter.JsonIterator iter) throws java.io.IOException { diff --git a/demo/src/main/java/decoder/java/util/Map_java/lang/String_java/lang/Object.java b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/Map_java/lang/String_java/lang/Object.java similarity index 63% rename from demo/src/main/java/decoder/java/util/Map_java/lang/String_java/lang/Object.java rename to android-demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/Map_java/lang/String_java/lang/Object.java index 146294e7..3250faa4 100644 --- a/demo/src/main/java/decoder/java/util/Map_java/lang/String_java/lang/Object.java +++ b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/Map_java/lang/String_java/lang/Object.java @@ -1,4 +1,4 @@ -package decoder.java.util.Map_java.lang.String_java.lang; +package jsoniter_codegen.cfg1173796797.decoder.java.util.Map_java.lang.String_java.lang; public class Object implements com.jsoniter.spi.Decoder { public static java.lang.Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { java.util.HashMap map = (java.util.HashMap)com.jsoniter.CodegenAccess.resetExistingObject(iter); if (iter.readNull()) { return null; } @@ -6,12 +6,10 @@ public class Object implements com.jsoniter.spi.Decoder { if (!com.jsoniter.CodegenAccess.readObjectStart(iter)) { return map; } -String field = com.jsoniter.CodegenAccess.readObjectFieldAsString(iter); -map.put(field, iter.read()); -while (com.jsoniter.CodegenAccess.nextToken(iter) == ',') { -field = com.jsoniter.CodegenAccess.readObjectFieldAsString(iter); -map.put(field, iter.read()); -} +do { +java.lang.Object mapKey = com.jsoniter.CodegenAccess.readObjectFieldAsString(iter); +map.put(mapKey, (java.lang.Object)iter.read()); +} while (com.jsoniter.CodegenAccess.nextToken(iter) == ','); return map; }public java.lang.Object decode(com.jsoniter.JsonIterator iter) throws java.io.IOException { return decode_(iter); diff --git a/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/com/example/myapplication/User.java b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/com/example/myapplication/User.java new file mode 100644 index 00000000..03e714c6 --- /dev/null +++ b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/com/example/myapplication/User.java @@ -0,0 +1,20 @@ +package jsoniter_codegen.cfg1173796797.encoder.com.example.myapplication; +public class User implements com.jsoniter.spi.Encoder { +public void encode(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +if (obj == null) { stream.writeNull(); return; } +encode_((com.example.myapplication.User)obj, stream); +} +public static void encode_(com.example.myapplication.User obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +stream.writeObjectStart(); +stream.writeIndention(); +stream.writeObjectField("firstName"); +stream.writeVal((java.lang.String)obj.firstName); +stream.writeMore(); +stream.writeObjectField("lastName"); +stream.writeVal((java.lang.String)obj.lastName); +stream.writeMore(); +stream.writeObjectField("score"); +stream.writeVal((int)obj.score); +stream.writeObjectEnd(); +} +} diff --git a/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/int_array.java b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/int_array.java new file mode 100644 index 00000000..e799b768 --- /dev/null +++ b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/int_array.java @@ -0,0 +1,21 @@ +package jsoniter_codegen.cfg1173796797.encoder; +public class int_array implements com.jsoniter.spi.Encoder { +public void encode(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +if (obj == null) { stream.writeNull(); return; } +encode_((int[])obj, stream); +} +public static void encode_(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +int[] arr = (int[])obj; +if (arr.length == 0) { stream.write((byte)'[', (byte)']'); return; } +stream.writeArrayStart(); stream.writeIndention(); +int i = 0; +int e = arr[i++]; +stream.writeVal((int)e); +while (i < arr.length) { +stream.writeMore(); +e = arr[i++]; +stream.writeVal((int)e); +} +stream.writeArrayEnd(); +} +} diff --git a/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/List_com/example/myapplication/User.java b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/List_com/example/myapplication/User.java new file mode 100644 index 00000000..10daee09 --- /dev/null +++ b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/List_com/example/myapplication/User.java @@ -0,0 +1,29 @@ +package jsoniter_codegen.cfg1173796797.encoder.java.util.List_com.example.myapplication; +public class User implements com.jsoniter.spi.Encoder { +public void encode(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +if (obj == null) { stream.writeNull(); return; } +encode_((java.util.List)obj, stream); +} +public static void encode_(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +java.util.List list = (java.util.List)obj; +int size = list.size(); +if (size == 0) { stream.write((byte)'[', (byte)']'); return; } +stream.writeArrayStart(); stream.writeIndention(); +java.lang.Object e = list.get(0); +if (e == null) { stream.writeNull(); } else { + +jsoniter_codegen.cfg1173796797.encoder.com.example.myapplication.User.encode_((com.example.myapplication.User)e, stream); + +} +for (int i = 1; i < size; i++) { +stream.writeMore(); +e = list.get(i); +if (e == null) { stream.writeNull(); } else { + +jsoniter_codegen.cfg1173796797.encoder.com.example.myapplication.User.encode_((com.example.myapplication.User)e, stream); + +} +} +stream.writeArrayEnd(); +} +} diff --git a/demo/src/main/java/encoder/java/util/List_java/lang/Integer.java b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/List_java/lang/Integer.java similarity index 57% rename from demo/src/main/java/encoder/java/util/List_java/lang/Integer.java rename to android-demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/List_java/lang/Integer.java index 63fa777b..0e2069fb 100644 --- a/demo/src/main/java/encoder/java/util/List_java/lang/Integer.java +++ b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/List_java/lang/Integer.java @@ -1,25 +1,25 @@ -package encoder.java.util.List_java.lang; -public class Integer extends com.jsoniter.spi.EmptyEncoder { -public void encode(Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +package jsoniter_codegen.cfg1173796797.encoder.java.util.List_java.lang; +public class Integer implements com.jsoniter.spi.Encoder { +public void encode(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { if (obj == null) { stream.writeNull(); return; } -stream.write((byte)'['); encode_((java.util.List)obj, stream); -stream.write((byte)']'); } public static void encode_(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { java.util.List list = (java.util.List)obj; int size = list.size(); -if (size == 0) { return; } +if (size == 0) { stream.write((byte)'[', (byte)']'); return; } +stream.writeArrayStart(); stream.writeIndention(); java.lang.Object e = list.get(0); if (e == null) { stream.writeNull(); } else { stream.writeVal((java.lang.Integer)e); } for (int i = 1; i < size; i++) { -stream.write(','); +stream.writeMore(); e = list.get(i); if (e == null) { stream.writeNull(); } else { stream.writeVal((java.lang.Integer)e); } } +stream.writeArrayEnd(); } } diff --git a/demo/src/main/java/encoder/java/util/Map_java/lang/String_java/lang/Object.java b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/Map_java/lang/String_java/lang/Object.java similarity index 54% rename from demo/src/main/java/encoder/java/util/Map_java/lang/String_java/lang/Object.java rename to android-demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/Map_java/lang/String_java/lang/Object.java index 14397ef0..f2bd7203 100644 --- a/demo/src/main/java/encoder/java/util/Map_java/lang/String_java/lang/Object.java +++ b/android-demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/Map_java/lang/String_java/lang/Object.java @@ -1,29 +1,30 @@ -package encoder.java.util.Map_java.lang.String_java.lang; -public class Object extends com.jsoniter.spi.EmptyEncoder { -public void encode(Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +package jsoniter_codegen.cfg1173796797.encoder.java.util.Map_java.lang.String_java.lang; +public class Object implements com.jsoniter.spi.Encoder { +public void encode(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { if (obj == null) { stream.writeNull(); return; } -stream.write((byte)'{'); encode_((java.util.Map)obj, stream); -stream.write((byte)'}'); } public static void encode_(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { if (obj == null) { stream.writeNull(); return; } java.util.Map map = (java.util.Map)obj; java.util.Iterator iter = map.entrySet().iterator(); -if(!iter.hasNext()) { return; } +if(!iter.hasNext()) { stream.write((byte)'{', (byte)'}'); return; } java.util.Map.Entry entry = (java.util.Map.Entry)iter.next(); -stream.writeVal((String)entry.getKey()); -stream.write((byte)':'); +stream.writeObjectStart(); stream.writeIndention(); +stream.writeVal((java.lang.String)entry.getKey()); +stream.write((byte)':', (byte)' '); if (entry.getValue() == null) { stream.writeNull(); } else { stream.writeVal((java.lang.Object)entry.getValue()); } while(iter.hasNext()) { entry = (java.util.Map.Entry)iter.next(); -stream.write((byte)','); -stream.writeObjectField((String)entry.getKey()); +stream.writeMore(); +stream.writeVal((java.lang.String)entry.getKey()); +stream.write((byte)':', (byte)' '); if (entry.getValue() == null) { stream.writeNull(); } else { stream.writeVal((java.lang.Object)entry.getValue()); } } +stream.writeObjectEnd(); } } diff --git a/android-demo/src/main/res/layout/activity_main.xml b/android-demo/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..e01d85ca --- /dev/null +++ b/android-demo/src/main/res/layout/activity_main.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/android-demo/src/main/res/mipmap-hdpi/ic_launcher.png b/android-demo/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..cde69bcc Binary files /dev/null and b/android-demo/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android-demo/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android-demo/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 00000000..9a078e3e Binary files /dev/null and b/android-demo/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/android-demo/src/main/res/mipmap-mdpi/ic_launcher.png b/android-demo/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..c133a0cb Binary files /dev/null and b/android-demo/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android-demo/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android-demo/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 00000000..efc028a6 Binary files /dev/null and b/android-demo/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/android-demo/src/main/res/mipmap-xhdpi/ic_launcher.png b/android-demo/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..bfa42f0e Binary files /dev/null and b/android-demo/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android-demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android-demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 00000000..3af2608a Binary files /dev/null and b/android-demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/android-demo/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android-demo/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..324e72cd Binary files /dev/null and b/android-demo/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android-demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android-demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..9bec2e62 Binary files /dev/null and b/android-demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/android-demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android-demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..aee44e13 Binary files /dev/null and b/android-demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..34947cd6 Binary files /dev/null and b/android-demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android-demo/src/main/res/values/colors.xml b/android-demo/src/main/res/values/colors.xml new file mode 100644 index 00000000..3ab3e9cb --- /dev/null +++ b/android-demo/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff --git a/android-demo/src/main/res/values/strings.xml b/android-demo/src/main/res/values/strings.xml new file mode 100644 index 00000000..efd30732 --- /dev/null +++ b/android-demo/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + My Application + diff --git a/android-demo/src/main/res/values/styles.xml b/android-demo/src/main/res/values/styles.xml new file mode 100644 index 00000000..5885930d --- /dev/null +++ b/android-demo/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/android-demo/src/test/java/com/example/myapplication/ExampleUnitTest.java b/android-demo/src/test/java/com/example/myapplication/ExampleUnitTest.java new file mode 100644 index 00000000..ff8f26e0 --- /dev/null +++ b/android-demo/src/test/java/com/example/myapplication/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.example.myapplication; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/demo/pom.xml b/demo/pom.xml index 6fa89d3a..962f0c8e 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -2,9 +2,9 @@ 4.0.0 com.jsoniter - 0.9.8-SNAPSHOT + 0.9.21-SNAPSHOT jsoniter-demo - json iterator + json iterator demo jsoniter (json-iterator) is fast and flexible JSON parser available in Java and Go http://jsoniter.com jar @@ -49,7 +49,7 @@ com.jsoniter jsoniter - 0.9.8-SNAPSHOT + 0.9.21-SNAPSHOT org.openjdk.jmh @@ -86,11 +86,11 @@ slf4j-api 1.7.22 - - com.dslplatform - dsl-json-processor - 1.4.1 - + + + + + com.alibaba fastjson @@ -115,8 +115,8 @@ maven-compiler-plugin 3.6.0 - 1.8 - 1.8 + 1.6 + 1.6 UTF-8 @@ -137,7 +137,7 @@ -classpath - com.jsoniter.StaticCodeGenerator + com.jsoniter.static_codegen.StaticCodegen com.jsoniter.demo.DemoCodegenConfig diff --git a/demo/src/main/java/com/jsoniter/demo/Demo.java b/demo/src/main/java/com/jsoniter/demo/Demo.java index f1f5c5b1..a25650df 100644 --- a/demo/src/main/java/com/jsoniter/demo/Demo.java +++ b/demo/src/main/java/com/jsoniter/demo/Demo.java @@ -1,17 +1,15 @@ package com.jsoniter.demo; -import com.jsoniter.DecodingMode; import com.jsoniter.JsonIterator; +import com.jsoniter.any.Any; import com.jsoniter.output.EncodingMode; import com.jsoniter.output.JsonStream; +import com.jsoniter.spi.DecodingMode; public class Demo { static { // ensure the jsoniter is properly setup new DemoCodegenConfig().setup(); - JsonIterator.setMode(DecodingMode.STATIC_MODE); - JsonStream.setMode(EncodingMode.STATIC_MODE); - JsonStream.defaultIndentionStep = 2; } public static void main(String[] args) { @@ -19,6 +17,7 @@ public static void main(String[] args) { System.out.println(user.firstName); System.out.println(user.lastName); System.out.println(user.score); + user.attachment = Any.wrapArray(new int[]{1, 2, 3}); System.out.println(JsonStream.serialize(user)); } } diff --git a/demo/src/main/java/com/jsoniter/demo/DemoCodegenConfig.java b/demo/src/main/java/com/jsoniter/demo/DemoCodegenConfig.java index 6184d32f..4c16249d 100644 --- a/demo/src/main/java/com/jsoniter/demo/DemoCodegenConfig.java +++ b/demo/src/main/java/com/jsoniter/demo/DemoCodegenConfig.java @@ -1,24 +1,29 @@ package com.jsoniter.demo; import com.jsoniter.JsonIterator; -import com.jsoniter.StaticCodeGenerator; -import com.jsoniter.annotation.JsoniterAnnotationSupport; -import com.jsoniter.spi.CodegenConfig; +import com.jsoniter.any.Any; +import com.jsoniter.output.EncodingMode; +import com.jsoniter.output.JsonStream; import com.jsoniter.spi.Decoder; +import com.jsoniter.spi.DecodingMode; import com.jsoniter.spi.JsoniterSpi; import com.jsoniter.spi.TypeLiteral; +import com.jsoniter.static_codegen.StaticCodegenConfig; import java.io.IOException; import java.util.List; import java.util.Map; -public class DemoCodegenConfig implements CodegenConfig { +public class DemoCodegenConfig implements StaticCodegenConfig { @Override public void setup() { - JsoniterAnnotationSupport.enable(); // register custom decoder or extensions before codegen // so that we doing codegen, we know in which case, we need to callback + Any.registerEncoders(); + JsonIterator.setMode(DecodingMode.STATIC_MODE); + JsonStream.setMode(EncodingMode.STATIC_MODE); + JsonStream.setIndentionStep(2); JsoniterSpi.registerPropertyDecoder(User.class, "score", new Decoder.IntDecoder() { @Override public int decodeInt(JsonIterator iter) throws IOException { @@ -43,8 +48,4 @@ public TypeLiteral[] whatToCodegen() { TypeLiteral.create(User.class) }; } - - public static void main(String[] args) throws Exception { - StaticCodeGenerator.main(new String[]{DemoCodegenConfig.class.getCanonicalName()}); - } } diff --git a/demo/src/main/java/com/jsoniter/demo/User.java b/demo/src/main/java/com/jsoniter/demo/User.java index 9f5a2e7c..9c4e5906 100644 --- a/demo/src/main/java/com/jsoniter/demo/User.java +++ b/demo/src/main/java/com/jsoniter/demo/User.java @@ -1,6 +1,7 @@ package com.jsoniter.demo; import com.jsoniter.annotation.JsonProperty; +import com.jsoniter.any.Any; public class User { @JsonProperty(nullable = false) @@ -8,4 +9,5 @@ public class User { @JsonProperty(nullable = false) public String lastName; public int score; + public Any attachment; } diff --git a/demo/src/main/java/decoder/com/jsoniter/demo/User.java b/demo/src/main/java/decoder/com/jsoniter/demo/User.java deleted file mode 100644 index 714edcb0..00000000 --- a/demo/src/main/java/decoder/com/jsoniter/demo/User.java +++ /dev/null @@ -1,68 +0,0 @@ -package decoder.com.jsoniter.demo; -public class User implements com.jsoniter.spi.Decoder { -public static java.lang.Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { if (iter.readNull()) { com.jsoniter.CodegenAccess.resetExistingObject(iter); return null; } -com.jsoniter.demo.User obj = (com.jsoniter.CodegenAccess.existingObject(iter) == null ? new com.jsoniter.demo.User() : (com.jsoniter.demo.User)com.jsoniter.CodegenAccess.resetExistingObject(iter)); -if (!com.jsoniter.CodegenAccess.readObjectStart(iter)) { return obj; } -int hash = com.jsoniter.CodegenAccess.readObjectFieldAsHash(iter); -if (hash == -1078100014) { -obj.lastName = (java.lang.String)iter.readString(); -} else { -switch (hash) { -case -1078100014: -obj.lastName = (java.lang.String)iter.readString(); -break; -case -799547430: -obj.firstName = (java.lang.String)iter.readString(); -break; -case -768634731: -obj.score = com.jsoniter.CodegenAccess.readInt("score@decoder.com.jsoniter.demo.User", iter); -break; -default: -iter.skip(); -} -} -while (true) { -if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { break; } -hash = com.jsoniter.CodegenAccess.readObjectFieldAsHash(iter); -if (hash == -799547430) { -obj.firstName = (java.lang.String)iter.readString(); -} else { -switch (hash) { -case -1078100014: -obj.lastName = (java.lang.String)iter.readString(); -continue; -case -799547430: -obj.firstName = (java.lang.String)iter.readString(); -continue; -case -768634731: -obj.score = com.jsoniter.CodegenAccess.readInt("score@decoder.com.jsoniter.demo.User", iter); -continue; -default: -iter.skip(); -} -} -if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { break; } -hash = com.jsoniter.CodegenAccess.readObjectFieldAsHash(iter); -if (hash == -768634731) { -obj.score = com.jsoniter.CodegenAccess.readInt("score@decoder.com.jsoniter.demo.User", iter); -} else { -switch (hash) { -case -1078100014: -obj.lastName = (java.lang.String)iter.readString(); -continue; -case -799547430: -obj.firstName = (java.lang.String)iter.readString(); -continue; -case -768634731: -obj.score = com.jsoniter.CodegenAccess.readInt("score@decoder.com.jsoniter.demo.User", iter); -continue; -default: -iter.skip(); -} -} -} -return obj; -}public java.lang.Object decode(com.jsoniter.JsonIterator iter) throws java.io.IOException { -return decode_(iter); -} -} diff --git a/demo/src/main/java/decoder/int_array.java b/demo/src/main/java/decoder/int_array.java deleted file mode 100644 index bbd84629..00000000 --- a/demo/src/main/java/decoder/int_array.java +++ /dev/null @@ -1,41 +0,0 @@ -package decoder; -public class int_array implements com.jsoniter.spi.Decoder { -public static java.lang.Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { com.jsoniter.CodegenAccess.resetExistingObject(iter); -if (iter.readNull()) { return null; } -if (!com.jsoniter.CodegenAccess.readArrayStart(iter)) { -return new int[0]; -} -int a1 = iter.readInt(); -if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { -return new int[]{ a1 }; -} -int a2 = iter.readInt(); -if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { -return new int[]{ a1, a2 }; -} -int a3 = iter.readInt(); -if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { -return new int[]{ a1, a2, a3 }; -} -int a4 = (int) iter.readInt(); -int[] arr = new int[8]; -arr[0] = a1; -arr[1] = a2; -arr[2] = a3; -arr[3] = a4; -int i = 4; -while (com.jsoniter.CodegenAccess.nextToken(iter) == ',') { -if (i == arr.length) { -int[] newArr = new int[arr.length * 2]; -System.arraycopy(arr, 0, newArr, 0, arr.length); -arr = newArr; -} -arr[i++] = iter.readInt(); -} -int[] result = new int[i]; -System.arraycopy(arr, 0, result, 0, i); -return result; -}public java.lang.Object decode(com.jsoniter.JsonIterator iter) throws java.io.IOException { -return decode_(iter); -} -} diff --git a/demo/src/main/java/encoder/com/jsoniter/demo/User.java b/demo/src/main/java/encoder/com/jsoniter/demo/User.java deleted file mode 100644 index 9cf7d188..00000000 --- a/demo/src/main/java/encoder/com/jsoniter/demo/User.java +++ /dev/null @@ -1,16 +0,0 @@ -package encoder.com.jsoniter.demo; -public class User extends com.jsoniter.spi.EmptyEncoder { -public void encode(Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { -if (obj == null) { stream.writeNull(); return; } -stream.writeRaw("{\"lastName\":\"", 13); -encode_((com.jsoniter.demo.User)obj, stream); -stream.write((byte)'}'); -} -public static void encode_(com.jsoniter.demo.User obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { -com.jsoniter.output.CodegenAccess.writeStringWithoutQuote((java.lang.String)obj.lastName, stream); -stream.writeRaw("\",\"firstName\":\"", 15); -com.jsoniter.output.CodegenAccess.writeStringWithoutQuote((java.lang.String)obj.firstName, stream); -stream.writeRaw("\",\"score\":", 10); -stream.writeVal((int)obj.score); -} -} diff --git a/demo/src/main/java/encoder/int_array.java b/demo/src/main/java/encoder/int_array.java deleted file mode 100644 index 3309a7b5..00000000 --- a/demo/src/main/java/encoder/int_array.java +++ /dev/null @@ -1,21 +0,0 @@ -package encoder; -public class int_array extends com.jsoniter.spi.EmptyEncoder { -public void encode(Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { -if (obj == null) { stream.writeNull(); return; } -stream.write((byte)'['); -encode_((int[])obj, stream); -stream.write((byte)']'); -} -public static void encode_(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { -int[] arr = (int[])obj; -if (arr.length == 0) { return; } -int i = 0; -int e = arr[i++]; -stream.writeVal((int)e); -while (i < arr.length) { -stream.write(','); -e = arr[i++]; -stream.writeVal((int)e); -} -} -} diff --git a/demo/src/main/java/encoder/java/util/List_com/jsoniter/demo/User.java b/demo/src/main/java/encoder/java/util/List_com/jsoniter/demo/User.java deleted file mode 100644 index f1bce9c4..00000000 --- a/demo/src/main/java/encoder/java/util/List_com/jsoniter/demo/User.java +++ /dev/null @@ -1,29 +0,0 @@ -package encoder.java.util.List_com.jsoniter.demo; -public class User extends com.jsoniter.spi.EmptyEncoder { -public void encode(Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { -if (obj == null) { stream.writeNull(); return; } -stream.write((byte)'['); -encode_((java.util.List)obj, stream); -stream.write((byte)']'); -} -public static void encode_(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { -java.util.List list = (java.util.List)obj; -int size = list.size(); -if (size == 0) { return; } -java.lang.Object e = list.get(0); -if (e == null) { stream.writeNull(); } else { -stream.writeRaw("{\"lastName\":\"", 13); -encoder.com.jsoniter.demo.User.encode_((com.jsoniter.demo.User)e, stream); -stream.write((byte)'}'); -} -for (int i = 1; i < size; i++) { -stream.write(','); -e = list.get(i); -if (e == null) { stream.writeNull(); } else { -stream.writeRaw("{\"lastName\":\"", 13); -encoder.com.jsoniter.demo.User.encode_((com.jsoniter.demo.User)e, stream); -stream.write((byte)'}'); -} -} -} -} diff --git a/demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/com/jsoniter/demo/User.java b/demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/com/jsoniter/demo/User.java new file mode 100644 index 00000000..569ab3f9 --- /dev/null +++ b/demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/com/jsoniter/demo/User.java @@ -0,0 +1,60 @@ +package jsoniter_codegen.cfg1173796797.decoder.com.jsoniter.demo; +public class User implements com.jsoniter.spi.Decoder { +public static java.lang.Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { java.lang.Object existingObj = com.jsoniter.CodegenAccess.resetExistingObject(iter); +byte nextToken = com.jsoniter.CodegenAccess.readByte(iter); +if (nextToken != '{') { +if (nextToken == 'n') { +com.jsoniter.CodegenAccess.skipFixedBytes(iter, 3); +return null; +} else { +nextToken = com.jsoniter.CodegenAccess.nextToken(iter); +if (nextToken == 'n') { +com.jsoniter.CodegenAccess.skipFixedBytes(iter, 3); +return null; +} +} // end of if null +} // end of if { +nextToken = com.jsoniter.CodegenAccess.readByte(iter); +if (nextToken != '"') { +if (nextToken == '}') { +return (existingObj == null ? new com.jsoniter.demo.User() : (com.jsoniter.demo.User)existingObj); +} else { +nextToken = com.jsoniter.CodegenAccess.nextToken(iter); +if (nextToken == '}') { +return (existingObj == null ? new com.jsoniter.demo.User() : (com.jsoniter.demo.User)existingObj); +} else { +com.jsoniter.CodegenAccess.unreadByte(iter); +} +} // end of if end +} else { com.jsoniter.CodegenAccess.unreadByte(iter); }// end of if not quote +java.lang.String _firstName_ = null; +java.lang.String _lastName_ = null; +int _score_ = 0; +com.jsoniter.any.Any _attachment_ = null; +do { +switch (com.jsoniter.CodegenAccess.readObjectFieldAsHash(iter)) { +case -1513391000: +_attachment_ = (com.jsoniter.any.Any)iter.readAny(); +continue; +case -1078100014: +_lastName_ = (java.lang.String)iter.readString(); +continue; +case -799547430: +_firstName_ = (java.lang.String)iter.readString(); +continue; +case -768634731: +_score_ = (int)com.jsoniter.CodegenAccess.readInt("score@jsoniter_codegen.cfg1173796797.decoder.com.jsoniter.demo.User", iter); +continue; +} +iter.skip(); +} while (com.jsoniter.CodegenAccess.nextTokenIsComma(iter)); +com.jsoniter.demo.User obj = (existingObj == null ? new com.jsoniter.demo.User() : (com.jsoniter.demo.User)existingObj); +obj.firstName = _firstName_; +obj.lastName = _lastName_; +obj.score = _score_; +obj.attachment = _attachment_; +return obj; +}public java.lang.Object decode(com.jsoniter.JsonIterator iter) throws java.io.IOException { +return decode_(iter); +} +} diff --git a/demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/int_array.java b/demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/int_array.java new file mode 100644 index 00000000..9ff993e7 --- /dev/null +++ b/demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/int_array.java @@ -0,0 +1,60 @@ +package jsoniter_codegen.cfg1173796797.decoder; +public class int_array implements com.jsoniter.spi.Decoder { +public static java.lang.Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { com.jsoniter.CodegenAccess.resetExistingObject(iter); +byte nextToken = com.jsoniter.CodegenAccess.readByte(iter); +if (nextToken != '[') { +if (nextToken == 'n') { +com.jsoniter.CodegenAccess.skipFixedBytes(iter, 3); +com.jsoniter.CodegenAccess.resetExistingObject(iter); return null; +} else { +nextToken = com.jsoniter.CodegenAccess.nextToken(iter); +if (nextToken == 'n') { +com.jsoniter.CodegenAccess.skipFixedBytes(iter, 3); +com.jsoniter.CodegenAccess.resetExistingObject(iter); return null; +} +} +} +nextToken = com.jsoniter.CodegenAccess.nextToken(iter); +if (nextToken == ']') { +return new int[0]; +} +com.jsoniter.CodegenAccess.unreadByte(iter); +int a1 = (int)iter.readInt(); +if (!com.jsoniter.CodegenAccess.nextTokenIsComma(iter)) { +return new int[]{ a1 }; +} +int a2 = (int)iter.readInt(); +if (!com.jsoniter.CodegenAccess.nextTokenIsComma(iter)) { +return new int[]{ a1, a2 }; +} +int a3 = (int)iter.readInt(); +if (!com.jsoniter.CodegenAccess.nextTokenIsComma(iter)) { +return new int[]{ a1, a2, a3 }; +} +int a4 = (int) (int)iter.readInt(); +if (!com.jsoniter.CodegenAccess.nextTokenIsComma(iter)) { +return new int[]{ a1, a2, a3, a4 }; +} +int a5 = (int) (int)iter.readInt(); +int[] arr = new int[10]; +arr[0] = a1; +arr[1] = a2; +arr[2] = a3; +arr[3] = a4; +arr[4] = a5; +int i = 5; +while (com.jsoniter.CodegenAccess.nextTokenIsComma(iter)) { +if (i == arr.length) { +int[] newArr = new int[arr.length * 2]; +System.arraycopy(arr, 0, newArr, 0, arr.length); +arr = newArr; +} +arr[i++] = (int)iter.readInt(); +} +int[] result = new int[i]; +System.arraycopy(arr, 0, result, 0, i); +return result; +}public java.lang.Object decode(com.jsoniter.JsonIterator iter) throws java.io.IOException { +return decode_(iter); +} +} diff --git a/demo/src/main/java/decoder/java/util/List_com/jsoniter/demo/User.java b/demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/List_com/jsoniter/demo/User.java similarity index 67% rename from demo/src/main/java/decoder/java/util/List_com/jsoniter/demo/User.java rename to demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/List_com/jsoniter/demo/User.java index d268a471..c257a011 100644 --- a/demo/src/main/java/decoder/java/util/List_com/jsoniter/demo/User.java +++ b/demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/List_com/jsoniter/demo/User.java @@ -1,24 +1,24 @@ -package decoder.java.util.List_com.jsoniter.demo; +package jsoniter_codegen.cfg1173796797.decoder.java.util.List_com.jsoniter.demo; public class User implements com.jsoniter.spi.Decoder { public static java.lang.Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { java.util.ArrayList col = (java.util.ArrayList)com.jsoniter.CodegenAccess.resetExistingObject(iter); -if (iter.readNull()) { return null; } +if (iter.readNull()) { com.jsoniter.CodegenAccess.resetExistingObject(iter); return null; } if (!com.jsoniter.CodegenAccess.readArrayStart(iter)) { return col == null ? new java.util.ArrayList(0): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); } -Object a1 = decoder.com.jsoniter.demo.User.decode_(iter); +Object a1 = (com.jsoniter.demo.User)jsoniter_codegen.cfg1173796797.decoder.com.jsoniter.demo.User.decode_(iter); if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { java.util.ArrayList obj = col == null ? new java.util.ArrayList(1): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); obj.add(a1); return obj; } -Object a2 = decoder.com.jsoniter.demo.User.decode_(iter); +Object a2 = (com.jsoniter.demo.User)jsoniter_codegen.cfg1173796797.decoder.com.jsoniter.demo.User.decode_(iter); if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { java.util.ArrayList obj = col == null ? new java.util.ArrayList(2): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); obj.add(a1); obj.add(a2); return obj; } -Object a3 = decoder.com.jsoniter.demo.User.decode_(iter); +Object a3 = (com.jsoniter.demo.User)jsoniter_codegen.cfg1173796797.decoder.com.jsoniter.demo.User.decode_(iter); if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { java.util.ArrayList obj = col == null ? new java.util.ArrayList(3): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); obj.add(a1); @@ -26,14 +26,14 @@ public class User implements com.jsoniter.spi.Decoder { obj.add(a3); return obj; } -Object a4 = decoder.com.jsoniter.demo.User.decode_(iter); +Object a4 = (com.jsoniter.demo.User)jsoniter_codegen.cfg1173796797.decoder.com.jsoniter.demo.User.decode_(iter); java.util.ArrayList obj = col == null ? new java.util.ArrayList(8): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); obj.add(a1); obj.add(a2); obj.add(a3); obj.add(a4); while (com.jsoniter.CodegenAccess.nextToken(iter) == ',') { -obj.add(decoder.com.jsoniter.demo.User.decode_(iter)); +obj.add((com.jsoniter.demo.User)jsoniter_codegen.cfg1173796797.decoder.com.jsoniter.demo.User.decode_(iter)); } return obj; }public java.lang.Object decode(com.jsoniter.JsonIterator iter) throws java.io.IOException { diff --git a/demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/List_java/lang/Integer.java b/demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/List_java/lang/Integer.java new file mode 100644 index 00000000..419b2413 --- /dev/null +++ b/demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/List_java/lang/Integer.java @@ -0,0 +1,42 @@ +package jsoniter_codegen.cfg1173796797.decoder.java.util.List_java.lang; +public class Integer implements com.jsoniter.spi.Decoder { +public static java.lang.Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { java.util.ArrayList col = (java.util.ArrayList)com.jsoniter.CodegenAccess.resetExistingObject(iter); +if (iter.readNull()) { com.jsoniter.CodegenAccess.resetExistingObject(iter); return null; } +if (!com.jsoniter.CodegenAccess.readArrayStart(iter)) { +return col == null ? new java.util.ArrayList(0): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); +} +Object a1 = (java.lang.Integer)(iter.readNull() ? null : java.lang.Integer.valueOf(iter.readInt())); +if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { +java.util.ArrayList obj = col == null ? new java.util.ArrayList(1): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); +obj.add(a1); +return obj; +} +Object a2 = (java.lang.Integer)(iter.readNull() ? null : java.lang.Integer.valueOf(iter.readInt())); +if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { +java.util.ArrayList obj = col == null ? new java.util.ArrayList(2): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); +obj.add(a1); +obj.add(a2); +return obj; +} +Object a3 = (java.lang.Integer)(iter.readNull() ? null : java.lang.Integer.valueOf(iter.readInt())); +if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') { +java.util.ArrayList obj = col == null ? new java.util.ArrayList(3): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); +obj.add(a1); +obj.add(a2); +obj.add(a3); +return obj; +} +Object a4 = (java.lang.Integer)(iter.readNull() ? null : java.lang.Integer.valueOf(iter.readInt())); +java.util.ArrayList obj = col == null ? new java.util.ArrayList(8): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col); +obj.add(a1); +obj.add(a2); +obj.add(a3); +obj.add(a4); +while (com.jsoniter.CodegenAccess.nextToken(iter) == ',') { +obj.add((java.lang.Integer)(iter.readNull() ? null : java.lang.Integer.valueOf(iter.readInt()))); +} +return obj; +}public java.lang.Object decode(com.jsoniter.JsonIterator iter) throws java.io.IOException { +return decode_(iter); +} +} diff --git a/demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/Map_java/lang/String_java/lang/Object.java b/demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/Map_java/lang/String_java/lang/Object.java new file mode 100644 index 00000000..3250faa4 --- /dev/null +++ b/demo/src/main/java/jsoniter_codegen/cfg1173796797/decoder/java/util/Map_java/lang/String_java/lang/Object.java @@ -0,0 +1,17 @@ +package jsoniter_codegen.cfg1173796797.decoder.java.util.Map_java.lang.String_java.lang; +public class Object implements com.jsoniter.spi.Decoder { +public static java.lang.Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { java.util.HashMap map = (java.util.HashMap)com.jsoniter.CodegenAccess.resetExistingObject(iter); +if (iter.readNull()) { return null; } +if (map == null) { map = new java.util.HashMap(); } +if (!com.jsoniter.CodegenAccess.readObjectStart(iter)) { +return map; +} +do { +java.lang.Object mapKey = com.jsoniter.CodegenAccess.readObjectFieldAsString(iter); +map.put(mapKey, (java.lang.Object)iter.read()); +} while (com.jsoniter.CodegenAccess.nextToken(iter) == ','); +return map; +}public java.lang.Object decode(com.jsoniter.JsonIterator iter) throws java.io.IOException { +return decode_(iter); +} +} diff --git a/demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/com/jsoniter/demo/User.java b/demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/com/jsoniter/demo/User.java new file mode 100644 index 00000000..03ccb36e --- /dev/null +++ b/demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/com/jsoniter/demo/User.java @@ -0,0 +1,25 @@ +package jsoniter_codegen.cfg1173796797.encoder.com.jsoniter.demo; +public class User implements com.jsoniter.spi.Encoder { +public void encode(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +if (obj == null) { stream.writeNull(); return; } +encode_((com.jsoniter.demo.User)obj, stream); +} +public static void encode_(com.jsoniter.demo.User obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +stream.writeObjectStart(); +stream.writeIndention(); +stream.writeObjectField("firstName"); +stream.writeVal((java.lang.String)obj.firstName); +stream.writeMore(); +stream.writeObjectField("lastName"); +stream.writeVal((java.lang.String)obj.lastName); +stream.writeMore(); +stream.writeObjectField("score"); +stream.writeVal((int)obj.score); +stream.writeMore(); +stream.writeObjectField("attachment"); +if (obj.attachment == null) { stream.writeNull(); } else { +stream.writeVal((com.jsoniter.any.Any)obj.attachment); +} +stream.writeObjectEnd(); +} +} diff --git a/demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/int_array.java b/demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/int_array.java new file mode 100644 index 00000000..e799b768 --- /dev/null +++ b/demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/int_array.java @@ -0,0 +1,21 @@ +package jsoniter_codegen.cfg1173796797.encoder; +public class int_array implements com.jsoniter.spi.Encoder { +public void encode(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +if (obj == null) { stream.writeNull(); return; } +encode_((int[])obj, stream); +} +public static void encode_(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +int[] arr = (int[])obj; +if (arr.length == 0) { stream.write((byte)'[', (byte)']'); return; } +stream.writeArrayStart(); stream.writeIndention(); +int i = 0; +int e = arr[i++]; +stream.writeVal((int)e); +while (i < arr.length) { +stream.writeMore(); +e = arr[i++]; +stream.writeVal((int)e); +} +stream.writeArrayEnd(); +} +} diff --git a/demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/List_com/jsoniter/demo/User.java b/demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/List_com/jsoniter/demo/User.java new file mode 100644 index 00000000..ab31cdd9 --- /dev/null +++ b/demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/List_com/jsoniter/demo/User.java @@ -0,0 +1,29 @@ +package jsoniter_codegen.cfg1173796797.encoder.java.util.List_com.jsoniter.demo; +public class User implements com.jsoniter.spi.Encoder { +public void encode(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +if (obj == null) { stream.writeNull(); return; } +encode_((java.util.List)obj, stream); +} +public static void encode_(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +java.util.List list = (java.util.List)obj; +int size = list.size(); +if (size == 0) { stream.write((byte)'[', (byte)']'); return; } +stream.writeArrayStart(); stream.writeIndention(); +java.lang.Object e = list.get(0); +if (e == null) { stream.writeNull(); } else { + +jsoniter_codegen.cfg1173796797.encoder.com.jsoniter.demo.User.encode_((com.jsoniter.demo.User)e, stream); + +} +for (int i = 1; i < size; i++) { +stream.writeMore(); +e = list.get(i); +if (e == null) { stream.writeNull(); } else { + +jsoniter_codegen.cfg1173796797.encoder.com.jsoniter.demo.User.encode_((com.jsoniter.demo.User)e, stream); + +} +} +stream.writeArrayEnd(); +} +} diff --git a/demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/List_java/lang/Integer.java b/demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/List_java/lang/Integer.java new file mode 100644 index 00000000..0e2069fb --- /dev/null +++ b/demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/List_java/lang/Integer.java @@ -0,0 +1,25 @@ +package jsoniter_codegen.cfg1173796797.encoder.java.util.List_java.lang; +public class Integer implements com.jsoniter.spi.Encoder { +public void encode(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +if (obj == null) { stream.writeNull(); return; } +encode_((java.util.List)obj, stream); +} +public static void encode_(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +java.util.List list = (java.util.List)obj; +int size = list.size(); +if (size == 0) { stream.write((byte)'[', (byte)']'); return; } +stream.writeArrayStart(); stream.writeIndention(); +java.lang.Object e = list.get(0); +if (e == null) { stream.writeNull(); } else { +stream.writeVal((java.lang.Integer)e); +} +for (int i = 1; i < size; i++) { +stream.writeMore(); +e = list.get(i); +if (e == null) { stream.writeNull(); } else { +stream.writeVal((java.lang.Integer)e); +} +} +stream.writeArrayEnd(); +} +} diff --git a/demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/Map_java/lang/String_java/lang/Object.java b/demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/Map_java/lang/String_java/lang/Object.java new file mode 100644 index 00000000..f2bd7203 --- /dev/null +++ b/demo/src/main/java/jsoniter_codegen/cfg1173796797/encoder/java/util/Map_java/lang/String_java/lang/Object.java @@ -0,0 +1,30 @@ +package jsoniter_codegen.cfg1173796797.encoder.java.util.Map_java.lang.String_java.lang; +public class Object implements com.jsoniter.spi.Encoder { +public void encode(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +if (obj == null) { stream.writeNull(); return; } +encode_((java.util.Map)obj, stream); +} +public static void encode_(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException { +if (obj == null) { stream.writeNull(); return; } +java.util.Map map = (java.util.Map)obj; +java.util.Iterator iter = map.entrySet().iterator(); +if(!iter.hasNext()) { stream.write((byte)'{', (byte)'}'); return; } +java.util.Map.Entry entry = (java.util.Map.Entry)iter.next(); +stream.writeObjectStart(); stream.writeIndention(); +stream.writeVal((java.lang.String)entry.getKey()); +stream.write((byte)':', (byte)' '); +if (entry.getValue() == null) { stream.writeNull(); } else { +stream.writeVal((java.lang.Object)entry.getValue()); +} +while(iter.hasNext()) { +entry = (java.util.Map.Entry)iter.next(); +stream.writeMore(); +stream.writeVal((java.lang.String)entry.getKey()); +stream.write((byte)':', (byte)' '); +if (entry.getValue() == null) { stream.writeNull(); } else { +stream.writeVal((java.lang.Object)entry.getValue()); +} +} +stream.writeObjectEnd(); +} +} diff --git a/demo/src/test/java/com/dslplatform/json/CustomJsonReader.java b/demo/src/test/java/com/dslplatform/json/CustomJsonReader.java deleted file mode 100644 index 4bc3ff47..00000000 --- a/demo/src/test/java/com/dslplatform/json/CustomJsonReader.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.dslplatform.json; - -public class CustomJsonReader extends JsonReader { - public CustomJsonReader(byte[] buffer) { - super(buffer, null); - } - public void reset() { - super.reset(length()); - } -} diff --git a/demo/src/test/java/com/dslplatform/json/ExternalSerialization.java b/demo/src/test/java/com/dslplatform/json/ExternalSerialization.java deleted file mode 100644 index 3c11e36e..00000000 --- a/demo/src/test/java/com/dslplatform/json/ExternalSerialization.java +++ /dev/null @@ -1,1532 +0,0 @@ -/* -* Created by DSL Platform -* v1.7.6218.18384 -*/ - -package com.dslplatform.json; - - - -public class ExternalSerialization implements Configuration { - - - @SuppressWarnings("unchecked") - public void configure(final DslJson json) { - setup(json); - } - - @SuppressWarnings("unchecked") - public static void setup(final DslJson json) { - - - json.registerReader(com.jsoniter.demo.object_with_1_double_field.TestObject.class, JSON_READER_struct5); - json.registerWriter(com.jsoniter.demo.object_with_1_double_field.TestObject.class, new JsonWriter.WriteObject() { - @Override - public void write(JsonWriter writer, com.jsoniter.demo.object_with_1_double_field.TestObject value) { - serialize(value, writer, json.omitDefaults); - } - }); - - json.registerReader(com.jsoniter.demo.object_with_1_field.TestObject.class, JSON_READER_struct1); - json.registerWriter(com.jsoniter.demo.object_with_1_field.TestObject.class, new JsonWriter.WriteObject() { - @Override - public void write(JsonWriter writer, com.jsoniter.demo.object_with_1_field.TestObject value) { - serialize(value, writer, json.omitDefaults); - } - }); - - json.registerReader(com.jsoniter.demo.object_with_10_fields.TestObject.class, JSON_READER_struct6); - json.registerWriter(com.jsoniter.demo.object_with_10_fields.TestObject.class, new JsonWriter.WriteObject() { - @Override - public void write(JsonWriter writer, com.jsoniter.demo.object_with_10_fields.TestObject value) { - serialize(value, writer, json.omitDefaults); - } - }); - - json.registerReader(com.jsoniter.demo.object_with_1_int_field.TestObject.class, JSON_READER_struct0); - json.registerWriter(com.jsoniter.demo.object_with_1_int_field.TestObject.class, new JsonWriter.WriteObject() { - @Override - public void write(JsonWriter writer, com.jsoniter.demo.object_with_1_int_field.TestObject value) { - serialize(value, writer, json.omitDefaults); - } - }); - - json.registerReader(com.jsoniter.demo.object_with_5_fields.TestObject.class, JSON_READER_struct2); - json.registerWriter(com.jsoniter.demo.object_with_5_fields.TestObject.class, new JsonWriter.WriteObject() { - @Override - public void write(JsonWriter writer, com.jsoniter.demo.object_with_5_fields.TestObject value) { - serialize(value, writer, json.omitDefaults); - } - }); - - json.registerReader(com.jsoniter.demo.object_with_15_fields.TestObject.class, JSON_READER_struct4); - json.registerWriter(com.jsoniter.demo.object_with_15_fields.TestObject.class, new JsonWriter.WriteObject() { - @Override - public void write(JsonWriter writer, com.jsoniter.demo.object_with_15_fields.TestObject value) { - serialize(value, writer, json.omitDefaults); - } - }); - - json.registerReader(com.jsoniter.demo.SimpleObjectBinding.TestObject.class, JSON_READER_struct3); - json.registerWriter(com.jsoniter.demo.SimpleObjectBinding.TestObject.class, new JsonWriter.WriteObject() { - @Override - public void write(JsonWriter writer, com.jsoniter.demo.SimpleObjectBinding.TestObject value) { - serialize(value, writer, json.omitDefaults); - } - }); - } - - public static void serialize(final com.jsoniter.demo.object_with_1_double_field.TestObject self, final JsonWriter sw, final boolean minimal) { - sw.writeByte(JsonWriter.OBJECT_START); - if (minimal) { - __serializeJsonObjectMinimal(self, sw, false); - } else { - __serializeJsonObjectFull(self, sw, false); - } - sw.writeByte(JsonWriter.OBJECT_END); - } - - static void __serializeJsonObjectMinimal(final com.jsoniter.demo.object_with_1_double_field.TestObject self, JsonWriter sw, boolean hasWrittenProperty) { - - - if (self.field1 != 0.0) { - hasWrittenProperty = true; - sw.writeAscii("\"field1\":", 9); - NumberConverter.serialize(self.field1, sw); - } - } - - static void __serializeJsonObjectFull(final com.jsoniter.demo.object_with_1_double_field.TestObject self, JsonWriter sw, boolean hasWrittenProperty) { - - - - sw.writeAscii("\"field1\":", 9); - NumberConverter.serialize(self.field1, sw); - } - - public static final JsonReader.ReadObject JSON_READER_struct5 = new JsonReader.ReadObject() { - @SuppressWarnings("unchecked") - @Override - public com.jsoniter.demo.object_with_1_double_field.TestObject read(final JsonReader reader) throws java.io.IOException { - if(reader.last() != '{') { - throw new java.io.IOException("Expecting \'{\' at position " + reader.positionInStream() + ". Found " + (char)reader.last()); - } - reader.getNextToken(); - final com.jsoniter.demo.object_with_1_double_field.TestObject instance = new com.jsoniter.demo.object_with_1_double_field.TestObject(); - deserialize(instance, reader); - return instance; - } - }; - - @SuppressWarnings("unchecked") - static com.jsoniter.demo.object_with_1_double_field.TestObject deserializestruct5(final JsonReader reader) throws java.io.IOException { - final com.jsoniter.demo.object_with_1_double_field.TestObject instance = new com.jsoniter.demo.object_with_1_double_field.TestObject(); - deserialize(instance, reader); - return instance; - } - - @SuppressWarnings("unchecked") - static void deserialize(final com.jsoniter.demo.object_with_1_double_field.TestObject instance, final JsonReader reader) throws java.io.IOException { - - double _field1_ = 0.0; - byte nextToken = reader.last(); - if(nextToken != '}') { - int nameHash = reader.fillName(); - nextToken = reader.getNextToken(); - if(nextToken == 'n') { - if (reader.wasNull()) { - nextToken = reader.getNextToken(); - } else { - throw new java.io.IOException("Expecting 'u' (as null) at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } else { - switch(nameHash) { - - case 1212206434: - _field1_ = NumberConverter.deserializeDouble(reader); - nextToken = reader.getNextToken(); - break; - default: - nextToken = reader.skip(); - break; - } - } - while (nextToken == ',') { - nextToken = reader.getNextToken(); - nameHash = reader.fillName(); - nextToken = reader.getNextToken(); - if(nextToken == 'n') { - if (reader.wasNull()) { - nextToken = reader.getNextToken(); - continue; - } else { - throw new java.io.IOException("Expecting 'u' (as null) at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } - switch(nameHash) { - - case 1212206434: - _field1_ = NumberConverter.deserializeDouble(reader); - nextToken = reader.getNextToken(); - break; - default: - nextToken = reader.skip(); - break; - } - } - if (nextToken != '}') { - throw new java.io.IOException("Expecting '}' at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } - - instance.field1 = _field1_; - } - - public static void serialize(final com.jsoniter.demo.object_with_1_field.TestObject self, final JsonWriter sw, final boolean minimal) { - sw.writeByte(JsonWriter.OBJECT_START); - if (minimal) { - __serializeJsonObjectMinimal(self, sw, false); - } else { - __serializeJsonObjectFull(self, sw, false); - } - sw.writeByte(JsonWriter.OBJECT_END); - } - - static void __serializeJsonObjectMinimal(final com.jsoniter.demo.object_with_1_field.TestObject self, JsonWriter sw, boolean hasWrittenProperty) { - - - if (self.field1 != null) { - hasWrittenProperty = true; - sw.writeAscii("\"field1\":", 9); - sw.writeString(self.field1); - } - } - - static void __serializeJsonObjectFull(final com.jsoniter.demo.object_with_1_field.TestObject self, JsonWriter sw, boolean hasWrittenProperty) { - - - - if (self.field1 != null) { - sw.writeAscii("\"field1\":", 9); - sw.writeString(self.field1); - } else { - sw.writeAscii("\"field1\":null", 13); - } - } - - public static final JsonReader.ReadObject JSON_READER_struct1 = new JsonReader.ReadObject() { - @SuppressWarnings("unchecked") - @Override - public com.jsoniter.demo.object_with_1_field.TestObject read(final JsonReader reader) throws java.io.IOException { - if(reader.last() != '{') { - throw new java.io.IOException("Expecting \'{\' at position " + reader.positionInStream() + ". Found " + (char)reader.last()); - } - reader.getNextToken(); - final com.jsoniter.demo.object_with_1_field.TestObject instance = new com.jsoniter.demo.object_with_1_field.TestObject(); - deserialize(instance, reader); - return instance; - } - }; - - @SuppressWarnings("unchecked") - static com.jsoniter.demo.object_with_1_field.TestObject deserializestruct1(final JsonReader reader) throws java.io.IOException { - final com.jsoniter.demo.object_with_1_field.TestObject instance = new com.jsoniter.demo.object_with_1_field.TestObject(); - deserialize(instance, reader); - return instance; - } - - @SuppressWarnings("unchecked") - static void deserialize(final com.jsoniter.demo.object_with_1_field.TestObject instance, final JsonReader reader) throws java.io.IOException { - - String _field1_ = null; - byte nextToken = reader.last(); - if(nextToken != '}') { - int nameHash = reader.fillName(); - nextToken = reader.getNextToken(); - if(nextToken == 'n') { - if (reader.wasNull()) { - nextToken = reader.getNextToken(); - } else { - throw new java.io.IOException("Expecting 'u' (as null) at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } else { - switch(nameHash) { - - case 1212206434: - _field1_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - default: - nextToken = reader.skip(); - break; - } - } - while (nextToken == ',') { - nextToken = reader.getNextToken(); - nameHash = reader.fillName(); - nextToken = reader.getNextToken(); - if(nextToken == 'n') { - if (reader.wasNull()) { - nextToken = reader.getNextToken(); - continue; - } else { - throw new java.io.IOException("Expecting 'u' (as null) at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } - switch(nameHash) { - - case 1212206434: - _field1_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - default: - nextToken = reader.skip(); - break; - } - } - if (nextToken != '}') { - throw new java.io.IOException("Expecting '}' at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } - - instance.field1 = _field1_; - } - - public static void serialize(final com.jsoniter.demo.object_with_10_fields.TestObject self, final JsonWriter sw, final boolean minimal) { - sw.writeByte(JsonWriter.OBJECT_START); - if (minimal) { - __serializeJsonObjectMinimal(self, sw, false); - } else { - __serializeJsonObjectFull(self, sw, false); - } - sw.writeByte(JsonWriter.OBJECT_END); - } - - static void __serializeJsonObjectMinimal(final com.jsoniter.demo.object_with_10_fields.TestObject self, JsonWriter sw, boolean hasWrittenProperty) { - - - if (self.field1 != null) { - hasWrittenProperty = true; - sw.writeAscii("\"field1\":", 9); - sw.writeString(self.field1); - } - - if (self.field10 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field10\":", 10); - sw.writeString(self.field10); - } - - if (self.field7 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field7\":", 9); - sw.writeString(self.field7); - } - - if (self.field6 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field6\":", 9); - sw.writeString(self.field6); - } - - if (self.field9 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field9\":", 9); - sw.writeString(self.field9); - } - - if (self.field8 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field8\":", 9); - sw.writeString(self.field8); - } - - if (self.field3 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field3\":", 9); - sw.writeString(self.field3); - } - - if (self.field2 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field2\":", 9); - sw.writeString(self.field2); - } - - if (self.field5 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field5\":", 9); - sw.writeString(self.field5); - } - - if (self.field4 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field4\":", 9); - sw.writeString(self.field4); - } - } - - static void __serializeJsonObjectFull(final com.jsoniter.demo.object_with_10_fields.TestObject self, JsonWriter sw, boolean hasWrittenProperty) { - - - - if (self.field1 != null) { - sw.writeAscii("\"field1\":", 9); - sw.writeString(self.field1); - } else { - sw.writeAscii("\"field1\":null", 13); - } - - - if (self.field10 != null) { - sw.writeAscii(",\"field10\":", 11); - sw.writeString(self.field10); - } else { - sw.writeAscii(",\"field10\":null", 15); - } - - - if (self.field7 != null) { - sw.writeAscii(",\"field7\":", 10); - sw.writeString(self.field7); - } else { - sw.writeAscii(",\"field7\":null", 14); - } - - - if (self.field6 != null) { - sw.writeAscii(",\"field6\":", 10); - sw.writeString(self.field6); - } else { - sw.writeAscii(",\"field6\":null", 14); - } - - - if (self.field9 != null) { - sw.writeAscii(",\"field9\":", 10); - sw.writeString(self.field9); - } else { - sw.writeAscii(",\"field9\":null", 14); - } - - - if (self.field8 != null) { - sw.writeAscii(",\"field8\":", 10); - sw.writeString(self.field8); - } else { - sw.writeAscii(",\"field8\":null", 14); - } - - - if (self.field3 != null) { - sw.writeAscii(",\"field3\":", 10); - sw.writeString(self.field3); - } else { - sw.writeAscii(",\"field3\":null", 14); - } - - - if (self.field2 != null) { - sw.writeAscii(",\"field2\":", 10); - sw.writeString(self.field2); - } else { - sw.writeAscii(",\"field2\":null", 14); - } - - - if (self.field5 != null) { - sw.writeAscii(",\"field5\":", 10); - sw.writeString(self.field5); - } else { - sw.writeAscii(",\"field5\":null", 14); - } - - - if (self.field4 != null) { - sw.writeAscii(",\"field4\":", 10); - sw.writeString(self.field4); - } else { - sw.writeAscii(",\"field4\":null", 14); - } - } - - public static final JsonReader.ReadObject JSON_READER_struct6 = new JsonReader.ReadObject() { - @SuppressWarnings("unchecked") - @Override - public com.jsoniter.demo.object_with_10_fields.TestObject read(final JsonReader reader) throws java.io.IOException { - if(reader.last() != '{') { - throw new java.io.IOException("Expecting \'{\' at position " + reader.positionInStream() + ". Found " + (char)reader.last()); - } - reader.getNextToken(); - final com.jsoniter.demo.object_with_10_fields.TestObject instance = new com.jsoniter.demo.object_with_10_fields.TestObject(); - deserialize(instance, reader); - return instance; - } - }; - - @SuppressWarnings("unchecked") - static com.jsoniter.demo.object_with_10_fields.TestObject deserializestruct6(final JsonReader reader) throws java.io.IOException { - final com.jsoniter.demo.object_with_10_fields.TestObject instance = new com.jsoniter.demo.object_with_10_fields.TestObject(); - deserialize(instance, reader); - return instance; - } - - @SuppressWarnings("unchecked") - public static void deserialize(final com.jsoniter.demo.object_with_10_fields.TestObject instance, final JsonReader reader) throws java.io.IOException { - - String _field1_ = null; - String _field10_ = null; - String _field7_ = null; - String _field6_ = null; - String _field9_ = null; - String _field8_ = null; - String _field3_ = null; - String _field2_ = null; - String _field5_ = null; - String _field4_ = null; - byte nextToken = reader.last(); - if(nextToken != '}') { - int nameHash = reader.fillName(); - nextToken = reader.getNextToken(); - if(nextToken == 'n') { - if (reader.wasNull()) { - nextToken = reader.getNextToken(); - } else { - throw new java.io.IOException("Expecting 'u' (as null) at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } else { - switch(nameHash) { - - case 1212206434: - _field1_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 268646422: - _field10_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1111540720: - _field7_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1128318339: - _field6_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1346427386: - _field9_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1363205005: - _field8_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1178651196: - _field3_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1195428815: - _field2_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1145095958: - _field5_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1161873577: - _field4_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - default: - nextToken = reader.skip(); - break; - } - } - while (nextToken == ',') { - nextToken = reader.getNextToken(); - nameHash = reader.fillName(); - nextToken = reader.getNextToken(); - if(nextToken == 'n') { - if (reader.wasNull()) { - nextToken = reader.getNextToken(); - continue; - } else { - throw new java.io.IOException("Expecting 'u' (as null) at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } - switch(nameHash) { - - case 1212206434: - _field1_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 268646422: - _field10_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1111540720: - _field7_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1128318339: - _field6_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1346427386: - _field9_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1363205005: - _field8_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1178651196: - _field3_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1195428815: - _field2_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1145095958: - _field5_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1161873577: - _field4_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - default: - nextToken = reader.skip(); - break; - } - } - if (nextToken != '}') { - throw new java.io.IOException("Expecting '}' at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } - - instance.field1 = _field1_; - instance.field10 = _field10_; - instance.field7 = _field7_; - instance.field6 = _field6_; - instance.field9 = _field9_; - instance.field8 = _field8_; - instance.field3 = _field3_; - instance.field2 = _field2_; - instance.field5 = _field5_; - instance.field4 = _field4_; - } - - public static void serialize(final com.jsoniter.demo.object_with_1_int_field.TestObject self, final JsonWriter sw, final boolean minimal) { - sw.writeByte(JsonWriter.OBJECT_START); - if (minimal) { - __serializeJsonObjectMinimal(self, sw, false); - } else { - __serializeJsonObjectFull(self, sw, false); - } - sw.writeByte(JsonWriter.OBJECT_END); - } - - static void __serializeJsonObjectMinimal(final com.jsoniter.demo.object_with_1_int_field.TestObject self, JsonWriter sw, boolean hasWrittenProperty) { - - - if (self.field1 != 0) { - hasWrittenProperty = true; - sw.writeAscii("\"field1\":", 9); - NumberConverter.serialize(self.field1, sw); - } - } - - static void __serializeJsonObjectFull(final com.jsoniter.demo.object_with_1_int_field.TestObject self, JsonWriter sw, boolean hasWrittenProperty) { - - - - sw.writeAscii("\"field1\":", 9); - NumberConverter.serialize(self.field1, sw); - } - - public static final JsonReader.ReadObject JSON_READER_struct0 = new JsonReader.ReadObject() { - @SuppressWarnings("unchecked") - @Override - public com.jsoniter.demo.object_with_1_int_field.TestObject read(final JsonReader reader) throws java.io.IOException { - if(reader.last() != '{') { - throw new java.io.IOException("Expecting \'{\' at position " + reader.positionInStream() + ". Found " + (char)reader.last()); - } - reader.getNextToken(); - final com.jsoniter.demo.object_with_1_int_field.TestObject instance = new com.jsoniter.demo.object_with_1_int_field.TestObject(); - deserialize(instance, reader); - return instance; - } - }; - - @SuppressWarnings("unchecked") - static com.jsoniter.demo.object_with_1_int_field.TestObject deserializestruct0(final JsonReader reader) throws java.io.IOException { - final com.jsoniter.demo.object_with_1_int_field.TestObject instance = new com.jsoniter.demo.object_with_1_int_field.TestObject(); - deserialize(instance, reader); - return instance; - } - - @SuppressWarnings("unchecked") - static void deserialize(final com.jsoniter.demo.object_with_1_int_field.TestObject instance, final JsonReader reader) throws java.io.IOException { - - int _field1_ = 0; - byte nextToken = reader.last(); - if(nextToken != '}') { - int nameHash = reader.fillName(); - nextToken = reader.getNextToken(); - if(nextToken == 'n') { - if (reader.wasNull()) { - nextToken = reader.getNextToken(); - } else { - throw new java.io.IOException("Expecting 'u' (as null) at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } else { - switch(nameHash) { - - case 1212206434: - _field1_ = NumberConverter.deserializeInt(reader); - nextToken = reader.getNextToken(); - break; - default: - nextToken = reader.skip(); - break; - } - } - while (nextToken == ',') { - nextToken = reader.getNextToken(); - nameHash = reader.fillName(); - nextToken = reader.getNextToken(); - if(nextToken == 'n') { - if (reader.wasNull()) { - nextToken = reader.getNextToken(); - continue; - } else { - throw new java.io.IOException("Expecting 'u' (as null) at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } - switch(nameHash) { - - case 1212206434: - _field1_ = NumberConverter.deserializeInt(reader); - nextToken = reader.getNextToken(); - break; - default: - nextToken = reader.skip(); - break; - } - } - if (nextToken != '}') { - throw new java.io.IOException("Expecting '}' at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } - - instance.field1 = _field1_; - } - - public static void serialize(final com.jsoniter.demo.object_with_5_fields.TestObject self, final JsonWriter sw, final boolean minimal) { - sw.writeByte(JsonWriter.OBJECT_START); - if (minimal) { - __serializeJsonObjectMinimal(self, sw, false); - } else { - __serializeJsonObjectFull(self, sw, false); - } - sw.writeByte(JsonWriter.OBJECT_END); - } - - static void __serializeJsonObjectMinimal(final com.jsoniter.demo.object_with_5_fields.TestObject self, JsonWriter sw, boolean hasWrittenProperty) { - - - if (self.field1 != null) { - hasWrittenProperty = true; - sw.writeAscii("\"field1\":", 9); - sw.writeString(self.field1); - } - - if (self.field3 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field3\":", 9); - sw.writeString(self.field3); - } - - if (self.field2 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field2\":", 9); - sw.writeString(self.field2); - } - - if (self.field5 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field5\":", 9); - sw.writeString(self.field5); - } - - if (self.field4 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field4\":", 9); - sw.writeString(self.field4); - } - } - - static void __serializeJsonObjectFull(final com.jsoniter.demo.object_with_5_fields.TestObject self, JsonWriter sw, boolean hasWrittenProperty) { - - - - if (self.field1 != null) { - sw.writeAscii("\"field1\":", 9); - sw.writeString(self.field1); - } else { - sw.writeAscii("\"field1\":null", 13); - } - - - if (self.field3 != null) { - sw.writeAscii(",\"field3\":", 10); - sw.writeString(self.field3); - } else { - sw.writeAscii(",\"field3\":null", 14); - } - - - if (self.field2 != null) { - sw.writeAscii(",\"field2\":", 10); - sw.writeString(self.field2); - } else { - sw.writeAscii(",\"field2\":null", 14); - } - - - if (self.field5 != null) { - sw.writeAscii(",\"field5\":", 10); - sw.writeString(self.field5); - } else { - sw.writeAscii(",\"field5\":null", 14); - } - - - if (self.field4 != null) { - sw.writeAscii(",\"field4\":", 10); - sw.writeString(self.field4); - } else { - sw.writeAscii(",\"field4\":null", 14); - } - } - - public static final JsonReader.ReadObject JSON_READER_struct2 = new JsonReader.ReadObject() { - @SuppressWarnings("unchecked") - @Override - public com.jsoniter.demo.object_with_5_fields.TestObject read(final JsonReader reader) throws java.io.IOException { - if(reader.last() != '{') { - throw new java.io.IOException("Expecting \'{\' at position " + reader.positionInStream() + ". Found " + (char)reader.last()); - } - reader.getNextToken(); - final com.jsoniter.demo.object_with_5_fields.TestObject instance = new com.jsoniter.demo.object_with_5_fields.TestObject(); - deserialize(instance, reader); - return instance; - } - }; - - @SuppressWarnings("unchecked") - static com.jsoniter.demo.object_with_5_fields.TestObject deserializestruct2(final JsonReader reader) throws java.io.IOException { - final com.jsoniter.demo.object_with_5_fields.TestObject instance = new com.jsoniter.demo.object_with_5_fields.TestObject(); - deserialize(instance, reader); - return instance; - } - - @SuppressWarnings("unchecked") - public static void deserialize(final com.jsoniter.demo.object_with_5_fields.TestObject instance, final JsonReader reader) throws java.io.IOException { - - String _field1_ = null; - String _field3_ = null; - String _field2_ = null; - String _field5_ = null; - String _field4_ = null; - byte nextToken = reader.last(); - if(nextToken != '}') { - int nameHash = reader.fillName(); - nextToken = reader.getNextToken(); - if(nextToken == 'n') { - if (reader.wasNull()) { - nextToken = reader.getNextToken(); - } else { - throw new java.io.IOException("Expecting 'u' (as null) at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } else { - switch(nameHash) { - - case 1212206434: - _field1_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1178651196: - _field3_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1195428815: - _field2_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1145095958: - _field5_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1161873577: - _field4_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - default: - nextToken = reader.skip(); - break; - } - } - while (nextToken == ',') { - nextToken = reader.getNextToken(); - nameHash = reader.fillName(); - nextToken = reader.getNextToken(); - if(nextToken == 'n') { - if (reader.wasNull()) { - nextToken = reader.getNextToken(); - continue; - } else { - throw new java.io.IOException("Expecting 'u' (as null) at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } - switch(nameHash) { - - case 1212206434: - _field1_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1178651196: - _field3_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1195428815: - _field2_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1145095958: - _field5_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1161873577: - _field4_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - default: - nextToken = reader.skip(); - break; - } - } - if (nextToken != '}') { - throw new java.io.IOException("Expecting '}' at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } - - instance.field1 = _field1_; - instance.field3 = _field3_; - instance.field2 = _field2_; - instance.field5 = _field5_; - instance.field4 = _field4_; - } - - public static void serialize(final com.jsoniter.demo.object_with_15_fields.TestObject self, final JsonWriter sw, final boolean minimal) { - sw.writeByte(JsonWriter.OBJECT_START); - if (minimal) { - __serializeJsonObjectMinimal(self, sw, false); - } else { - __serializeJsonObjectFull(self, sw, false); - } - sw.writeByte(JsonWriter.OBJECT_END); - } - - static void __serializeJsonObjectMinimal(final com.jsoniter.demo.object_with_15_fields.TestObject self, JsonWriter sw, boolean hasWrittenProperty) { - - - if (self.field11 != null) { - hasWrittenProperty = true; - sw.writeAscii("\"field11\":", 10); - sw.writeString(self.field11); - } - - if (self.field12 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field12\":", 10); - sw.writeString(self.field12); - } - - if (self.field1 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field1\":", 9); - sw.writeString(self.field1); - } - - if (self.field10 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field10\":", 10); - sw.writeString(self.field10); - } - - if (self.field15 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field15\":", 10); - sw.writeString(self.field15); - } - - if (self.field13 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field13\":", 10); - sw.writeString(self.field13); - } - - if (self.field14 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field14\":", 10); - sw.writeString(self.field14); - } - - if (self.field7 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field7\":", 9); - sw.writeString(self.field7); - } - - if (self.field6 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field6\":", 9); - sw.writeString(self.field6); - } - - if (self.field9 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field9\":", 9); - sw.writeString(self.field9); - } - - if (self.field8 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field8\":", 9); - sw.writeString(self.field8); - } - - if (self.field3 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field3\":", 9); - sw.writeString(self.field3); - } - - if (self.field2 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field2\":", 9); - sw.writeString(self.field2); - } - - if (self.field5 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field5\":", 9); - sw.writeString(self.field5); - } - - if (self.field4 != null) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field4\":", 9); - sw.writeString(self.field4); - } - } - - static void __serializeJsonObjectFull(final com.jsoniter.demo.object_with_15_fields.TestObject self, JsonWriter sw, boolean hasWrittenProperty) { - - - - if (self.field11 != null) { - sw.writeAscii("\"field11\":", 10); - sw.writeString(self.field11); - } else { - sw.writeAscii("\"field11\":null", 14); - } - - - if (self.field12 != null) { - sw.writeAscii(",\"field12\":", 11); - sw.writeString(self.field12); - } else { - sw.writeAscii(",\"field12\":null", 15); - } - - - if (self.field1 != null) { - sw.writeAscii(",\"field1\":", 10); - sw.writeString(self.field1); - } else { - sw.writeAscii(",\"field1\":null", 14); - } - - - if (self.field10 != null) { - sw.writeAscii(",\"field10\":", 11); - sw.writeString(self.field10); - } else { - sw.writeAscii(",\"field10\":null", 15); - } - - - if (self.field15 != null) { - sw.writeAscii(",\"field15\":", 11); - sw.writeString(self.field15); - } else { - sw.writeAscii(",\"field15\":null", 15); - } - - - if (self.field13 != null) { - sw.writeAscii(",\"field13\":", 11); - sw.writeString(self.field13); - } else { - sw.writeAscii(",\"field13\":null", 15); - } - - - if (self.field14 != null) { - sw.writeAscii(",\"field14\":", 11); - sw.writeString(self.field14); - } else { - sw.writeAscii(",\"field14\":null", 15); - } - - - if (self.field7 != null) { - sw.writeAscii(",\"field7\":", 10); - sw.writeString(self.field7); - } else { - sw.writeAscii(",\"field7\":null", 14); - } - - - if (self.field6 != null) { - sw.writeAscii(",\"field6\":", 10); - sw.writeString(self.field6); - } else { - sw.writeAscii(",\"field6\":null", 14); - } - - - if (self.field9 != null) { - sw.writeAscii(",\"field9\":", 10); - sw.writeString(self.field9); - } else { - sw.writeAscii(",\"field9\":null", 14); - } - - - if (self.field8 != null) { - sw.writeAscii(",\"field8\":", 10); - sw.writeString(self.field8); - } else { - sw.writeAscii(",\"field8\":null", 14); - } - - - if (self.field3 != null) { - sw.writeAscii(",\"field3\":", 10); - sw.writeString(self.field3); - } else { - sw.writeAscii(",\"field3\":null", 14); - } - - - if (self.field2 != null) { - sw.writeAscii(",\"field2\":", 10); - sw.writeString(self.field2); - } else { - sw.writeAscii(",\"field2\":null", 14); - } - - - if (self.field5 != null) { - sw.writeAscii(",\"field5\":", 10); - sw.writeString(self.field5); - } else { - sw.writeAscii(",\"field5\":null", 14); - } - - - if (self.field4 != null) { - sw.writeAscii(",\"field4\":", 10); - sw.writeString(self.field4); - } else { - sw.writeAscii(",\"field4\":null", 14); - } - } - - public static final JsonReader.ReadObject JSON_READER_struct4 = new JsonReader.ReadObject() { - @SuppressWarnings("unchecked") - @Override - public com.jsoniter.demo.object_with_15_fields.TestObject read(final JsonReader reader) throws java.io.IOException { - if(reader.last() != '{') { - throw new java.io.IOException("Expecting \'{\' at position " + reader.positionInStream() + ". Found " + (char)reader.last()); - } - reader.getNextToken(); - final com.jsoniter.demo.object_with_15_fields.TestObject instance = new com.jsoniter.demo.object_with_15_fields.TestObject(); - deserialize(instance, reader); - return instance; - } - }; - - @SuppressWarnings("unchecked") - static com.jsoniter.demo.object_with_15_fields.TestObject deserializestruct4(final JsonReader reader) throws java.io.IOException { - final com.jsoniter.demo.object_with_15_fields.TestObject instance = new com.jsoniter.demo.object_with_15_fields.TestObject(); - deserialize(instance, reader); - return instance; - } - - @SuppressWarnings("unchecked") - public static void deserialize(final com.jsoniter.demo.object_with_15_fields.TestObject instance, final JsonReader reader) throws java.io.IOException { - - String _field11_ = null; - String _field12_ = null; - String _field1_ = null; - String _field10_ = null; - String _field15_ = null; - String _field13_ = null; - String _field14_ = null; - String _field7_ = null; - String _field6_ = null; - String _field9_ = null; - String _field8_ = null; - String _field3_ = null; - String _field2_ = null; - String _field5_ = null; - String _field4_ = null; - byte nextToken = reader.last(); - if(nextToken != '}') { - int nameHash = reader.fillName(); - nextToken = reader.getNextToken(); - if(nextToken == 'n') { - if (reader.wasNull()) { - nextToken = reader.getNextToken(); - } else { - throw new java.io.IOException("Expecting 'u' (as null) at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } else { - switch(nameHash) { - - case 285424041: - _field11_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 235091184: - _field12_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1212206434: - _field1_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 268646422: - _field10_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 352534517: - _field15_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 251868803: - _field13_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 335756898: - _field14_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1111540720: - _field7_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1128318339: - _field6_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1346427386: - _field9_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1363205005: - _field8_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1178651196: - _field3_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1195428815: - _field2_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1145095958: - _field5_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1161873577: - _field4_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - default: - nextToken = reader.skip(); - break; - } - } - while (nextToken == ',') { - nextToken = reader.getNextToken(); - nameHash = reader.fillName(); - nextToken = reader.getNextToken(); - if(nextToken == 'n') { - if (reader.wasNull()) { - nextToken = reader.getNextToken(); - continue; - } else { - throw new java.io.IOException("Expecting 'u' (as null) at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } - switch(nameHash) { - - case 285424041: - _field11_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 235091184: - _field12_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1212206434: - _field1_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 268646422: - _field10_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 352534517: - _field15_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 251868803: - _field13_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 335756898: - _field14_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1111540720: - _field7_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1128318339: - _field6_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1346427386: - _field9_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1363205005: - _field8_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1178651196: - _field3_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1195428815: - _field2_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1145095958: - _field5_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - case 1161873577: - _field4_ = StringConverter.deserialize(reader); - nextToken = reader.getNextToken(); - break; - default: - nextToken = reader.skip(); - break; - } - } - if (nextToken != '}') { - throw new java.io.IOException("Expecting '}' at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } - - instance.field11 = _field11_; - instance.field12 = _field12_; - instance.field1 = _field1_; - instance.field10 = _field10_; - instance.field15 = _field15_; - instance.field13 = _field13_; - instance.field14 = _field14_; - instance.field7 = _field7_; - instance.field6 = _field6_; - instance.field9 = _field9_; - instance.field8 = _field8_; - instance.field3 = _field3_; - instance.field2 = _field2_; - instance.field5 = _field5_; - instance.field4 = _field4_; - } - - public static void serialize(final com.jsoniter.demo.SimpleObjectBinding.TestObject self, final JsonWriter sw, final boolean minimal) { - sw.writeByte(JsonWriter.OBJECT_START); - if (minimal) { - __serializeJsonObjectMinimal(self, sw, false); - } else { - __serializeJsonObjectFull(self, sw, false); - } - sw.writeByte(JsonWriter.OBJECT_END); - } - - static void __serializeJsonObjectMinimal(final com.jsoniter.demo.SimpleObjectBinding.TestObject self, JsonWriter sw, boolean hasWrittenProperty) { - - - if (self.field1 != 0) { - hasWrittenProperty = true; - sw.writeAscii("\"field1\":", 9); - NumberConverter.serialize(self.field1, sw); - } - - if (self.field2 != 0) { - if(hasWrittenProperty) sw.writeByte(JsonWriter.COMMA); - hasWrittenProperty = true; - sw.writeAscii("\"field2\":", 9); - NumberConverter.serialize(self.field2, sw); - } - } - - static void __serializeJsonObjectFull(final com.jsoniter.demo.SimpleObjectBinding.TestObject self, JsonWriter sw, boolean hasWrittenProperty) { - - - - sw.writeAscii("\"field1\":", 9); - NumberConverter.serialize(self.field1, sw); - - - sw.writeAscii(",\"field2\":", 10); - NumberConverter.serialize(self.field2, sw); - } - - public static final JsonReader.ReadObject JSON_READER_struct3 = new JsonReader.ReadObject() { - @SuppressWarnings("unchecked") - @Override - public com.jsoniter.demo.SimpleObjectBinding.TestObject read(final JsonReader reader) throws java.io.IOException { - if(reader.last() != '{') { - throw new java.io.IOException("Expecting \'{\' at position " + reader.positionInStream() + ". Found " + (char)reader.last()); - } - reader.getNextToken(); - final com.jsoniter.demo.SimpleObjectBinding.TestObject instance = new com.jsoniter.demo.SimpleObjectBinding.TestObject(); - deserialize(instance, reader); - return instance; - } - }; - - @SuppressWarnings("unchecked") - static com.jsoniter.demo.SimpleObjectBinding.TestObject deserializestruct3(final JsonReader reader) throws java.io.IOException { - final com.jsoniter.demo.SimpleObjectBinding.TestObject instance = new com.jsoniter.demo.SimpleObjectBinding.TestObject(); - deserialize(instance, reader); - return instance; - } - - @SuppressWarnings("unchecked") - static void deserialize(final com.jsoniter.demo.SimpleObjectBinding.TestObject instance, final JsonReader reader) throws java.io.IOException { - - int _field1_ = 0; - int _field2_ = 0; - byte nextToken = reader.last(); - if(nextToken != '}') { - int nameHash = reader.fillName(); - nextToken = reader.getNextToken(); - if(nextToken == 'n') { - if (reader.wasNull()) { - nextToken = reader.getNextToken(); - } else { - throw new java.io.IOException("Expecting 'u' (as null) at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } else { - switch(nameHash) { - - case 1212206434: - _field1_ = NumberConverter.deserializeInt(reader); - nextToken = reader.getNextToken(); - break; - case 1195428815: - _field2_ = NumberConverter.deserializeInt(reader); - nextToken = reader.getNextToken(); - break; - default: - nextToken = reader.skip(); - break; - } - } - while (nextToken == ',') { - nextToken = reader.getNextToken(); - nameHash = reader.fillName(); - nextToken = reader.getNextToken(); - if(nextToken == 'n') { - if (reader.wasNull()) { - nextToken = reader.getNextToken(); - continue; - } else { - throw new java.io.IOException("Expecting 'u' (as null) at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } - switch(nameHash) { - - case 1212206434: - _field1_ = NumberConverter.deserializeInt(reader); - nextToken = reader.getNextToken(); - break; - case 1195428815: - _field2_ = NumberConverter.deserializeInt(reader); - nextToken = reader.getNextToken(); - break; - default: - nextToken = reader.skip(); - break; - } - } - if (nextToken != '}') { - throw new java.io.IOException("Expecting '}' at position " + reader.positionInStream() + ". Found " + (char)nextToken); - } - } - - instance.field1 = _field1_; - instance.field2 = _field2_; - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/ArrayBinding.java b/demo/src/test/java/com/jsoniter/demo/ArrayBinding.java deleted file mode 100644 index 560a9f2a..00000000 --- a/demo/src/test/java/com/jsoniter/demo/ArrayBinding.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.jsoniter.demo; - -import com.dslplatform.json.DslJson; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.afterburner.AfterburnerModule; -import com.jsoniter.JsonIterator; -import com.jsoniter.annotation.JacksonAnnotationSupport; -import com.jsoniter.spi.TypeLiteral; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; - -import java.io.IOException; - -@State(Scope.Thread) -public class ArrayBinding { - private TypeLiteral typeLiteral; - private ObjectMapper jackson; - private byte[] input; - private TypeReference typeRef; - private String inputStr; - - private JsonIterator iter; - private DslJson dslJson; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - inputStr = "[1,2,3,4,5,6,7,8,9]".replace('\'', '"'); - input = inputStr.getBytes(); - iter = JsonIterator.parse(input); - typeLiteral = new TypeLiteral() { - }; - typeRef = new TypeReference() { - }; - JacksonAnnotationSupport.enable(); - jackson = new ObjectMapper(); - jackson.registerModule(new AfterburnerModule()); - dslJson = new DslJson(); - } - - @Test - public void test() throws IOException { - benchSetup(null); - System.out.println(withJsoniter()); - System.out.println(withIterator()); - System.out.println(withJackson()); - System.out.println(withDsljson()); - } - - public static void main(String[] args) throws Exception { - Main.main(new String[]{ - "ArrayBinding", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } - - @Benchmark - public void withJsoniterBinding(Blackhole bh) throws IOException { - bh.consume(withJsoniter()); - } - - @Benchmark - public void withJsoniterIterator(Blackhole bh) throws IOException { - bh.consume(withIterator()); - } - - @Benchmark - public void withJackson(Blackhole bh) throws IOException { - bh.consume(withJackson()); - } - - @Benchmark - public void withDsljson(Blackhole bh) throws IOException { - bh.consume(withDsljson()); - } - - private int withJsoniter() throws IOException { - iter.reset(input); - int[] arr = iter.read(typeLiteral); - int total = 0; - for (int i = 0; i < arr.length; i++) { - total += arr[i]; - } - return total; - } - - private int withJackson() throws IOException { - int[] arr = jackson.readValue(input, typeRef); - int total = 0; - for (int i = 0; i < arr.length; i++) { - total += arr[i]; - } - return total; - } - - private int withDsljson() throws IOException { - int[] arr = (int[]) dslJson.deserialize(int[].class, input, input.length); - int total = 0; - for (int i = 0; i < arr.length; i++) { - total += arr[i]; - } - return total; - } - - private int withIterator() throws IOException { - iter.reset(input); - int total = 0; - while (iter.readArray()) { - total += iter.readInt(); - } - return total; - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/ConstructorBinding.java b/demo/src/test/java/com/jsoniter/demo/ConstructorBinding.java deleted file mode 100644 index d18225b3..00000000 --- a/demo/src/test/java/com/jsoniter/demo/ConstructorBinding.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.jsoniter.demo; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.afterburner.AfterburnerModule; -import com.jsoniter.DecodingMode; -import com.jsoniter.JsonIterator; -import com.jsoniter.ReflectionDecoderFactory; -import com.jsoniter.annotation.JacksonAnnotationSupport; -import com.jsoniter.spi.JsoniterSpi; -import com.jsoniter.spi.TypeLiteral; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; - -import java.io.IOException; - -@State(Scope.Thread) -public class ConstructorBinding { - - private TypeLiteral typeLiteral; - private ObjectMapper jackson; - private byte[] input; - private TypeReference typeRef; - private String inputStr; - - public static class TestObject { - @JsonIgnore - private int field1; - @JsonIgnore - private int field2; - - @JsonCreator - public TestObject( - @JsonProperty("field1") int field1, - @JsonProperty("field2") int field2) { - this.field1 = field1; - this.field2 = field2; - } - - @Override - public String toString() { - return "TestObject1{" + - "field1=" + field1 + - ", field2=" + field2 + - '}'; - } - } - - - private JsonIterator iter; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - inputStr = "{'field1':100,'field2':101}"; - input = inputStr.replace('\'', '"').getBytes(); - iter = JsonIterator.parse(input); - typeLiteral = new TypeLiteral() { - }; - typeRef = new TypeReference() { - }; - JacksonAnnotationSupport.enable(); - jackson = new ObjectMapper(); - jackson.registerModule(new AfterburnerModule()); - if (params != null) { - if (params.getBenchmark().contains("withJsoniterStrictMode")) { - JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_STRICTLY); - } - if (params.getBenchmark().contains("withJsoniterReflection")) { - JsoniterSpi.registerTypeDecoder(TestObject.class, ReflectionDecoderFactory.create(TestObject.class)); - } - } - } - - @Test - public void test() throws IOException { - benchSetup(null); - JsoniterSpi.registerTypeDecoder(TestObject.class, ReflectionDecoderFactory.create(TestObject.class)); - System.out.println(withJsoniter()); - System.out.println(withJackson()); - } - - public static void main(String[] args) throws Exception { - Main.main(new String[]{ - "ConstructorBinding", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } - - @Benchmark - public void withJsoniterHashMode(Blackhole bh) throws IOException { - bh.consume(withJsoniter()); - } - - @Benchmark - public void withJsoniterStrictMode(Blackhole bh) throws IOException { - bh.consume(withJsoniter()); - } - - @Benchmark - public void withJsoniterReflection(Blackhole bh) throws IOException { - bh.consume(withJsoniter()); - } - - @Benchmark - public void withJackson(Blackhole bh) throws IOException { - bh.consume(withJackson()); - } - - private TestObject withJsoniter() throws IOException { - iter.reset(input); - return iter.read(typeLiteral); - } - - private TestObject withJackson() throws IOException { - return jackson.readValue(input, typeRef); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/FieldMatching.java b/demo/src/test/java/com/jsoniter/demo/FieldMatching.java deleted file mode 100644 index 72bc1ef8..00000000 --- a/demo/src/test/java/com/jsoniter/demo/FieldMatching.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.jsoniter.demo; - -import com.jsoniter.DecodingMode; -import com.jsoniter.spi.JsonException; -import com.jsoniter.JsonIterator; -import com.jsoniter.annotation.JsonProperty; -import com.jsoniter.annotation.JsonObject; -import com.jsoniter.annotation.JsoniterAnnotationSupport; -import com.jsoniter.spi.TypeLiteral; -import org.junit.Assert; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.Blackhole; - -import java.io.IOException; - -@State(Scope.Thread) -public class FieldMatching { - private TypeLiteral testObject0Type; - private TypeLiteral testObject1Type; - private TypeLiteral testObject2Type; - private TypeLiteral testObject3Type; - private TypeLiteral testObject4Type; - private JsonIterator iter0; - private JsonIterator iter1Success; - private byte[] iter0Input; - private byte[] iter1SuccessInput; - - public static class TestObject0 { - public int field1; - public int field2; - public int field3; - } - - public static class TestObject1 { - @JsonProperty(required = true) - public int field1; - @JsonProperty(required = true) - public int field2; - @JsonProperty(required = true) - public int field3; - } - - @JsonObject(asExtraForUnknownProperties = true) - public static class TestObject2 { - public int field1; - public int field2; - } - - @JsonObject(asExtraForUnknownProperties = true, unknownPropertiesWhitelist = {"field2"}) - public static class TestObject3 { - public int field1; - } - - @JsonObject(unknownPropertiesBlacklist = {"field3"}) - public static class TestObject4 { - public int field1; - } - - @Setup(Level.Trial) - public void benchSetup() { - JsoniterAnnotationSupport.enable(); - JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_STRICTLY); - iter0Input = "{'field1':101,'field2':101,'field3':101}".replace('\'', '"').getBytes(); - iter0 = JsonIterator.parse(iter0Input); - iter1SuccessInput = "{'field1':101,'field2':101,'field3':101}".replace('\'', '"').getBytes(); - iter1Success = JsonIterator.parse(iter1SuccessInput); - testObject0Type = new TypeLiteral() { - }; - testObject1Type = new TypeLiteral() { - }; - testObject2Type = new TypeLiteral() { - }; - testObject3Type = new TypeLiteral() { - }; - testObject4Type = new TypeLiteral() { - }; - } - - @Test - public void test() throws IOException { - benchSetup(); - try { - JsonIterator iter1Failure = JsonIterator.parse("{'field2':101}".replace('\'', '"').getBytes()); - iter1Failure.read(testObject1Type); - Assert.fail(); - } catch (JsonException e) { - System.out.println(e); - } - try { - JsonIterator iter2Failure = JsonIterator.parse("{'field1':101,'field2':101,'field3':101}".replace('\'', '"').getBytes()); - iter2Failure.read(testObject2Type); - Assert.fail(); - } catch (JsonException e) { - System.out.println(e); - } - try { - JsonIterator iter3Failure = JsonIterator.parse("{'field1':101,'field2':101,'field3':101}".replace('\'', '"').getBytes()); - iter3Failure.read(testObject3Type); - Assert.fail(); - } catch (JsonException e) { - System.out.println(e); - } - try { - JsonIterator iter4Failure = JsonIterator.parse("{'field1':101,'field2':101,'field3':101}".replace('\'', '"').getBytes()); - iter4Failure.read(testObject4Type); - Assert.fail(); - } catch (JsonException e) { - System.out.println(e); - } - } - - public static void main(String[] args) throws Exception { - Main.main(new String[]{ - "FieldMatching", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } - - @Benchmark - public void iter0(Blackhole bh) throws IOException { - iter0.reset(iter0Input); - bh.consume(iter0.read(testObject0Type)); - } - - @Benchmark - public void iter1Success(Blackhole bh) throws IOException { - iter1Success.reset(iter1SuccessInput); - bh.consume(iter1Success.read(testObject1Type)); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/FloatOutput.java b/demo/src/test/java/com/jsoniter/demo/FloatOutput.java deleted file mode 100644 index a4bfc23f..00000000 --- a/demo/src/test/java/com/jsoniter/demo/FloatOutput.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.jsoniter.demo; - - -import com.dslplatform.json.DslJson; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.jsoniter.output.JsonStream; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -@State(Scope.Thread) -public class FloatOutput { - - private ByteArrayOutputStream baos; - private ObjectMapper objectMapper; - private JsonStream stream; - private byte[] buffer; - private DslJson dslJson; - - public static void main(String[] args) throws Exception { - Main.main(new String[]{ - "FloatOutput", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } - - @Test - public void test() throws IOException { - benchSetup(null); - jsoniter(); - System.out.println(baos.toString()); - jackson(); - System.out.println(baos.toString()); - dsljson(); - System.out.println(baos.toString()); - } - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - baos = new ByteArrayOutputStream(1024 * 64); - objectMapper = new ObjectMapper(); - objectMapper.getFactory().configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true); - stream = new JsonStream(baos, 4096); - buffer = new byte[4096]; - dslJson = new DslJson(); - } - - @Benchmark - public void jsoniter() throws IOException { - baos.reset(); - stream.reset(baos); - stream.writeVal(10.24f); - stream.flush(); - } - - @Benchmark - public void jackson() throws IOException { - baos.reset(); - objectMapper.writeValue(baos, 10.24f); - } - - @Benchmark - public void dsljson() throws IOException { - baos.reset(); - dslJson.serialize(10.24f, baos); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/IntegerOutput.java b/demo/src/test/java/com/jsoniter/demo/IntegerOutput.java deleted file mode 100644 index 65f297f3..00000000 --- a/demo/src/test/java/com/jsoniter/demo/IntegerOutput.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.jsoniter.demo; - - -import com.dslplatform.json.DslJson; -import com.dslplatform.json.JsonWriter; -import com.dslplatform.json.NumberConverter; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.jsoniter.annotation.JsonWrapper; -import com.jsoniter.output.JsonStream; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -@State(Scope.Thread) -public class IntegerOutput { - - private ByteArrayOutputStream baos; - private ObjectMapper objectMapper; - private JsonStream stream; - private byte[] buffer; - private DslJson dslJson; - private JsonWriter jsonWriter; - - public static void main(String[] args) throws Exception { - Main.main(new String[]{ - "IntegerOutput", - "-i", "5", - "-wi", "5", - "-f", "1", - "-prof", "stack" - }); - } - - @Test - public void test() throws IOException { - benchSetup(null); - jsoniter(null); - System.out.println(baos.toString()); - jackson(); - System.out.println(baos.toString()); - dsljson(null); - System.out.println(baos.toString()); - } - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - baos = new ByteArrayOutputStream(1024 * 64); - objectMapper = new ObjectMapper(); - objectMapper.getFactory().configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true); - stream = new JsonStream(baos, 4096); - buffer = new byte[4096]; - dslJson = new DslJson(); - jsonWriter = new JsonWriter(); - } - - @Benchmark - public void jsoniter(Blackhole bh) throws IOException { - baos.reset(); - stream.reset(baos); - stream.writeVal(1024); - stream.flush(); -// bh.consume(stream); - } - - @Benchmark - public void jackson() throws IOException { - baos.reset(); - objectMapper.writeValue(baos, 1024); - } - -// @Benchmark - public void dsljson(Blackhole bh) throws IOException { -// baos.reset(); - jsonWriter.reset(); - NumberConverter.serialize(1024, jsonWriter); -// bh.consume(jsonWriter); -// jsonWriter.toStream(baos); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/JmhFlightRecorderProfiler.java b/demo/src/test/java/com/jsoniter/demo/JmhFlightRecorderProfiler.java deleted file mode 100644 index 4fc92ec8..00000000 --- a/demo/src/test/java/com/jsoniter/demo/JmhFlightRecorderProfiler.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.jsoniter.demo; - -import java.io.File; -import java.lang.management.ManagementFactory; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.profile.ExternalProfiler; -import org.openjdk.jmh.results.AggregationPolicy; -import org.openjdk.jmh.results.Aggregator; -import org.openjdk.jmh.results.BenchmarkResult; -import org.openjdk.jmh.results.Result; -import org.openjdk.jmh.results.ResultRole; - -/** - * - * @author zoly - */ -public final class JmhFlightRecorderProfiler implements ExternalProfiler { - - private static final String DUMP_FOLDER = System.getProperty("jmh.stack.profiles", "/tmp"); - - private static final String DEFAULT_OPTIONS = System.getProperty("jmh.fr.options", - "defaultrecording=true,settings=profile"); - - - - @Override - public Collection addJVMInvokeOptions(final BenchmarkParams params) { - return Collections.emptyList(); - } - - private volatile String dumpFile; - - private static volatile String benchmarkName; - - public static String benchmarkName() { - return benchmarkName; - } - - - /** - * See: - * http://docs.oracle.com/cd/E15289_01/doc.40/e15070/usingjfr.htm - * and - * http://docs.oracle.com/cd/E15289_01/doc.40/e15070/config_rec_data.htm - * @param params - * @return - */ - @Override - public Collection addJVMOptions(final BenchmarkParams params) { - final String id = params.id(); - benchmarkName = id; - dumpFile = DUMP_FOLDER + '/' + id + ".jfr"; - String flightRecorderOptions = DEFAULT_OPTIONS + ",dumponexit=true,dumponexitpath=" + dumpFile; - return Arrays.asList( - "-XX:+FlightRecorder", - "-XX:FlightRecorderOptions=" + flightRecorderOptions); - } - - @Override - public void beforeTrial(final BenchmarkParams benchmarkParams) { - final List inputArguments = ManagementFactory.getRuntimeMXBean().getInputArguments(); -// if (new Version(org.spf4j.base.Runtime.JAVA_VERSION).compareTo(new Version("1.8.0_40")) <= 0 -// && !inputArguments.contains("-XX:+UnlockCommercialFeatures")) { -// throw new RuntimeException("-XX:+UnlockCommercialFeatures must pre present in the JVM options," -// + " current options are: " + inputArguments); -// } - } - - - @Override - public boolean allowPrintOut() { - return true; - } - - @Override - public boolean allowPrintErr() { - return false; - } - - - @Override - public String getDescription() { - return "Java Flight Recording profiler runs for every benchmark."; - } - - @Override - public Collection afterTrial(final BenchmarkResult bp, final long l, - final File file, final File file1) { - NoResult r = new NoResult("Profile saved to " + dumpFile + ", results: " + bp - + ", stdOutFile = " + file + ", stdErrFile = " + file1); - return Collections.singleton(r); - } - - private static final class NoResult extends Result { - private static final long serialVersionUID = 1L; - - private final String output; - - NoResult(final String output) { - super(ResultRole.SECONDARY, "JFR", of(Double.NaN), "N/A", AggregationPolicy.SUM); - this.output = output; - } - - @Override - protected Aggregator getThreadAggregator() { - return new NoResultAggregator(); - } - - @Override - protected Aggregator getIterationAggregator() { - return new NoResultAggregator(); - } - - private static class NoResultAggregator implements Aggregator { - - @Override - public NoResult aggregate(final Collection results) { - StringBuilder agg = new StringBuilder(); - for (NoResult r : results) { - agg.append(r.output); - } - return new NoResult(agg.toString()); - } - } - } - - @Override - public String toString() { - return "JmhFlightRecorderProfiler{" + "dumpFile=" + dumpFile + '}'; - } - -} \ No newline at end of file diff --git a/demo/src/test/java/com/jsoniter/demo/LazyAny.java b/demo/src/test/java/com/jsoniter/demo/LazyAny.java deleted file mode 100644 index 6ff9ebda..00000000 --- a/demo/src/test/java/com/jsoniter/demo/LazyAny.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.jsoniter.demo; - -import com.jsoniter.any.Any; -import com.jsoniter.JsonIterator; -import com.jsoniter.Slice; -import com.jsoniter.output.JsonStream; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@State(Scope.Thread) -public class LazyAny { - - private JsonIterator iter; - private Slice input; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) throws IOException { - InputStream resourceAsStream = LazyAny.class.getResourceAsStream("/large.json"); - byte[] buf = new byte[32 * 1024]; - int size = resourceAsStream.read(buf); - input = new Slice(buf, 0, size); - iter = new JsonIterator(); - } - - public static void main(String[] args) throws Exception { - Main.main(new String[]{ - "LazyAny", - "-i", "5", - "-wi", "5", - "-f", "1", - "-prof", "stack", - }); - } - - public static class User { - public int index; - public String name; - } - - @Test - public void test() throws IOException { - benchSetup(null); - System.out.println(jsoniter()); - System.out.println(jsoniter_object()); - - User tom = new User(); - tom.index = 1; - tom.name = "tom"; - Map tomAsMap = Any.wrap(tom).asMap(); - tomAsMap.put("age", Any.wrap(17)); - System.out.println(JsonStream.serialize(tomAsMap)); - } - - @Benchmark - public void jsoniter(Blackhole bh) throws IOException { - bh.consume(jsoniter()); - } - - @Benchmark - public void jsoniter_object(Blackhole bh) throws IOException { - bh.consume(jsoniter_object()); - } - - public int jsoniter() throws IOException { - iter.reset(input); - Any users = iter.readAny(); - int total = 0; - for (Any user : users) { - total += user.get("friends").size(); - } - return total; - } - - public int jsoniter_object() throws IOException { - iter.reset(input); - List users = (List) iter.read(); - int total = 0; - for (Object userObj : users) { - Map user = (Map) userObj; - List friends = (List) user.get("friends"); - total += friends.size(); - } - return total; - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/ListBinding.java b/demo/src/test/java/com/jsoniter/demo/ListBinding.java deleted file mode 100644 index a8854000..00000000 --- a/demo/src/test/java/com/jsoniter/demo/ListBinding.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.jsoniter.demo; - -import com.dslplatform.json.DslJson; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.afterburner.AfterburnerModule; -import com.jsoniter.JsonIterator; -import com.jsoniter.annotation.JacksonAnnotationSupport; -import com.jsoniter.spi.TypeLiteral; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; - -import java.io.IOException; -import java.util.List; - -@State(Scope.Thread) -public class ListBinding { - private TypeLiteral> typeLiteral; - private ObjectMapper jackson; - private byte[] input; - private TypeReference> typeRef; - private String inputStr; - - private JsonIterator iter; - private DslJson dslJson; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - inputStr = "['jackson','jsoniter','fastjson']".replace('\'', '"'); - input = inputStr.getBytes(); - iter = JsonIterator.parse(input); - typeLiteral = new TypeLiteral>() { - }; - typeRef = new TypeReference>() { - }; - JacksonAnnotationSupport.enable(); - jackson = new ObjectMapper(); - jackson.registerModule(new AfterburnerModule()); - dslJson = new DslJson(); - } - - @Test - public void test() throws IOException { - benchSetup(null); - System.out.println(withJsoniter()); - System.out.println(withJackson()); - System.out.println(withDsljson()); - } - - public static void main(String[] args) throws Exception { - Main.main(new String[]{ - "ListBinding", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } - - @Benchmark - public void withJsoniterBinding(Blackhole bh) throws IOException { - bh.consume(withJsoniter()); - } - - @Benchmark - public void withJackson(Blackhole bh) throws IOException { - bh.consume(withJackson()); - } - - @Benchmark - public void withDsljson(Blackhole bh) throws IOException { - bh.consume(withDsljson()); - } - - private List withJsoniter() throws IOException { - iter.reset(input); - return iter.read(typeLiteral); - } - - private List withJackson() throws IOException { - return jackson.readValue(input, typeRef); - } - - private List withDsljson() throws IOException { - return (List) dslJson.deserializeList(String.class, input, input.length); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/MapBinding.java b/demo/src/test/java/com/jsoniter/demo/MapBinding.java deleted file mode 100644 index d4b83e21..00000000 --- a/demo/src/test/java/com/jsoniter/demo/MapBinding.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.jsoniter.demo; - - -import com.dslplatform.json.DslJson; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.afterburner.AfterburnerModule; -import com.jsoniter.JsonIterator; -import com.jsoniter.annotation.JacksonAnnotationSupport; -import com.jsoniter.spi.TypeLiteral; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Map; - -@State(Scope.Thread) -public class MapBinding { - private TypeLiteral> typeLiteral; - private ObjectMapper jackson; - private byte[] input; - private TypeReference> typeRef; - private String inputStr; - - private JsonIterator iter; - private DslJson dslJson; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - inputStr = "{'jsoniter':53.9123,'jackson':-8772.2131,'dsljson':99877}".replace('\'', '"'); - input = inputStr.getBytes(); - iter = JsonIterator.parse(input); - typeLiteral = new TypeLiteral>() { - }; - typeRef = new TypeReference>() { - }; - JacksonAnnotationSupport.enable(); - jackson = new ObjectMapper(); - jackson.registerModule(new AfterburnerModule()); - dslJson = new DslJson(); - } - - @Test - public void test() throws IOException { - benchSetup(null); - System.out.println(withJsoniter()); - System.out.println(withJackson()); - System.out.println(withDsljson()); - } - - public static void main(String[] args) throws Exception { - Main.main(new String[]{ - "MapBinding", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } - - @Benchmark - public void withJsoniterBinding(Blackhole bh) throws IOException { - bh.consume(withJsoniter()); - } - - @Benchmark - public void withJackson(Blackhole bh) throws IOException { - bh.consume(withJackson()); - } - - @Benchmark - public void withDsljson(Blackhole bh) throws IOException { - bh.consume(withDsljson()); - } - - private Map withJsoniter() throws IOException { - iter.reset(input); - return iter.read(typeLiteral); - } - - private Map withJackson() throws IOException { - return jackson.readValue(input, typeRef); - } - - private Map withDsljson() throws IOException { - return (Map) dslJson.deserialize(Map.class, input, input.length); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/ModelTest.java b/demo/src/test/java/com/jsoniter/demo/ModelTest.java deleted file mode 100644 index 6745d667..00000000 --- a/demo/src/test/java/com/jsoniter/demo/ModelTest.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.jsoniter.demo; - -import com.alibaba.fastjson.JSON; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.afterburner.AfterburnerModule; -import com.jsoniter.DecodingMode; -import com.jsoniter.JsonIterator; -import com.jsoniter.spi.TypeLiteral; -import com.squareup.moshi.JsonAdapter; -import com.squareup.moshi.Moshi; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.IOException; - -// Benchmark Mode Cnt Score Error Units -// ModelTest.fastjson thrpt 5 7790201.506 ± 260185.529 ops/s -// ModelTest.jackson thrpt 5 4063696.579 ± 169609.697 ops/s -// ModelTest.jsoniter thrpt 5 16392968.819 ± 197563.536 ops/s -@State(Scope.Thread) -public class ModelTest { - - private String input; - private JsonIterator iter; - private byte[] inputBytes; - private TypeLiteral modelTypeLiteral; // this is thread-safe can reused - private ObjectMapper jackson; - private TypeReference modelTypeReference; - private JsonAdapter moshiAdapter; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { -// JsonIterator.enableStreamingSupport(); - input = "{\"name\":\"wenshao\",\"id\":1001}"; - inputBytes = input.getBytes(); - iter = new JsonIterator(); - modelTypeLiteral = new TypeLiteral() { - }; - JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); - jackson = new ObjectMapper(); - jackson.registerModule(new AfterburnerModule()); - modelTypeReference = new TypeReference() { - }; - Moshi moshi = new Moshi.Builder().build(); - moshiAdapter = moshi.adapter(Model.class); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "ModelTest", - "-i", "5", - "-wi", "5", - "-f", "1", -// "-jvmArgsAppend", "-server -XX:+DoEscapeAnalysis", - }); - } - - @Test - public void test() throws IOException { - benchSetup(null); - iter.reset(inputBytes); - System.out.println(iter.read(modelTypeLiteral).name); - System.out.println(moshiAdapter.fromJson(input).name); - } - -// public static void main(String[] args) throws Exception { -// Options opt = new OptionsBuilder() -// .include("ModelTest") -// .addProfiler(JmhFlightRecorderProfiler.class) -// .jvmArgs("-Xmx512m", "-Xms512m", "-XX:+UnlockCommercialFeatures", -// "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintAssembly", -// "-Djmh.stack.profiles=" + "/tmp", -// "-Djmh.executor=FJP", -// "-Djmh.fr.options=defaultrecording=true,settings=profile") -// .warmupIterations(5) -// .measurementTime(TimeValue.seconds(5)) -// .measurementIterations(5) -// .forks(1) -// .build(); -// new Runner(opt).run(); -// } - - @Benchmark - public void jsoniter(Blackhole bh) throws IOException { - iter.reset(inputBytes); - bh.consume(iter.read(modelTypeLiteral)); - } - -// @Benchmark - public void jsoniter_easy_mode(Blackhole bh) throws IOException { - bh.consume(JsonIterator.deserialize(inputBytes, Model.class)); - } - -// @Benchmark - public void fastjson(Blackhole bh) throws IOException { - // this is not a exactly fair comparison, - // as string => object is not - // bytes => object - bh.consume(JSON.parseObject(input, Model.class)); - } - - @Benchmark - public void moshi(Blackhole bh) throws IOException { - bh.consume(moshiAdapter.fromJson(input)); - } - -// @Benchmark - public void jackson(Blackhole bh) throws IOException { - bh.consume(jackson.readValue(inputBytes, modelTypeReference)); - } - - public static class Model { - public int id; - public String name; - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/PrivateFieldBinding.java b/demo/src/test/java/com/jsoniter/demo/PrivateFieldBinding.java deleted file mode 100644 index 6c22cb87..00000000 --- a/demo/src/test/java/com/jsoniter/demo/PrivateFieldBinding.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.jsoniter.demo; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.afterburner.AfterburnerModule; -import com.jsoniter.JsonIterator; -import com.jsoniter.ReflectionDecoderFactory; -import com.jsoniter.annotation.JacksonAnnotationSupport; -import com.jsoniter.spi.EmptyExtension; -import com.jsoniter.spi.JsoniterSpi; -import com.jsoniter.spi.ParameterizedTypeImpl; -import com.jsoniter.spi.TypeLiteral; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.Blackhole; - -import java.io.IOException; -import java.lang.reflect.Type; -import java.util.LinkedList; -import java.util.List; - -@State(Scope.Thread) -public class PrivateFieldBinding { - - private TypeLiteral typeLiteral; - private ObjectMapper jackson; - private byte[] input; - private TypeReference typeRef; - private String inputStr; - - public static class TestObject { - @JsonProperty - private int field1; - @JsonProperty - private int field2; - - @Override - public String toString() { - return "TestObject1{" + - "field1=" + field1 + - ", field2=" + field2 + - '}'; - } - } - - - private JsonIterator iter; - - @Setup(Level.Trial) - public void benchSetup() { - inputStr = "{'field1':100,'field2':101}"; - input = inputStr.replace('\'', '"').getBytes(); - iter = JsonIterator.parse(input); - typeLiteral = new TypeLiteral() { - }; - typeRef = new TypeReference() { - }; - JacksonAnnotationSupport.enable(); - JsoniterSpi.registerTypeDecoder(TestObject.class, ReflectionDecoderFactory.create(TestObject.class)); - jackson = new ObjectMapper(); - jackson.registerModule(new AfterburnerModule()); - } - - @Test - public void test() throws IOException { - JsoniterSpi.registerExtension(new EmptyExtension() { - @Override - public Type chooseImplementation(Type type) { - if (ParameterizedTypeImpl.isSameClass(type, List.class)) { - return ParameterizedTypeImpl.useImpl(type, LinkedList.class); - } else { - return type; - } - } - }); - benchSetup(); - System.out.println(withJsoniter()); - System.out.println(withJackson()); - } - - @Benchmark - public void withJsoniter(Blackhole bh) throws IOException { - bh.consume(withJsoniter()); - } - - @Benchmark - public void withJackson(Blackhole bh) throws IOException { - bh.consume(withJackson()); - } - - public static void main(String[] args) throws Exception { - Main.main(new String[]{ - "PrivateFieldBinding.*", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } - - private TestObject withJsoniter() throws IOException { - iter.reset(input); - return iter.read(typeLiteral); - } - - private TestObject withJackson() throws IOException { - return jackson.readValue(input, typeRef); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/ReadString.java b/demo/src/test/java/com/jsoniter/demo/ReadString.java deleted file mode 100644 index 9eab64cd..00000000 --- a/demo/src/test/java/com/jsoniter/demo/ReadString.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.jsoniter.demo; - - -import com.dslplatform.json.CustomJsonReader; -import com.dslplatform.json.JsonReader; -import com.jsoniter.JsonIterator; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; - -import java.io.IOException; - -@State(Scope.Thread) -public class ReadString { - - - private JsonIterator jsonIterator; - private byte[] input; - private CustomJsonReader customJsonReader; - - public static void main(String[] args) throws Exception { - Main.main(new String[]{ - "ReadString", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } - - @Test - public void test() throws IOException { - benchSetup(null); - } - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - jsonIterator = new JsonIterator(); - input = "\"hello world hello world\"".getBytes(); - customJsonReader = new CustomJsonReader(input); - } - - @Benchmark - public void jsoniter(Blackhole bh) throws IOException { - jsonIterator.reset(input); - bh.consume(jsonIterator.readString()); - } - - @Benchmark - public void dsljson(Blackhole bh) throws IOException { - customJsonReader.reset(); - customJsonReader.read(); - bh.consume(customJsonReader.readString()); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/SetterBinding.java b/demo/src/test/java/com/jsoniter/demo/SetterBinding.java deleted file mode 100644 index a695bee6..00000000 --- a/demo/src/test/java/com/jsoniter/demo/SetterBinding.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.jsoniter.demo; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.afterburner.AfterburnerModule; -import com.jsoniter.DecodingMode; -import com.jsoniter.JsonIterator; -import com.jsoniter.ReflectionDecoderFactory; -import com.jsoniter.annotation.JacksonAnnotationSupport; -import com.jsoniter.spi.JsoniterSpi; -import com.jsoniter.spi.TypeLiteral; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; - -import java.io.IOException; - -@State(Scope.Thread) -public class SetterBinding { - - private TypeLiteral typeLiteral; - private ObjectMapper jackson; - private byte[] input; - private TypeReference typeRef; - private String inputStr; - - public static class TestObject { - private int field1; - private int field2; - - public void setField1(int field1) { - this.field1 = field1; - } - - public void setField2(int field2) { - this.field2 = field2; - } - -// @JsonWrapper -// public void initialize( -// @JsonProperty("field1") int field1, -// @JsonProperty("field2") int field2) { -// this.field1 = field1; -// this.field2 = field2; -// } - } - - - private JsonIterator iter; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - inputStr = "{'field1':100,'field2':101}".replace('\'', '"'); - input = inputStr.getBytes(); - iter = JsonIterator.parse(input); - typeLiteral = new TypeLiteral() { - }; - typeRef = new TypeReference() { - }; - JacksonAnnotationSupport.enable(); - jackson = new ObjectMapper(); - jackson.registerModule(new AfterburnerModule()); - if (params != null) { - if (params.getBenchmark().contains("withJsoniterStrictMode")) { - JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_STRICTLY); - } - if (params.getBenchmark().contains("withJsoniterReflection")) { - JsoniterSpi.registerTypeDecoder(ConstructorBinding.TestObject.class, ReflectionDecoderFactory.create(TestObject.class)); - } - } - } - - @Test - public void test() throws IOException { - benchSetup(null); - JsoniterSpi.registerTypeDecoder(ConstructorBinding.TestObject.class, ReflectionDecoderFactory.create(TestObject.class)); - System.out.println(withJsoniter()); - System.out.println(withJackson()); - } - - public static void main(String[] args) throws Exception { - Main.main(new String[]{ - "ConstructorBinding", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } - - @Benchmark - public void withJsoniterHashMode(Blackhole bh) throws IOException { - bh.consume(withJsoniter()); - } - - @Benchmark - public void withJsoniterStrictMode(Blackhole bh) throws IOException { - bh.consume(withJsoniter()); - } - - @Benchmark - public void withJsoniterReflection(Blackhole bh) throws IOException { - bh.consume(withJsoniter()); - } - - @Benchmark - public void withJackson(Blackhole bh) throws IOException { - bh.consume(withJackson()); - } - - private ConstructorBinding.TestObject withJsoniter() throws IOException { - iter.reset(input); - return iter.read(typeLiteral); - } - - private ConstructorBinding.TestObject withJackson() throws IOException { - return jackson.readValue(input, typeRef); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/SimpleObjectBinding.java b/demo/src/test/java/com/jsoniter/demo/SimpleObjectBinding.java deleted file mode 100644 index c7408061..00000000 --- a/demo/src/test/java/com/jsoniter/demo/SimpleObjectBinding.java +++ /dev/null @@ -1,229 +0,0 @@ -package com.jsoniter.demo; - -import com.alibaba.fastjson.parser.DefaultJSONParser; -import com.dslplatform.json.CompiledJson; -import com.dslplatform.json.DslJson; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.afterburner.AfterburnerModule; -import com.jsoniter.DecodingMode; -import com.jsoniter.JsonIterator; -import com.jsoniter.ReflectionDecoderFactory; -import com.jsoniter.spi.JsoniterSpi; -import com.jsoniter.spi.TypeLiteral; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; - -import java.io.IOException; - -@State(Scope.Thread) -public class SimpleObjectBinding { - - private TypeLiteral typeLiteral; - private ObjectMapper jackson; - private byte[] input; - private TypeReference typeRef; - private DslJson dslJson; - private Class clazz; - private String inputStr; - private TestObject testObject; - - @CompiledJson - public static class TestObject { - public int field1; - public int field2; - - @Override - public String toString() { - return "TestObject1{" + - "field1=" + field1 + - ", field2=" + field2 + - '}'; - } - } - - - private JsonIterator iter; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - inputStr = "{'field1':100,'field2':101}".replace('\'', '"'); - input = inputStr.getBytes(); - iter = JsonIterator.parse(input); - typeLiteral = new TypeLiteral() { - }; - typeRef = new TypeReference() { - }; - clazz = TestObject.class; - jackson = new ObjectMapper(); - dslJson = new DslJson(); - testObject = new TestObject(); - if (params != null) { - if (params.getBenchmark().contains("withReflection")) { - JsoniterSpi.registerTypeDecoder(TestObject.class, ReflectionDecoderFactory.create(TestObject.class)); - } - if (params.getBenchmark().contains("withBindApiStrictMode")) { - JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_STRICTLY); - } - if (params.getBenchmark().contains("withJacksonAfterburner")) { - jackson.registerModule(new AfterburnerModule()); - } - } - } - - @Test - public void test() throws IOException { - benchSetup(null); - JsoniterSpi.registerTypeDecoder(TestObject.class, ReflectionDecoderFactory.create(TestObject.class)); - System.out.println(withIterator()); - System.out.println(withIteratorIfElse()); - System.out.println(withIteratorIntern()); - System.out.println(withBindApi()); - System.out.println(withExistingObject()); - System.out.println(withJackson()); - System.out.println(withDsljson()); - System.out.println(withFastjson()); - } - - public static void main(String[] args) throws Exception { - Main.main(new String[]{ - "SimpleObjectBinding.*", - "-i", "5", - "-wi", "5", - "-f", "1" - }); - } - -// @Benchmark - public void withIterator(Blackhole bh) throws IOException { - bh.consume(withIterator()); - } - -// @Benchmark - public void withIteratorIfElse(Blackhole bh) throws IOException { - bh.consume(withIteratorIfElse()); - } - -// @Benchmark - public void withIteratorIntern(Blackhole bh) throws IOException { - bh.consume(withIteratorIntern()); - } - - @Benchmark - public void withoutExistingObject(Blackhole bh) throws IOException { - bh.consume(withBindApi()); - } - - @Benchmark - public void withBindApiStrictMode(Blackhole bh) throws IOException { - bh.consume(withBindApi()); - } - - @Benchmark - public void withReflection(Blackhole bh) throws IOException { - bh.consume(withBindApi()); - } - - @Benchmark - public void withExistingObject(Blackhole bh) throws IOException { - bh.consume(withExistingObject()); - } - - @Benchmark - public void withJacksonAfterburner(Blackhole bh) throws IOException { - bh.consume(withJackson()); - } - - @Benchmark - public void withJacksonNoAfterburner(Blackhole bh) throws IOException { - bh.consume(withJackson()); - } - - @Benchmark - public void withDsljson(Blackhole bh) throws IOException { - bh.consume(withDsljson()); - } - - @Benchmark - public void withFastjson(Blackhole bh) throws IOException { - bh.consume(withFastjson()); - } - - private TestObject withIterator() throws IOException { - iter.reset(input); - TestObject obj = new TestObject(); - for (String field = iter.readObject(); field != null; field = iter.readObject()) { - switch (field) { - case "field1": - obj.field1 = iter.readInt(); - continue; - case "field2": - obj.field2 = iter.readInt(); - continue; - default: - iter.skip(); - } - } - return obj; - } - - private TestObject withIteratorIfElse() throws IOException { - iter.reset(input); - TestObject obj = new TestObject(); - for (String field = iter.readObject(); field != null; field = iter.readObject()) { - if (field.equals("field1")) { - obj.field1 = iter.readInt(); - continue; - } - if (field.equals("field2")) { - obj.field2 = iter.readInt(); - continue; - } - iter.skip(); - } - return obj; - } - - private TestObject withIteratorIntern() throws IOException { - iter.reset(input); - TestObject obj = new TestObject(); - for (String field = iter.readObject(); field != null; field = iter.readObject()) { - field = field.intern(); - if (field == "field1") { - obj.field1 = iter.readInt(); - continue; - } - if (field == "field2") { - obj.field2 = iter.readInt(); - continue; - } - iter.skip(); - } - return obj; - } - - private TestObject withBindApi() throws IOException { - iter.reset(input); - return iter.read(typeLiteral); - } - - private TestObject withExistingObject() throws IOException { - iter.reset(input); - return iter.read(typeLiteral, testObject); - } - - private TestObject withJackson() throws IOException { - return jackson.readValue(input, typeRef); - } - - private TestObject withDsljson() throws IOException { - return (TestObject) dslJson.deserialize(clazz, input, input.length); - } - - private TestObject withFastjson() { - return new DefaultJSONParser(inputStr).parseObject(TestObject.class); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/StringOutput.java b/demo/src/test/java/com/jsoniter/demo/StringOutput.java deleted file mode 100644 index 6bc73e83..00000000 --- a/demo/src/test/java/com/jsoniter/demo/StringOutput.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.jsoniter.demo; - - -import com.dslplatform.json.DslJson; -import com.dslplatform.json.JsonWriter; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.jsoniter.output.JsonStream; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -@State(Scope.Thread) -public class StringOutput { - - private ByteArrayOutputStream baos; - private ObjectMapper objectMapper; - private JsonStream stream; - private byte[] buffer; - private DslJson dslJson; - private JsonWriter jsonWriter; - - public static void main(String[] args) throws Exception { - Main.main(new String[]{ - "StringOutput", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } - - @Test - public void test() throws IOException { - benchSetup(null); - jsoniter(); - System.out.println(baos.toString()); - jackson(); - System.out.println(baos.toString()); - dsljson(); - System.out.println(baos.toString()); - } - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - baos = new ByteArrayOutputStream(1024 * 64); - objectMapper = new ObjectMapper(); - objectMapper.getFactory().configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true); - stream = new JsonStream(baos, 4096); - buffer = new byte[4096]; - dslJson = new DslJson(); - jsonWriter = new JsonWriter(); - } - - @Benchmark - public void jsoniter() throws IOException { - baos.reset(); - stream.reset(baos); - stream.writeVal("hello world ~~ hello 中文 ~~~"); - stream.flush(); - } - - @Benchmark - public void jackson() throws IOException { - baos.reset(); - objectMapper.writeValue(baos, "hello world ~~ hello 中文 ~~~"); - } - - @Benchmark - public void dsljson() throws IOException { - baos.reset(); - jsonWriter.reset(); - jsonWriter.write("hello world ~~ hello 中文 ~~~"); - jsonWriter.toStream(baos); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/WrapperUnwrapper.java b/demo/src/test/java/com/jsoniter/demo/WrapperUnwrapper.java deleted file mode 100644 index 5fafd580..00000000 --- a/demo/src/test/java/com/jsoniter/demo/WrapperUnwrapper.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.jsoniter.demo; - -import com.jsoniter.JsonIterator; -import com.jsoniter.annotation.*; -import com.jsoniter.output.JsonStream; -import org.junit.Test; - -import java.io.IOException; - -public class WrapperUnwrapper { - - public static class Name { - private final String firstName; - private final String lastName; - - public Name(String firstName, String lastName) { - this.firstName = firstName; - this.lastName = lastName; - } - - public String getFirstName() { - return firstName; - } - - public String getLastName() { - return lastName; - } - } - -public static class User { - private Name name; - public int score; - - @JsonIgnore - public Name getName() { - return name; - } - - @JsonUnwrapper - public void writeName(JsonStream stream) throws IOException { - stream.writeObjectField("firstName"); - stream.writeVal(name.getFirstName()); - stream.writeMore(); - stream.writeObjectField("lastName"); - stream.writeVal(name.getLastName()); - } - - @JsonWrapper - public void setName(@JsonProperty("firstName") String firstName, @JsonProperty("lastName") String lastName) { - System.out.println(firstName); - name = new Name(firstName, lastName); - } -} - - @Test - public void test() { - JsoniterAnnotationSupport.enable(); - String input = "{'firstName': 'tao', 'lastName': 'wen', 'score': 100}".replace('\'', '\"'); - System.out.println(input); - User user = JsonIterator.deserialize(input, User.class); - System.out.println(user.getName().getFirstName()); - System.out.println(JsonStream.serialize(user)); - - System.out.println(JsonStream.serialize(new int[]{1,2,3})); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_10_fields/BenchDslJson.java b/demo/src/test/java/com/jsoniter/demo/object_with_10_fields/BenchDslJson.java deleted file mode 100644 index 9c0084d2..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_10_fields/BenchDslJson.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.jsoniter.demo.object_with_10_fields; - -import com.dslplatform.json.CustomJsonReader; -import com.dslplatform.json.ExternalSerialization; -import com.dslplatform.json.JsonWriter; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchDslJson.deser thrpt 5 22328042.432 ± 311925.080 ops/s (3.67x) -BenchDslJson.ser thrpt 5 17639416.242 ± 136738.841 ops/s (2.17x) - */ -@State(Scope.Thread) -public class BenchDslJson { - - private TestObject testObject; - private JsonWriter jsonWriter; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private CustomJsonReader reader; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - testObject = TestObject.createTestObject(); - testJSON = TestObject.createTestJSON(); - jsonWriter = new JsonWriter(); - byteArrayOutputStream = new ByteArrayOutputStream(); - reader = new CustomJsonReader(testJSON); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - jsonWriter.reset(); - byteArrayOutputStream.reset(); - ExternalSerialization.serialize(testObject, jsonWriter, false); - jsonWriter.toStream(byteArrayOutputStream); - bh.consume(byteArrayOutputStream); - } - - @Benchmark - public void deser(Blackhole bh) throws IOException { - reader.reset(); - reader.read(); - reader.getNextToken(); - TestObject obj = new TestObject(); - ExternalSerialization.deserialize(obj, reader); - bh.consume(obj); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_10_fields.BenchDslJson", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_10_fields/BenchJackson.java b/demo/src/test/java/com/jsoniter/demo/object_with_10_fields/BenchJackson.java deleted file mode 100644 index 86f73d2d..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_10_fields/BenchJackson.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.jsoniter.demo.object_with_10_fields; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.afterburner.AfterburnerModule; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchJackson.deser thrpt 5 3536224.629 ± 22392.435 ops/s -BenchJackson.ser thrpt 5 5373951.842 ± 325328.400 ops/s - */ -@State(Scope.Thread) -public class BenchJackson { - - private ObjectMapper objectMapper; - private TypeReference typeReference; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private TestObject testObject; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - objectMapper = new ObjectMapper(); - objectMapper.registerModule(new AfterburnerModule()); - typeReference = new TypeReference() { - }; - byteArrayOutputStream = new ByteArrayOutputStream(); - testJSON = TestObject.createTestJSON(); - testObject = TestObject.createTestObject(); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - byteArrayOutputStream.reset(); - objectMapper.writeValue(byteArrayOutputStream, testObject); - bh.consume(byteArrayOutputStream); - } - - - @Benchmark - public void deser(Blackhole bh) throws IOException { - bh.consume(objectMapper.readValue(testJSON, typeReference)); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_10_fields.BenchJackson", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_10_fields/BenchJsoniter.java b/demo/src/test/java/com/jsoniter/demo/object_with_10_fields/BenchJsoniter.java deleted file mode 100644 index eec9862b..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_10_fields/BenchJsoniter.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.jsoniter.demo.object_with_10_fields; - -import com.jsoniter.DecodingMode; -import com.jsoniter.JsonIterator; -import com.jsoniter.output.EncodingMode; -import com.jsoniter.output.JsonStream; -import com.jsoniter.spi.TypeLiteral; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchJsoniter.deser thrpt 5 7886115.451 ± 335769.969 ops/s (2.23x) -BenchJsoniter.ser thrpt 5 13216858.758 ± 611385.896 ops/s (2.46x) - */ -@State(Scope.Thread) -public class BenchJsoniter { - - private TestObject testObject; - private JsonStream stream; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private JsonIterator iter; - private TypeLiteral typeLiteral; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - JsonStream.setMode(EncodingMode.DYNAMIC_MODE); - JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); - testObject = TestObject.createTestObject(); - testJSON = TestObject.createTestJSON(); - stream = new JsonStream(null, 512); - byteArrayOutputStream = new ByteArrayOutputStream(); - iter = new JsonIterator(); - typeLiteral = TypeLiteral.create(TestObject.class); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - byteArrayOutputStream.reset(); - stream.reset(byteArrayOutputStream); - stream.writeVal(testObject); - bh.consume(byteArrayOutputStream); - } - - @Benchmark - public void deser(Blackhole bh) throws IOException { - iter.reset(testJSON); - bh.consume(iter.read(typeLiteral)); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_10_fields.BenchJsoniter", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_10_fields/TestObject.java b/demo/src/test/java/com/jsoniter/demo/object_with_10_fields/TestObject.java deleted file mode 100644 index f10c900b..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_10_fields/TestObject.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.jsoniter.demo.object_with_10_fields; - -import com.dslplatform.json.CompiledJson; -import com.jsoniter.output.JsonStream; - -@CompiledJson -public class TestObject { - - public String field1; - public String field2; - public String field3; - public String field4; - public String field5; - public String field6; - public String field7; - public String field8; - public String field9; - public String field10; - - public static TestObject createTestObject() { - TestObject testObject = new TestObject(); - testObject.field1 = ""; - testObject.field2 = ""; - testObject.field3 = ""; - testObject.field4 = ""; - testObject.field5 = ""; - testObject.field6 = ""; - testObject.field7 = ""; - testObject.field8 = ""; - testObject.field9 = ""; - testObject.field10 = ""; - return testObject; - } - - public static byte[] createTestJSON() { - return JsonStream.serialize(createTestObject()).getBytes(); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_15_fields/BenchDslJson.java b/demo/src/test/java/com/jsoniter/demo/object_with_15_fields/BenchDslJson.java deleted file mode 100644 index 936953b3..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_15_fields/BenchDslJson.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.jsoniter.demo.object_with_15_fields; - -import com.dslplatform.json.CustomJsonReader; -import com.dslplatform.json.ExternalSerialization; -import com.dslplatform.json.JsonWriter; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchDslJson.deser thrpt 5 22328042.432 ± 311925.080 ops/s (3.67x) -BenchDslJson.ser thrpt 5 17639416.242 ± 136738.841 ops/s (2.17x) - */ -@State(Scope.Thread) -public class BenchDslJson { - - private TestObject testObject; - private JsonWriter jsonWriter; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private CustomJsonReader reader; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - testObject = TestObject.createTestObject(); - testJSON = TestObject.createTestJSON(); - jsonWriter = new JsonWriter(); - byteArrayOutputStream = new ByteArrayOutputStream(); - reader = new CustomJsonReader(testJSON); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - jsonWriter.reset(); - byteArrayOutputStream.reset(); - ExternalSerialization.serialize(testObject, jsonWriter, false); - jsonWriter.toStream(byteArrayOutputStream); - bh.consume(byteArrayOutputStream); - } - - @Benchmark - public void deser(Blackhole bh) throws IOException { - reader.reset(); - reader.read(); - reader.getNextToken(); - TestObject obj = new TestObject(); - ExternalSerialization.deserialize(obj, reader); - bh.consume(obj); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_15_fields.BenchDslJson", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_15_fields/BenchJackson.java b/demo/src/test/java/com/jsoniter/demo/object_with_15_fields/BenchJackson.java deleted file mode 100644 index ba4cc4b2..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_15_fields/BenchJackson.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.jsoniter.demo.object_with_15_fields; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.afterburner.AfterburnerModule; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchJackson.deser thrpt 5 1554344.234 ± 341967.936 ops/s -BenchJackson.ser thrpt 5 2399893.422 ± 199116.685 ops/s - */ -@State(Scope.Thread) -public class BenchJackson { - - private ObjectMapper objectMapper; - private TypeReference typeReference; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private TestObject testObject; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - objectMapper = new ObjectMapper(); - objectMapper.registerModule(new AfterburnerModule()); - typeReference = new TypeReference() { - }; - byteArrayOutputStream = new ByteArrayOutputStream(); - testJSON = TestObject.createTestJSON(); - testObject = TestObject.createTestObject(); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - byteArrayOutputStream.reset(); - objectMapper.writeValue(byteArrayOutputStream, testObject); - bh.consume(byteArrayOutputStream); - } - - - @Benchmark - public void deser(Blackhole bh) throws IOException { - bh.consume(objectMapper.readValue(testJSON, typeReference)); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_15_fields.BenchJackson", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_15_fields/BenchJsoniter.java b/demo/src/test/java/com/jsoniter/demo/object_with_15_fields/BenchJsoniter.java deleted file mode 100644 index b60ee1db..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_15_fields/BenchJsoniter.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.jsoniter.demo.object_with_15_fields; - -import com.jsoniter.DecodingMode; -import com.jsoniter.JsonIterator; -import com.jsoniter.output.EncodingMode; -import com.jsoniter.output.JsonStream; -import com.jsoniter.spi.TypeLiteral; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchJsoniter.deser thrpt 5 3168123.269 ± 467680.688 ops/s -BenchJsoniter.ser thrpt 5 8903974.466 ± 500114.000 ops/s - */ -@State(Scope.Thread) -public class BenchJsoniter { - - private TestObject testObject; - private JsonStream stream; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private JsonIterator iter; - private TypeLiteral typeLiteral; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - JsonStream.setMode(EncodingMode.DYNAMIC_MODE); - JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); - testObject = TestObject.createTestObject(); - testJSON = TestObject.createTestJSON(); - stream = new JsonStream(null, 512); - byteArrayOutputStream = new ByteArrayOutputStream(); - iter = new JsonIterator(); - typeLiteral = TypeLiteral.create(TestObject.class); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - byteArrayOutputStream.reset(); - stream.reset(byteArrayOutputStream); - stream.writeVal(testObject); - bh.consume(byteArrayOutputStream); - } - - @Benchmark - public void deser(Blackhole bh) throws IOException { - iter.reset(testJSON); - bh.consume(iter.read(typeLiteral)); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_15_fields.BenchJsoniter", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_15_fields/TestObject.java b/demo/src/test/java/com/jsoniter/demo/object_with_15_fields/TestObject.java deleted file mode 100644 index 8948aab5..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_15_fields/TestObject.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.jsoniter.demo.object_with_15_fields; - -import com.dslplatform.json.CompiledJson; -import com.jsoniter.output.JsonStream; - -@CompiledJson -public class TestObject { - - public String field1; - public String field2; - public String field3; - public String field4; - public String field5; - public String field6; - public String field7; - public String field8; - public String field9; - public String field10; - public String field11; - public String field12; - public String field13; - public String field14; - public String field15; - - public static TestObject createTestObject() { - TestObject testObject = new TestObject(); - testObject.field1 = ""; - testObject.field2 = ""; - testObject.field3 = ""; - testObject.field4 = ""; - testObject.field5 = ""; - testObject.field6 = ""; - testObject.field7 = ""; - testObject.field8 = ""; - testObject.field9 = ""; - testObject.field10 = ""; - testObject.field11 = ""; - testObject.field12 = ""; - testObject.field13 = ""; - testObject.field14 = ""; - testObject.field15 = ""; - return testObject; - } - - public static byte[] createTestJSON() { - return JsonStream.serialize(createTestObject()).getBytes(); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/BenchJackson.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/BenchJackson.java deleted file mode 100644 index 87864a4b..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/BenchJackson.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.jsoniter.demo.object_with_1_double_field; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.afterburner.AfterburnerModule; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchJackson.deser thrpt 5 5822021.435 ± 208030.778 ops/s -BenchJackson.ser thrpt 5 5359413.928 ± 180612.520 ops/s - */ -@State(Scope.Thread) -public class BenchJackson { - - private ObjectMapper objectMapper; - private TypeReference typeReference; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private TestObject testObject; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - objectMapper = new ObjectMapper(); - objectMapper.registerModule(new AfterburnerModule()); - typeReference = new TypeReference() { - }; - byteArrayOutputStream = new ByteArrayOutputStream(); - testJSON = TestObject.createTestJSON(); - testObject = TestObject.createTestObject(); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - byteArrayOutputStream.reset(); - objectMapper.writeValue(byteArrayOutputStream, testObject); - bh.consume(byteArrayOutputStream); - } - - - @Benchmark - public void deser(Blackhole bh) throws IOException { - bh.consume(objectMapper.readValue(testJSON, typeReference)); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_1_double_field.BenchJackson", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/BenchJsoniter.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/BenchJsoniter.java deleted file mode 100644 index b2f351fa..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/BenchJsoniter.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.jsoniter.demo.object_with_1_double_field; - -import com.jsoniter.DecodingMode; -import com.jsoniter.JsonIterator; -import com.jsoniter.output.EncodingMode; -import com.jsoniter.output.JsonStream; -import com.jsoniter.spi.TypeLiteral; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchJsoniter.deser thrpt 5 38116542.503 ± 1084084.112 ops/s (6.55x) -BenchJsoniter.ser thrpt 5 23531684.342 ± 132018.833 ops/s (4.39x) - */ -@State(Scope.Thread) -public class BenchJsoniter { - - private TestObject testObject; - private JsonStream stream; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private JsonIterator iter; - private TypeLiteral typeLiteral; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - JsonStream.setMode(EncodingMode.DYNAMIC_MODE); - JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); - testObject = TestObject.createTestObject(); - testJSON = TestObject.createTestJSON(); - stream = new JsonStream(null, 512); - byteArrayOutputStream = new ByteArrayOutputStream(); - iter = new JsonIterator(); - typeLiteral = TypeLiteral.create(TestObject.class); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - byteArrayOutputStream.reset(); - stream.reset(byteArrayOutputStream); - stream.writeVal(testObject); - bh.consume(byteArrayOutputStream); - } - - @Benchmark - public void deser(Blackhole bh) throws IOException { - iter.reset(testJSON); - bh.consume(iter.read(typeLiteral)); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_1_double_field.BenchJsoniter", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/BenchThrift.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/BenchThrift.java deleted file mode 100644 index 51d2036c..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/BenchThrift.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.jsoniter.demo.object_with_1_double_field; - -import org.apache.thrift.TDeserializer; -import org.apache.thrift.TException; -import org.apache.thrift.TSerializer; -import org.apache.thrift.protocol.TBinaryProtocol; -import org.apache.thrift.protocol.TCompactProtocol; -import org.apache.thrift.protocol.TTupleProtocol; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.IOException; - -/* -Tuple -Benchmark Mode Cnt Score Error Units -BenchThrift.deser thrpt 5 77600452.253 ± 3008784.281 ops/s (13.33x) -BenchThrift.ser thrpt 5 17332754.587 ± 61510.842 ops/s (3.23x) - -Binary -Benchmark Mode Cnt Score Error Units -BenchThrift.deser thrpt 5 49551979.325 ± 2339931.620 ops/s (8.51x) -BenchThrift.ser thrpt 5 8364225.101 ± 71229.879 ops/s (1.56x) - */ -@State(Scope.Thread) -public class BenchThrift { - - private TSerializer serializer; - private ThriftTestObject testObject; - private TDeserializer deserializer; - private byte[] testData; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) throws TException { - testObject = new ThriftTestObject(); - testObject.field1 = 1024; - serializer = new TSerializer(new TTupleProtocol.Factory()); -// serializer = new TSerializer(new TBinaryProtocol.Factory()); - deserializer = new TDeserializer(new TTupleProtocol.Factory()); -// deserializer = new TDeserializer(new TBinaryProtocol.Factory()); - testData = serializer.serialize(testObject); - } - - @Test - public void test() throws TException { - byte[] output = new TSerializer(new TCompactProtocol.Factory()).serialize(testObject); - System.out.println(output.length); - } - - @Benchmark - public void ser(Blackhole bh) throws TException { - bh.consume(serializer.serialize(testObject)); - } - - @Benchmark - public void deser(Blackhole bh) throws TException { - ThriftTestObject obj = new ThriftTestObject(); - deserializer.deserialize(testObject, testData); - bh.consume(obj); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_1_double_field.BenchThrift", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/TestObject.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/TestObject.java deleted file mode 100644 index 567aa950..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/TestObject.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.jsoniter.demo.object_with_1_double_field; - -import com.dslplatform.json.CompiledJson; -import com.jsoniter.output.JsonStream; - -@CompiledJson -public class TestObject { - - public double field1; - - public static TestObject createTestObject() { - TestObject testObject = new TestObject(); - testObject.field1 = 10.24d; - return testObject; - } - - public static byte[] createTestJSON() { - return JsonStream.serialize(createTestObject()).getBytes(); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/TestObject.thrift b/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/TestObject.thrift deleted file mode 100644 index 5eafe8ee..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/TestObject.thrift +++ /dev/null @@ -1,4 +0,0 @@ -struct ThriftTestObject -{ - 1: double field1 -} \ No newline at end of file diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/ThriftTestObject.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/ThriftTestObject.java deleted file mode 100644 index b58aa784..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_double_field/ThriftTestObject.java +++ /dev/null @@ -1,384 +0,0 @@ -package com.jsoniter.demo.object_with_1_double_field; /** - * Autogenerated by Thrift Compiler (0.9.1) - * - * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - * @generated - */ -import org.apache.thrift.scheme.IScheme; -import org.apache.thrift.scheme.SchemeFactory; -import org.apache.thrift.scheme.StandardScheme; - -import org.apache.thrift.scheme.TupleScheme; -import org.apache.thrift.protocol.TTupleProtocol; -import org.apache.thrift.protocol.TProtocolException; -import org.apache.thrift.EncodingUtils; -import org.apache.thrift.TException; -import org.apache.thrift.async.AsyncMethodCallback; -import org.apache.thrift.server.AbstractNonblockingServer.*; -import java.util.List; -import java.util.ArrayList; -import java.util.Map; -import java.util.HashMap; -import java.util.EnumMap; -import java.util.Set; -import java.util.HashSet; -import java.util.EnumSet; -import java.util.Collections; -import java.util.BitSet; -import java.nio.ByteBuffer; -import java.util.Arrays; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ThriftTestObject implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { - private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("ThriftTestObject"); - - private static final org.apache.thrift.protocol.TField FIELD1_FIELD_DESC = new org.apache.thrift.protocol.TField("field1", org.apache.thrift.protocol.TType.DOUBLE, (short)1); - - private static final Map, SchemeFactory> schemes = new HashMap, SchemeFactory>(); - static { - schemes.put(StandardScheme.class, new ThriftTestObjectStandardSchemeFactory()); - schemes.put(TupleScheme.class, new ThriftTestObjectTupleSchemeFactory()); - } - - public double field1; // required - - /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ - public enum _Fields implements org.apache.thrift.TFieldIdEnum { - FIELD1((short)1, "field1"); - - private static final Map byName = new HashMap(); - - static { - for (_Fields field : EnumSet.allOf(_Fields.class)) { - byName.put(field.getFieldName(), field); - } - } - - /** - * Find the _Fields constant that matches fieldId, or null if its not found. - */ - public static _Fields findByThriftId(int fieldId) { - switch(fieldId) { - case 1: // FIELD1 - return FIELD1; - default: - return null; - } - } - - /** - * Find the _Fields constant that matches fieldId, throwing an exception - * if it is not found. - */ - public static _Fields findByThriftIdOrThrow(int fieldId) { - _Fields fields = findByThriftId(fieldId); - if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!"); - return fields; - } - - /** - * Find the _Fields constant that matches name, or null if its not found. - */ - public static _Fields findByName(String name) { - return byName.get(name); - } - - private final short _thriftId; - private final String _fieldName; - - _Fields(short thriftId, String fieldName) { - _thriftId = thriftId; - _fieldName = fieldName; - } - - public short getThriftFieldId() { - return _thriftId; - } - - public String getFieldName() { - return _fieldName; - } - } - - // isset id assignments - private static final int __FIELD1_ISSET_ID = 0; - private byte __isset_bitfield = 0; - public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; - static { - Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); - tmpMap.put(_Fields.FIELD1, new org.apache.thrift.meta_data.FieldMetaData("field1", org.apache.thrift.TFieldRequirementType.DEFAULT, - new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.DOUBLE))); - metaDataMap = Collections.unmodifiableMap(tmpMap); - org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(ThriftTestObject.class, metaDataMap); - } - - public ThriftTestObject() { - } - - public ThriftTestObject( - double field1) - { - this(); - this.field1 = field1; - setField1IsSet(true); - } - - /** - * Performs a deep copy on other. - */ - public ThriftTestObject(ThriftTestObject other) { - __isset_bitfield = other.__isset_bitfield; - this.field1 = other.field1; - } - - public ThriftTestObject deepCopy() { - return new ThriftTestObject(this); - } - - @Override - public void clear() { - setField1IsSet(false); - this.field1 = 0.0; - } - - public double getField1() { - return this.field1; - } - - public ThriftTestObject setField1(double field1) { - this.field1 = field1; - setField1IsSet(true); - return this; - } - - public void unsetField1() { - __isset_bitfield = EncodingUtils.clearBit(__isset_bitfield, __FIELD1_ISSET_ID); - } - - /** Returns true if field field1 is set (has been assigned a value) and false otherwise */ - public boolean isSetField1() { - return EncodingUtils.testBit(__isset_bitfield, __FIELD1_ISSET_ID); - } - - public void setField1IsSet(boolean value) { - __isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __FIELD1_ISSET_ID, value); - } - - public void setFieldValue(_Fields field, Object value) { - switch (field) { - case FIELD1: - if (value == null) { - unsetField1(); - } else { - setField1((Double)value); - } - break; - - } - } - - public Object getFieldValue(_Fields field) { - switch (field) { - case FIELD1: - return Double.valueOf(getField1()); - - } - throw new IllegalStateException(); - } - - /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ - public boolean isSet(_Fields field) { - if (field == null) { - throw new IllegalArgumentException(); - } - - switch (field) { - case FIELD1: - return isSetField1(); - } - throw new IllegalStateException(); - } - - @Override - public boolean equals(Object that) { - if (that == null) - return false; - if (that instanceof ThriftTestObject) - return this.equals((ThriftTestObject)that); - return false; - } - - public boolean equals(ThriftTestObject that) { - if (that == null) - return false; - - boolean this_present_field1 = true; - boolean that_present_field1 = true; - if (this_present_field1 || that_present_field1) { - if (!(this_present_field1 && that_present_field1)) - return false; - if (this.field1 != that.field1) - return false; - } - - return true; - } - - @Override - public int hashCode() { - return 0; - } - - @Override - public int compareTo(ThriftTestObject other) { - if (!getClass().equals(other.getClass())) { - return getClass().getName().compareTo(other.getClass().getName()); - } - - int lastComparison = 0; - - lastComparison = Boolean.valueOf(isSetField1()).compareTo(other.isSetField1()); - if (lastComparison != 0) { - return lastComparison; - } - if (isSetField1()) { - lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.field1, other.field1); - if (lastComparison != 0) { - return lastComparison; - } - } - return 0; - } - - public _Fields fieldForId(int fieldId) { - return _Fields.findByThriftId(fieldId); - } - - public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { - schemes.get(iprot.getScheme()).getScheme().read(iprot, this); - } - - public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { - schemes.get(oprot.getScheme()).getScheme().write(oprot, this); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("ThriftTestObject("); - boolean first = true; - - sb.append("field1:"); - sb.append(this.field1); - first = false; - sb.append(")"); - return sb.toString(); - } - - public void validate() throws org.apache.thrift.TException { - // check for required fields - // check for sub-struct validity - } - - private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { - try { - write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); - } catch (org.apache.thrift.TException te) { - throw new java.io.IOException(te); - } - } - - private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { - try { - // it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor. - __isset_bitfield = 0; - read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); - } catch (org.apache.thrift.TException te) { - throw new java.io.IOException(te); - } - } - - private static class ThriftTestObjectStandardSchemeFactory implements SchemeFactory { - public ThriftTestObjectStandardScheme getScheme() { - return new ThriftTestObjectStandardScheme(); - } - } - - private static class ThriftTestObjectStandardScheme extends StandardScheme { - - public void read(org.apache.thrift.protocol.TProtocol iprot, ThriftTestObject struct) throws org.apache.thrift.TException { - org.apache.thrift.protocol.TField schemeField; - iprot.readStructBegin(); - while (true) - { - schemeField = iprot.readFieldBegin(); - if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { - break; - } - switch (schemeField.id) { - case 1: // FIELD1 - if (schemeField.type == org.apache.thrift.protocol.TType.DOUBLE) { - struct.field1 = iprot.readDouble(); - struct.setField1IsSet(true); - } else { - org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); - } - break; - default: - org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); - } - iprot.readFieldEnd(); - } - iprot.readStructEnd(); - - // check for required fields of primitive type, which can't be checked in the validate method - struct.validate(); - } - - public void write(org.apache.thrift.protocol.TProtocol oprot, ThriftTestObject struct) throws org.apache.thrift.TException { - struct.validate(); - - oprot.writeStructBegin(STRUCT_DESC); - oprot.writeFieldBegin(FIELD1_FIELD_DESC); - oprot.writeDouble(struct.field1); - oprot.writeFieldEnd(); - oprot.writeFieldStop(); - oprot.writeStructEnd(); - } - - } - - private static class ThriftTestObjectTupleSchemeFactory implements SchemeFactory { - public ThriftTestObjectTupleScheme getScheme() { - return new ThriftTestObjectTupleScheme(); - } - } - - private static class ThriftTestObjectTupleScheme extends TupleScheme { - - @Override - public void write(org.apache.thrift.protocol.TProtocol prot, ThriftTestObject struct) throws org.apache.thrift.TException { - TTupleProtocol oprot = (TTupleProtocol) prot; - BitSet optionals = new BitSet(); - if (struct.isSetField1()) { - optionals.set(0); - } - oprot.writeBitSet(optionals, 1); - if (struct.isSetField1()) { - oprot.writeDouble(struct.field1); - } - } - - @Override - public void read(org.apache.thrift.protocol.TProtocol prot, ThriftTestObject struct) throws org.apache.thrift.TException { - TTupleProtocol iprot = (TTupleProtocol) prot; - BitSet incoming = iprot.readBitSet(1); - if (incoming.get(0)) { - struct.field1 = iprot.readDouble(); - struct.setField1IsSet(true); - } - } - } - -} - diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_field/BenchDslJson.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_field/BenchDslJson.java deleted file mode 100644 index 9b39c1f2..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_field/BenchDslJson.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.jsoniter.demo.object_with_1_field; - -import com.dslplatform.json.CustomJsonReader; -import com.dslplatform.json.JsonWriter; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchDslJson.deser thrpt 5 33989158.545 ± 1354434.977 ops/s (4.17x) -BenchDslJson.ser thrpt 5 22636791.114 ± 66490.593 ops/s (2.36x) - */ -@State(Scope.Thread) -public class BenchDslJson { - - private TestObject testObject; - private JsonWriter jsonWriter; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private CustomJsonReader reader; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - testObject = TestObject.createTestObject(); - testJSON = TestObject.createTestJSON(); - jsonWriter = new JsonWriter(); - byteArrayOutputStream = new ByteArrayOutputStream(); - reader = new CustomJsonReader(testJSON); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - jsonWriter.reset(); - byteArrayOutputStream.reset(); -// ExternalSerialization.serialize(testObject, jsonWriter, false); - jsonWriter.toStream(byteArrayOutputStream); - bh.consume(byteArrayOutputStream); - } - - @Benchmark - public void deser(Blackhole bh) throws IOException { - reader.reset(); - reader.read(); - reader.getNextToken(); - TestObject obj = new TestObject(); -// ExternalSerialization.deserialize(obj, reader); - bh.consume(obj); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_1_field.BenchDslJson", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_field/BenchJackson.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_field/BenchJackson.java deleted file mode 100644 index 4b296fa1..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_field/BenchJackson.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.jsoniter.demo.object_with_1_field; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.afterburner.AfterburnerModule; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchJackson.deser thrpt 5 8159400.294 ± 689022.762 ops/s -BenchJackson.ser thrpt 5 9580832.217 ± 469534.662 ops/s - */ -@State(Scope.Thread) -public class BenchJackson { - - private ObjectMapper objectMapper; - private TypeReference typeReference; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private TestObject testObject; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - objectMapper = new ObjectMapper(); - objectMapper.registerModule(new AfterburnerModule()); - typeReference = new TypeReference() { - }; - byteArrayOutputStream = new ByteArrayOutputStream(); - testJSON = TestObject.createTestJSON(); - testObject = TestObject.createTestObject(); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - byteArrayOutputStream.reset(); - objectMapper.writeValue(byteArrayOutputStream, testObject); - bh.consume(byteArrayOutputStream); - } - - - @Benchmark - public void deser(Blackhole bh) throws IOException { - bh.consume(objectMapper.readValue(testJSON, typeReference)); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_1_field.BenchJackson", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_field/BenchJsoniter.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_field/BenchJsoniter.java deleted file mode 100644 index b9a190d2..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_field/BenchJsoniter.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.jsoniter.demo.object_with_1_field; - -import com.jsoniter.DecodingMode; -import com.jsoniter.JsonIterator; -import com.jsoniter.output.EncodingMode; -import com.jsoniter.output.JsonStream; -import com.jsoniter.spi.Encoder; -import com.jsoniter.spi.TypeLiteral; -import javassist.ClassClassPath; -import javassist.ClassPool; -import javassist.NotFoundException; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchJsoniter.deser thrpt 5 29611012.551 ± 1839988.704 ops/s (3.63x) -BenchJsoniter.ser thrpt 5 30237005.400 ± 110653.659 ops/s (3.16x) - */ -@State(Scope.Thread) -public class BenchJsoniter { - - private TestObject testObject; - private JsonStream stream; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private JsonIterator iter; - private TypeLiteral typeLiteral; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - JsonStream.setMode(EncodingMode.DYNAMIC_MODE); - JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); - testObject = TestObject.createTestObject(); - testJSON = TestObject.createTestJSON(); - stream = new JsonStream(null, 512); - byteArrayOutputStream = new ByteArrayOutputStream(); - iter = new JsonIterator(); - typeLiteral = TypeLiteral.create(TestObject.class); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - byteArrayOutputStream.reset(); - stream.reset(byteArrayOutputStream); - stream.writeVal(testObject); - bh.consume(byteArrayOutputStream); - } - - @Benchmark - public void deser(Blackhole bh) throws IOException { - iter.reset(testJSON); - bh.consume(iter.read(typeLiteral)); - } - - @Test - public void test() throws NotFoundException, ClassNotFoundException { - JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); - JsonIterator.deserialize("{}", TestObject.class); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_1_field.BenchJsoniter", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_field/BenchThrift.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_field/BenchThrift.java deleted file mode 100644 index 22588a14..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_field/BenchThrift.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.jsoniter.demo.object_with_1_field; - -import org.apache.thrift.TDeserializer; -import org.apache.thrift.TException; -import org.apache.thrift.TSerializer; -import org.apache.thrift.protocol.TBinaryProtocol; -import org.apache.thrift.protocol.TCompactProtocol; -import org.apache.thrift.protocol.TTupleProtocol; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.IOException; - -/* -Tuple -Benchmark Mode Cnt Score Error Units -BenchThrift.deser thrpt 5 19999891.957 ± 1690769.199 ops/s (2.45x) -BenchThrift.ser thrpt 5 7776020.372 ± 133622.260 ops/s (0.81x) - -Binary -Benchmark Mode Cnt Score Error Units -BenchThrift.deser thrpt 5 18565469.435 ± 669296.325 ops/s (2.28x) -BenchThrift.ser thrpt 5 6213563.710 ± 26744.572 ops/s (0.65x) - */ -@State(Scope.Thread) -public class BenchThrift { - - private TSerializer serializer; - private ThriftTestObject testObject; - private TDeserializer deserializer; - private byte[] testData; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) throws TException { - testObject = new ThriftTestObject(); - testObject.field1 = "field1"; - serializer = new TSerializer(new TTupleProtocol.Factory()); -// serializer = new TSerializer(new TBinaryProtocol.Factory()); - deserializer = new TDeserializer(new TTupleProtocol.Factory()); -// deserializer = new TDeserializer(new TBinaryProtocol.Factory()); - testData = serializer.serialize(testObject); - } - - @Test - public void test() throws TException { - byte[] output = new TSerializer(new TCompactProtocol.Factory()).serialize(testObject); - System.out.println(output.length); - } - - @Benchmark - public void ser(Blackhole bh) throws TException { - bh.consume(serializer.serialize(testObject)); - } - - @Benchmark - public void deser(Blackhole bh) throws TException { - ThriftTestObject obj = new ThriftTestObject(); - deserializer.deserialize(testObject, testData); - bh.consume(obj); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_1_field.BenchThrift", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_field/TestObject.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_field/TestObject.java deleted file mode 100644 index 133796a8..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_field/TestObject.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.jsoniter.demo.object_with_1_field; - -import com.dslplatform.json.CompiledJson; -import com.jsoniter.output.JsonStream; - -@CompiledJson -public class TestObject { - - public String field1; - - public static TestObject createTestObject() { - TestObject testObject = new TestObject(); - testObject.field1 = "field1field2field3field4field5"; - return testObject; - } - - public static byte[] createTestJSON() { - return JsonStream.serialize(createTestObject()).getBytes(); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_field/TestObject.thrift b/demo/src/test/java/com/jsoniter/demo/object_with_1_field/TestObject.thrift deleted file mode 100644 index c458d693..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_field/TestObject.thrift +++ /dev/null @@ -1,4 +0,0 @@ -struct ThriftTestObject -{ - 1: string field1 -} \ No newline at end of file diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_field/ThriftTestObject.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_field/ThriftTestObject.java deleted file mode 100644 index 921414c1..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_field/ThriftTestObject.java +++ /dev/null @@ -1,386 +0,0 @@ -package com.jsoniter.demo.object_with_1_field; /** - * Autogenerated by Thrift Compiler (0.9.1) - * - * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - * @generated - */ -import org.apache.thrift.scheme.IScheme; -import org.apache.thrift.scheme.SchemeFactory; -import org.apache.thrift.scheme.StandardScheme; - -import org.apache.thrift.scheme.TupleScheme; -import org.apache.thrift.protocol.TTupleProtocol; -import org.apache.thrift.protocol.TProtocolException; -import org.apache.thrift.EncodingUtils; -import org.apache.thrift.TException; -import org.apache.thrift.async.AsyncMethodCallback; -import org.apache.thrift.server.AbstractNonblockingServer.*; -import java.util.List; -import java.util.ArrayList; -import java.util.Map; -import java.util.HashMap; -import java.util.EnumMap; -import java.util.Set; -import java.util.HashSet; -import java.util.EnumSet; -import java.util.Collections; -import java.util.BitSet; -import java.nio.ByteBuffer; -import java.util.Arrays; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ThriftTestObject implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { - private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("ThriftTestObject"); - - private static final org.apache.thrift.protocol.TField FIELD1_FIELD_DESC = new org.apache.thrift.protocol.TField("field1", org.apache.thrift.protocol.TType.STRING, (short)1); - - private static final Map, SchemeFactory> schemes = new HashMap, SchemeFactory>(); - static { - schemes.put(StandardScheme.class, new ThriftTestObjectStandardSchemeFactory()); - schemes.put(TupleScheme.class, new ThriftTestObjectTupleSchemeFactory()); - } - - public String field1; // required - - /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ - public enum _Fields implements org.apache.thrift.TFieldIdEnum { - FIELD1((short)1, "field1"); - - private static final Map byName = new HashMap(); - - static { - for (_Fields field : EnumSet.allOf(_Fields.class)) { - byName.put(field.getFieldName(), field); - } - } - - /** - * Find the _Fields constant that matches fieldId, or null if its not found. - */ - public static _Fields findByThriftId(int fieldId) { - switch(fieldId) { - case 1: // FIELD1 - return FIELD1; - default: - return null; - } - } - - /** - * Find the _Fields constant that matches fieldId, throwing an exception - * if it is not found. - */ - public static _Fields findByThriftIdOrThrow(int fieldId) { - _Fields fields = findByThriftId(fieldId); - if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!"); - return fields; - } - - /** - * Find the _Fields constant that matches name, or null if its not found. - */ - public static _Fields findByName(String name) { - return byName.get(name); - } - - private final short _thriftId; - private final String _fieldName; - - _Fields(short thriftId, String fieldName) { - _thriftId = thriftId; - _fieldName = fieldName; - } - - public short getThriftFieldId() { - return _thriftId; - } - - public String getFieldName() { - return _fieldName; - } - } - - // isset id assignments - public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; - static { - Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); - tmpMap.put(_Fields.FIELD1, new org.apache.thrift.meta_data.FieldMetaData("field1", org.apache.thrift.TFieldRequirementType.DEFAULT, - new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); - metaDataMap = Collections.unmodifiableMap(tmpMap); - org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(ThriftTestObject.class, metaDataMap); - } - - public ThriftTestObject() { - } - - public ThriftTestObject( - String field1) - { - this(); - this.field1 = field1; - } - - /** - * Performs a deep copy on other. - */ - public ThriftTestObject(ThriftTestObject other) { - if (other.isSetField1()) { - this.field1 = other.field1; - } - } - - public ThriftTestObject deepCopy() { - return new ThriftTestObject(this); - } - - @Override - public void clear() { - this.field1 = null; - } - - public String getField1() { - return this.field1; - } - - public ThriftTestObject setField1(String field1) { - this.field1 = field1; - return this; - } - - public void unsetField1() { - this.field1 = null; - } - - /** Returns true if field field1 is set (has been assigned a value) and false otherwise */ - public boolean isSetField1() { - return this.field1 != null; - } - - public void setField1IsSet(boolean value) { - if (!value) { - this.field1 = null; - } - } - - public void setFieldValue(_Fields field, Object value) { - switch (field) { - case FIELD1: - if (value == null) { - unsetField1(); - } else { - setField1((String)value); - } - break; - - } - } - - public Object getFieldValue(_Fields field) { - switch (field) { - case FIELD1: - return getField1(); - - } - throw new IllegalStateException(); - } - - /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ - public boolean isSet(_Fields field) { - if (field == null) { - throw new IllegalArgumentException(); - } - - switch (field) { - case FIELD1: - return isSetField1(); - } - throw new IllegalStateException(); - } - - @Override - public boolean equals(Object that) { - if (that == null) - return false; - if (that instanceof ThriftTestObject) - return this.equals((ThriftTestObject)that); - return false; - } - - public boolean equals(ThriftTestObject that) { - if (that == null) - return false; - - boolean this_present_field1 = true && this.isSetField1(); - boolean that_present_field1 = true && that.isSetField1(); - if (this_present_field1 || that_present_field1) { - if (!(this_present_field1 && that_present_field1)) - return false; - if (!this.field1.equals(that.field1)) - return false; - } - - return true; - } - - @Override - public int hashCode() { - return 0; - } - - @Override - public int compareTo(ThriftTestObject other) { - if (!getClass().equals(other.getClass())) { - return getClass().getName().compareTo(other.getClass().getName()); - } - - int lastComparison = 0; - - lastComparison = Boolean.valueOf(isSetField1()).compareTo(other.isSetField1()); - if (lastComparison != 0) { - return lastComparison; - } - if (isSetField1()) { - lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.field1, other.field1); - if (lastComparison != 0) { - return lastComparison; - } - } - return 0; - } - - public _Fields fieldForId(int fieldId) { - return _Fields.findByThriftId(fieldId); - } - - public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { - schemes.get(iprot.getScheme()).getScheme().read(iprot, this); - } - - public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { - schemes.get(oprot.getScheme()).getScheme().write(oprot, this); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("ThriftTestObject("); - boolean first = true; - - sb.append("field1:"); - if (this.field1 == null) { - sb.append("null"); - } else { - sb.append(this.field1); - } - first = false; - sb.append(")"); - return sb.toString(); - } - - public void validate() throws org.apache.thrift.TException { - // check for required fields - // check for sub-struct validity - } - - private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { - try { - write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); - } catch (org.apache.thrift.TException te) { - throw new java.io.IOException(te); - } - } - - private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { - try { - read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); - } catch (org.apache.thrift.TException te) { - throw new java.io.IOException(te); - } - } - - private static class ThriftTestObjectStandardSchemeFactory implements SchemeFactory { - public ThriftTestObjectStandardScheme getScheme() { - return new ThriftTestObjectStandardScheme(); - } - } - - private static class ThriftTestObjectStandardScheme extends StandardScheme { - - public void read(org.apache.thrift.protocol.TProtocol iprot, ThriftTestObject struct) throws org.apache.thrift.TException { - org.apache.thrift.protocol.TField schemeField; - iprot.readStructBegin(); - while (true) - { - schemeField = iprot.readFieldBegin(); - if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { - break; - } - switch (schemeField.id) { - case 1: // FIELD1 - if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { - struct.field1 = iprot.readString(); - struct.setField1IsSet(true); - } else { - org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); - } - break; - default: - org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); - } - iprot.readFieldEnd(); - } - iprot.readStructEnd(); - - // check for required fields of primitive type, which can't be checked in the validate method - struct.validate(); - } - - public void write(org.apache.thrift.protocol.TProtocol oprot, ThriftTestObject struct) throws org.apache.thrift.TException { - struct.validate(); - - oprot.writeStructBegin(STRUCT_DESC); - if (struct.field1 != null) { - oprot.writeFieldBegin(FIELD1_FIELD_DESC); - oprot.writeString(struct.field1); - oprot.writeFieldEnd(); - } - oprot.writeFieldStop(); - oprot.writeStructEnd(); - } - - } - - private static class ThriftTestObjectTupleSchemeFactory implements SchemeFactory { - public ThriftTestObjectTupleScheme getScheme() { - return new ThriftTestObjectTupleScheme(); - } - } - - private static class ThriftTestObjectTupleScheme extends TupleScheme { - - @Override - public void write(org.apache.thrift.protocol.TProtocol prot, ThriftTestObject struct) throws org.apache.thrift.TException { - TTupleProtocol oprot = (TTupleProtocol) prot; - BitSet optionals = new BitSet(); - if (struct.isSetField1()) { - optionals.set(0); - } - oprot.writeBitSet(optionals, 1); - if (struct.isSetField1()) { - oprot.writeString(struct.field1); - } - } - - @Override - public void read(org.apache.thrift.protocol.TProtocol prot, ThriftTestObject struct) throws org.apache.thrift.TException { - TTupleProtocol iprot = (TTupleProtocol) prot; - BitSet incoming = iprot.readBitSet(1); - if (incoming.get(0)) { - struct.field1 = iprot.readString(); - struct.setField1IsSet(true); - } - } - } - -} - diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/BenchDslJson.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/BenchDslJson.java deleted file mode 100644 index 15ff21bf..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/BenchDslJson.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.jsoniter.demo.object_with_1_int_field; - -import com.dslplatform.json.CustomJsonReader; -import com.dslplatform.json.JsonWriter; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchDslJson.deser thrpt 5 36999837.132 ± 765406.929 ops/s (4.46x) -BenchDslJson.ser thrpt 5 23126447.202 ± 247889.872 ops/s (2.26x) - */ -@State(Scope.Thread) -public class BenchDslJson { - - private TestObject testObject; - private JsonWriter jsonWriter; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private CustomJsonReader reader; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - testObject = TestObject.createTestObject(); - testJSON = TestObject.createTestJSON(); - jsonWriter = new JsonWriter(); - byteArrayOutputStream = new ByteArrayOutputStream(); - reader = new CustomJsonReader(testJSON); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - jsonWriter.reset(); - byteArrayOutputStream.reset(); -// ExternalSerialization.serialize(testObject, jsonWriter, false); - jsonWriter.toStream(byteArrayOutputStream); - bh.consume(byteArrayOutputStream); - } - - @Benchmark - public void deser(Blackhole bh) throws IOException { - reader.reset(); - reader.read(); - reader.getNextToken(); - TestObject obj = new TestObject(); -// ExternalSerialization.deserialize(obj, reader); - bh.consume(obj); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_1_int_field.BenchDslJson", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/BenchJackson.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/BenchJackson.java deleted file mode 100644 index 49468964..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/BenchJackson.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.jsoniter.demo.object_with_1_int_field; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.afterburner.AfterburnerModule; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchJackson.deser thrpt 5 8294194.459 ± 167985.331 ops/s -BenchJackson.ser thrpt 5 10240747.474 ± 372948.884 ops/s - */ -@State(Scope.Thread) -public class BenchJackson { - - private ObjectMapper objectMapper; - private TypeReference typeReference; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private TestObject testObject; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - objectMapper = new ObjectMapper(); - objectMapper.registerModule(new AfterburnerModule()); - typeReference = new TypeReference() { - }; - byteArrayOutputStream = new ByteArrayOutputStream(); - testJSON = TestObject.createTestJSON(); - testObject = TestObject.createTestObject(); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - byteArrayOutputStream.reset(); - objectMapper.writeValue(byteArrayOutputStream, testObject); - bh.consume(byteArrayOutputStream); - } - - - @Benchmark - public void deser(Blackhole bh) throws IOException { - bh.consume(objectMapper.readValue(testJSON, typeReference)); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_1_int_field.BenchJackson", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/BenchJsoniter.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/BenchJsoniter.java deleted file mode 100644 index bf2acfb7..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/BenchJsoniter.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.jsoniter.demo.object_with_1_int_field; - -import com.jsoniter.DecodingMode; -import com.jsoniter.JsonIterator; -import com.jsoniter.output.EncodingMode; -import com.jsoniter.output.JsonStream; -import com.jsoniter.spi.TypeLiteral; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchJsoniter.deser thrpt 5 44446325.748 ± 1126285.176 ops/s (5.36x) -BenchJsoniter.ser thrpt 5 33525853.836 ± 1197831.185 ops/s (3.27x) - */ -@State(Scope.Thread) -public class BenchJsoniter { - - private TestObject testObject; - private JsonStream stream; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private JsonIterator iter; - private TypeLiteral typeLiteral; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - JsonStream.setMode(EncodingMode.DYNAMIC_MODE); - JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); - testObject = TestObject.createTestObject(); - testJSON = TestObject.createTestJSON(); - stream = new JsonStream(null, 512); - byteArrayOutputStream = new ByteArrayOutputStream(); - iter = new JsonIterator(); - typeLiteral = TypeLiteral.create(TestObject.class); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - byteArrayOutputStream.reset(); - stream.reset(byteArrayOutputStream); - stream.writeVal(testObject); - bh.consume(byteArrayOutputStream); - } - - @Benchmark - public void deser(Blackhole bh) throws IOException { - iter.reset(testJSON); - bh.consume(iter.read(typeLiteral)); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_1_int_field.BenchJsoniter", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/BenchThrift.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/BenchThrift.java deleted file mode 100644 index 9e9032dd..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/BenchThrift.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.jsoniter.demo.object_with_1_int_field; - -import org.apache.thrift.TDeserializer; -import org.apache.thrift.TException; -import org.apache.thrift.TSerializer; -import org.apache.thrift.protocol.TBinaryProtocol; -import org.apache.thrift.protocol.TCompactProtocol; -import org.apache.thrift.protocol.TTupleProtocol; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.IOException; - -/* -Tuple -Benchmark Mode Cnt Score Error Units -BenchThrift.deser thrpt 5 75471992.596 ± 2732275.161 ops/s (9.10x) -BenchThrift.ser thrpt 5 17130913.598 ± 74007.433 ops/s (1.67x) - -Binary -Benchmark Mode Cnt Score Error Units -BenchThrift.deser thrpt 5 56765029.626 ± 2660078.316 ops/s (6.84x) -BenchThrift.ser thrpt 5 8511186.468 ± 49663.163 ops/s (0.83x) - */ -@State(Scope.Thread) -public class BenchThrift { - - private TSerializer serializer; - private ThriftTestObject testObject; - private TDeserializer deserializer; - private byte[] testData; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) throws TException { - testObject = new ThriftTestObject(); - testObject.setField1(1024); - serializer = new TSerializer(new TTupleProtocol.Factory()); -// serializer = new TSerializer(new TBinaryProtocol.Factory()); - deserializer = new TDeserializer(new TTupleProtocol.Factory()); -// deserializer = new TDeserializer(new TBinaryProtocol.Factory()); - testData = serializer.serialize(testObject); - } - - @Test - public void test() throws TException { - byte[] output = new TSerializer(new TCompactProtocol.Factory()).serialize(testObject); - System.out.println(output.length); - } - - @Benchmark - public void ser(Blackhole bh) throws TException { - bh.consume(serializer.serialize(testObject)); - } - - @Benchmark - public void deser(Blackhole bh) throws TException { - ThriftTestObject obj = new ThriftTestObject(); - deserializer.deserialize(testObject, testData); - bh.consume(obj); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_1_int_field.BenchThrift", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/TestObject.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/TestObject.java deleted file mode 100644 index 83a9a0cf..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/TestObject.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.jsoniter.demo.object_with_1_int_field; - -import com.dslplatform.json.CompiledJson; -import com.jsoniter.output.JsonStream; - -@CompiledJson -public class TestObject { - - public int field1; - - public static TestObject createTestObject() { - TestObject testObject = new TestObject(); - testObject.field1 = 1024; - return testObject; - } - - public static byte[] createTestJSON() { - return JsonStream.serialize(createTestObject()).getBytes(); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/TestObject.thrift b/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/TestObject.thrift deleted file mode 100644 index b55e7829..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/TestObject.thrift +++ /dev/null @@ -1,4 +0,0 @@ -struct ThriftTestObject -{ - 1: i32 field1 -} \ No newline at end of file diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/ThriftTestObject.java b/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/ThriftTestObject.java deleted file mode 100644 index d4df9da8..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_1_int_field/ThriftTestObject.java +++ /dev/null @@ -1,384 +0,0 @@ -package com.jsoniter.demo.object_with_1_int_field; /** - * Autogenerated by Thrift Compiler (0.9.1) - * - * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - * @generated - */ -import org.apache.thrift.scheme.IScheme; -import org.apache.thrift.scheme.SchemeFactory; -import org.apache.thrift.scheme.StandardScheme; - -import org.apache.thrift.scheme.TupleScheme; -import org.apache.thrift.protocol.TTupleProtocol; -import org.apache.thrift.protocol.TProtocolException; -import org.apache.thrift.EncodingUtils; -import org.apache.thrift.TException; -import org.apache.thrift.async.AsyncMethodCallback; -import org.apache.thrift.server.AbstractNonblockingServer.*; -import java.util.List; -import java.util.ArrayList; -import java.util.Map; -import java.util.HashMap; -import java.util.EnumMap; -import java.util.Set; -import java.util.HashSet; -import java.util.EnumSet; -import java.util.Collections; -import java.util.BitSet; -import java.nio.ByteBuffer; -import java.util.Arrays; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ThriftTestObject implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { - private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("ThriftTestObject"); - - private static final org.apache.thrift.protocol.TField FIELD1_FIELD_DESC = new org.apache.thrift.protocol.TField("field1", org.apache.thrift.protocol.TType.I32, (short)1); - - private static final Map, SchemeFactory> schemes = new HashMap, SchemeFactory>(); - static { - schemes.put(StandardScheme.class, new ThriftTestObjectStandardSchemeFactory()); - schemes.put(TupleScheme.class, new ThriftTestObjectTupleSchemeFactory()); - } - - public int field1; // required - - /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ - public enum _Fields implements org.apache.thrift.TFieldIdEnum { - FIELD1((short)1, "field1"); - - private static final Map byName = new HashMap(); - - static { - for (_Fields field : EnumSet.allOf(_Fields.class)) { - byName.put(field.getFieldName(), field); - } - } - - /** - * Find the _Fields constant that matches fieldId, or null if its not found. - */ - public static _Fields findByThriftId(int fieldId) { - switch(fieldId) { - case 1: // FIELD1 - return FIELD1; - default: - return null; - } - } - - /** - * Find the _Fields constant that matches fieldId, throwing an exception - * if it is not found. - */ - public static _Fields findByThriftIdOrThrow(int fieldId) { - _Fields fields = findByThriftId(fieldId); - if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!"); - return fields; - } - - /** - * Find the _Fields constant that matches name, or null if its not found. - */ - public static _Fields findByName(String name) { - return byName.get(name); - } - - private final short _thriftId; - private final String _fieldName; - - _Fields(short thriftId, String fieldName) { - _thriftId = thriftId; - _fieldName = fieldName; - } - - public short getThriftFieldId() { - return _thriftId; - } - - public String getFieldName() { - return _fieldName; - } - } - - // isset id assignments - private static final int __FIELD1_ISSET_ID = 0; - private byte __isset_bitfield = 0; - public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; - static { - Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); - tmpMap.put(_Fields.FIELD1, new org.apache.thrift.meta_data.FieldMetaData("field1", org.apache.thrift.TFieldRequirementType.DEFAULT, - new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I32))); - metaDataMap = Collections.unmodifiableMap(tmpMap); - org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(ThriftTestObject.class, metaDataMap); - } - - public ThriftTestObject() { - } - - public ThriftTestObject( - int field1) - { - this(); - this.field1 = field1; - setField1IsSet(true); - } - - /** - * Performs a deep copy on other. - */ - public ThriftTestObject(ThriftTestObject other) { - __isset_bitfield = other.__isset_bitfield; - this.field1 = other.field1; - } - - public ThriftTestObject deepCopy() { - return new ThriftTestObject(this); - } - - @Override - public void clear() { - setField1IsSet(false); - this.field1 = 0; - } - - public int getField1() { - return this.field1; - } - - public ThriftTestObject setField1(int field1) { - this.field1 = field1; - setField1IsSet(true); - return this; - } - - public void unsetField1() { - __isset_bitfield = EncodingUtils.clearBit(__isset_bitfield, __FIELD1_ISSET_ID); - } - - /** Returns true if field field1 is set (has been assigned a value) and false otherwise */ - public boolean isSetField1() { - return EncodingUtils.testBit(__isset_bitfield, __FIELD1_ISSET_ID); - } - - public void setField1IsSet(boolean value) { - __isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __FIELD1_ISSET_ID, value); - } - - public void setFieldValue(_Fields field, Object value) { - switch (field) { - case FIELD1: - if (value == null) { - unsetField1(); - } else { - setField1((Integer)value); - } - break; - - } - } - - public Object getFieldValue(_Fields field) { - switch (field) { - case FIELD1: - return Integer.valueOf(getField1()); - - } - throw new IllegalStateException(); - } - - /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ - public boolean isSet(_Fields field) { - if (field == null) { - throw new IllegalArgumentException(); - } - - switch (field) { - case FIELD1: - return isSetField1(); - } - throw new IllegalStateException(); - } - - @Override - public boolean equals(Object that) { - if (that == null) - return false; - if (that instanceof ThriftTestObject) - return this.equals((ThriftTestObject)that); - return false; - } - - public boolean equals(ThriftTestObject that) { - if (that == null) - return false; - - boolean this_present_field1 = true; - boolean that_present_field1 = true; - if (this_present_field1 || that_present_field1) { - if (!(this_present_field1 && that_present_field1)) - return false; - if (this.field1 != that.field1) - return false; - } - - return true; - } - - @Override - public int hashCode() { - return 0; - } - - @Override - public int compareTo(ThriftTestObject other) { - if (!getClass().equals(other.getClass())) { - return getClass().getName().compareTo(other.getClass().getName()); - } - - int lastComparison = 0; - - lastComparison = Boolean.valueOf(isSetField1()).compareTo(other.isSetField1()); - if (lastComparison != 0) { - return lastComparison; - } - if (isSetField1()) { - lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.field1, other.field1); - if (lastComparison != 0) { - return lastComparison; - } - } - return 0; - } - - public _Fields fieldForId(int fieldId) { - return _Fields.findByThriftId(fieldId); - } - - public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { - schemes.get(iprot.getScheme()).getScheme().read(iprot, this); - } - - public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { - schemes.get(oprot.getScheme()).getScheme().write(oprot, this); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("ThriftTestObject("); - boolean first = true; - - sb.append("field1:"); - sb.append(this.field1); - first = false; - sb.append(")"); - return sb.toString(); - } - - public void validate() throws org.apache.thrift.TException { - // check for required fields - // check for sub-struct validity - } - - private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { - try { - write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); - } catch (org.apache.thrift.TException te) { - throw new java.io.IOException(te); - } - } - - private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { - try { - // it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor. - __isset_bitfield = 0; - read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); - } catch (org.apache.thrift.TException te) { - throw new java.io.IOException(te); - } - } - - private static class ThriftTestObjectStandardSchemeFactory implements SchemeFactory { - public ThriftTestObjectStandardScheme getScheme() { - return new ThriftTestObjectStandardScheme(); - } - } - - private static class ThriftTestObjectStandardScheme extends StandardScheme { - - public void read(org.apache.thrift.protocol.TProtocol iprot, ThriftTestObject struct) throws org.apache.thrift.TException { - org.apache.thrift.protocol.TField schemeField; - iprot.readStructBegin(); - while (true) - { - schemeField = iprot.readFieldBegin(); - if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { - break; - } - switch (schemeField.id) { - case 1: // FIELD1 - if (schemeField.type == org.apache.thrift.protocol.TType.I32) { - struct.field1 = iprot.readI32(); - struct.setField1IsSet(true); - } else { - org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); - } - break; - default: - org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); - } - iprot.readFieldEnd(); - } - iprot.readStructEnd(); - - // check for required fields of primitive type, which can't be checked in the validate method - struct.validate(); - } - - public void write(org.apache.thrift.protocol.TProtocol oprot, ThriftTestObject struct) throws org.apache.thrift.TException { - struct.validate(); - - oprot.writeStructBegin(STRUCT_DESC); - oprot.writeFieldBegin(FIELD1_FIELD_DESC); - oprot.writeI32(struct.field1); - oprot.writeFieldEnd(); - oprot.writeFieldStop(); - oprot.writeStructEnd(); - } - - } - - private static class ThriftTestObjectTupleSchemeFactory implements SchemeFactory { - public ThriftTestObjectTupleScheme getScheme() { - return new ThriftTestObjectTupleScheme(); - } - } - - private static class ThriftTestObjectTupleScheme extends TupleScheme { - - @Override - public void write(org.apache.thrift.protocol.TProtocol prot, ThriftTestObject struct) throws org.apache.thrift.TException { - TTupleProtocol oprot = (TTupleProtocol) prot; - BitSet optionals = new BitSet(); - if (struct.isSetField1()) { - optionals.set(0); - } - oprot.writeBitSet(optionals, 1); - if (struct.isSetField1()) { - oprot.writeI32(struct.field1); - } - } - - @Override - public void read(org.apache.thrift.protocol.TProtocol prot, ThriftTestObject struct) throws org.apache.thrift.TException { - TTupleProtocol iprot = (TTupleProtocol) prot; - BitSet incoming = iprot.readBitSet(1); - if (incoming.get(0)) { - struct.field1 = iprot.readI32(); - struct.setField1IsSet(true); - } - } - } - -} - diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/BenchDslJson.java b/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/BenchDslJson.java deleted file mode 100644 index 9bd89117..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/BenchDslJson.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.jsoniter.demo.object_with_5_fields; - -import com.dslplatform.json.CustomJsonReader; -import com.dslplatform.json.ExternalSerialization; -import com.dslplatform.json.JsonWriter; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchDslJson.deser thrpt 5 22328042.432 ± 311925.080 ops/s (3.67x) -BenchDslJson.ser thrpt 5 17639416.242 ± 136738.841 ops/s (2.17x) - */ -@State(Scope.Thread) -public class BenchDslJson { - - private TestObject testObject; - private JsonWriter jsonWriter; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private CustomJsonReader reader; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - testObject = TestObject.createTestObject(); - testJSON = TestObject.createTestJSON(); - jsonWriter = new JsonWriter(); - byteArrayOutputStream = new ByteArrayOutputStream(); - reader = new CustomJsonReader(testJSON); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - jsonWriter.reset(); - byteArrayOutputStream.reset(); - ExternalSerialization.serialize(testObject, jsonWriter, false); - jsonWriter.toStream(byteArrayOutputStream); - bh.consume(byteArrayOutputStream); - } - - @Benchmark - public void deser(Blackhole bh) throws IOException { - reader.reset(); - reader.read(); - reader.getNextToken(); - TestObject obj = new TestObject(); - ExternalSerialization.deserialize(obj, reader); - bh.consume(obj); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_5_fields.BenchDslJson", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/BenchJackson.java b/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/BenchJackson.java deleted file mode 100644 index 7aaed549..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/BenchJackson.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.jsoniter.demo.object_with_5_fields; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.afterburner.AfterburnerModule; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchJackson.deser thrpt 5 3536224.629 ± 22392.435 ops/s -BenchJackson.ser thrpt 5 5373951.842 ± 325328.400 ops/s - */ -@State(Scope.Thread) -public class BenchJackson { - - private ObjectMapper objectMapper; - private TypeReference typeReference; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private TestObject testObject; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - objectMapper = new ObjectMapper(); - objectMapper.registerModule(new AfterburnerModule()); - typeReference = new TypeReference() { - }; - byteArrayOutputStream = new ByteArrayOutputStream(); - testJSON = TestObject.createTestJSON(); - testObject = TestObject.createTestObject(); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - byteArrayOutputStream.reset(); - objectMapper.writeValue(byteArrayOutputStream, testObject); - bh.consume(byteArrayOutputStream); - } - - - @Benchmark - public void deser(Blackhole bh) throws IOException { - bh.consume(objectMapper.readValue(testJSON, typeReference)); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_5_fields.BenchJackson", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/BenchJsoniter.java b/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/BenchJsoniter.java deleted file mode 100644 index 7e80d4d3..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/BenchJsoniter.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.jsoniter.demo.object_with_5_fields; - -import com.jsoniter.CodegenAccess; -import com.jsoniter.DecodingMode; -import com.jsoniter.JsonIterator; -import com.jsoniter.output.EncodingMode; -import com.jsoniter.output.JsonStream; -import com.jsoniter.spi.TypeLiteral; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/* -Benchmark Mode Cnt Score Error Units -BenchJsoniter.deser thrpt 5 7886115.451 ± 335769.969 ops/s (2.23x) -BenchJsoniter.ser thrpt 5 13216858.758 ± 611385.896 ops/s (2.46x) - */ -@State(Scope.Thread) -public class BenchJsoniter { - - private TestObject testObject; - private JsonStream stream; - private ByteArrayOutputStream byteArrayOutputStream; - private byte[] testJSON; - private JsonIterator iter; - private TypeLiteral typeLiteral; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) { - JsonStream.setMode(EncodingMode.DYNAMIC_MODE); - JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); - testObject = TestObject.createTestObject(); - testJSON = TestObject.createTestJSON(); - stream = new JsonStream(null, 512); - byteArrayOutputStream = new ByteArrayOutputStream(); - iter = new JsonIterator(); - typeLiteral = TypeLiteral.create(TestObject.class); - } - - @Benchmark - public void ser(Blackhole bh) throws IOException { - byteArrayOutputStream.reset(); - stream.reset(byteArrayOutputStream); - stream.writeVal(testObject); - bh.consume(byteArrayOutputStream); - } - - @Benchmark - public void deser(Blackhole bh) throws IOException { - iter.reset(testJSON); - bh.consume(iter.read(typeLiteral)); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_5_fields.BenchJsoniter", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/BenchThrift.java b/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/BenchThrift.java deleted file mode 100644 index cd6b2d90..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/BenchThrift.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.jsoniter.demo.object_with_5_fields; - -import org.apache.thrift.TDeserializer; -import org.apache.thrift.TException; -import org.apache.thrift.TSerializer; -import org.apache.thrift.protocol.TBinaryProtocol; -import org.apache.thrift.protocol.TCompactProtocol; -import org.apache.thrift.protocol.TTupleProtocol; -import org.junit.Test; -import org.openjdk.jmh.Main; -import org.openjdk.jmh.annotations.*; -import org.openjdk.jmh.infra.BenchmarkParams; -import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.RunnerException; - -import java.io.IOException; - -/* -Tuple -Benchmark Mode Cnt Score Error Units -BenchThrift.deser thrpt 5 4894731.174 ± 190486.954 ops/s (1.38x) -BenchThrift.ser thrpt 5 2537935.619 ± 132875.762 ops/s (0.47x) - -Compact -Benchmark Mode Cnt Score Error Units -BenchThrift.deser thrpt 5 4490620.091 ± 118728.895 ops/s -BenchThrift.ser thrpt 5 2114218.709 ± 66750.207 ops/s - -Binary -Benchmark Mode Cnt Score Error Units -BenchThrift.deser thrpt 5 4463916.092 ± 74085.264 ops/s -BenchThrift.ser thrpt 5 1780672.495 ± 21550.292 ops/s - */ -@State(Scope.Thread) -public class BenchThrift { - - private TSerializer serializer; - private ThriftTestObject testObject; - private TDeserializer deserializer; - private byte[] testData; - - @Setup(Level.Trial) - public void benchSetup(BenchmarkParams params) throws TException { - testObject = new ThriftTestObject(); - testObject.field1 = "field1"; - testObject.field2 = "field2"; - testObject.field3 = "field3"; - testObject.field4 = "field4"; - testObject.field5 = "field5"; -// serializer = new TSerializer(new TTupleProtocol.Factory()); - serializer = new TSerializer(new TCompactProtocol.Factory()); -// serializer = new TSerializer(new TBinaryProtocol.Factory()); -// deserializer = new TDeserializer(new TTupleProtocol.Factory()); - deserializer = new TDeserializer(new TCompactProtocol.Factory()); -// deserializer = new TDeserializer(new TBinaryProtocol.Factory()); - testData = serializer.serialize(testObject); - } - - @Test - public void test() throws TException { - byte[] output = new TSerializer(new TCompactProtocol.Factory()).serialize(testObject); - System.out.println(output.length); - } - - @Benchmark - public void ser(Blackhole bh) throws TException { - bh.consume(serializer.serialize(testObject)); - } - - @Benchmark - public void deser(Blackhole bh) throws TException { - ThriftTestObject obj = new ThriftTestObject(); - deserializer.deserialize(testObject, testData); - bh.consume(obj); - } - - public static void main(String[] args) throws IOException, RunnerException { - Main.main(new String[]{ - "object_with_5_fields.BenchThrift", - "-i", "5", - "-wi", "5", - "-f", "1", - }); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/TestObect.proto b/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/TestObect.proto deleted file mode 100644 index 4ed13dbf..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/TestObect.proto +++ /dev/null @@ -1,8 +0,0 @@ -syntax = "proto3"; -message TestObject { - string field1 = 1; - string field2 = 2; - string field3 = 3; - string field4 = 4; - string field5 = 5; -} \ No newline at end of file diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/TestObject.java b/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/TestObject.java deleted file mode 100644 index b5569cdf..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/TestObject.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.jsoniter.demo.object_with_5_fields; - -import com.dslplatform.json.CompiledJson; -import com.jsoniter.output.JsonStream; - -@CompiledJson -public class TestObject { - - public String field1; - public String field2; - public String field3; - public String field4; - public String field5; - - public static TestObject createTestObject() { - TestObject testObject = new TestObject(); - testObject.field1 = "field1"; - testObject.field2 = "field2"; - testObject.field3 = "field3"; - testObject.field4 = "field4"; - testObject.field5 = "field5"; - return testObject; - } - - public static byte[] createTestJSON() { - return JsonStream.serialize(createTestObject()).getBytes(); - } -} diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/TestObject.thrift b/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/TestObject.thrift deleted file mode 100644 index 42ba8029..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/TestObject.thrift +++ /dev/null @@ -1,8 +0,0 @@ -struct ThriftTestObject -{ - 1: string field1 - 2: string field2 - 3: string field3 - 4: string field4 - 5: string field5 -} \ No newline at end of file diff --git a/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/ThriftTestObject.java b/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/ThriftTestObject.java deleted file mode 100644 index f0c7437c..00000000 --- a/demo/src/test/java/com/jsoniter/demo/object_with_5_fields/ThriftTestObject.java +++ /dev/null @@ -1,786 +0,0 @@ -package com.jsoniter.demo.object_with_5_fields; /** - * Autogenerated by Thrift Compiler (0.9.1) - * - * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - * @generated - */ -import org.apache.thrift.scheme.IScheme; -import org.apache.thrift.scheme.SchemeFactory; -import org.apache.thrift.scheme.StandardScheme; - -import org.apache.thrift.scheme.TupleScheme; -import org.apache.thrift.protocol.TTupleProtocol; -import org.apache.thrift.protocol.TProtocolException; -import org.apache.thrift.EncodingUtils; -import org.apache.thrift.TException; -import org.apache.thrift.async.AsyncMethodCallback; -import org.apache.thrift.server.AbstractNonblockingServer.*; -import java.util.List; -import java.util.ArrayList; -import java.util.Map; -import java.util.HashMap; -import java.util.EnumMap; -import java.util.Set; -import java.util.HashSet; -import java.util.EnumSet; -import java.util.Collections; -import java.util.BitSet; -import java.nio.ByteBuffer; -import java.util.Arrays; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ThriftTestObject implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { - private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("ThriftTestObject"); - - private static final org.apache.thrift.protocol.TField FIELD1_FIELD_DESC = new org.apache.thrift.protocol.TField("field1", org.apache.thrift.protocol.TType.STRING, (short)1); - private static final org.apache.thrift.protocol.TField FIELD2_FIELD_DESC = new org.apache.thrift.protocol.TField("field2", org.apache.thrift.protocol.TType.STRING, (short)2); - private static final org.apache.thrift.protocol.TField FIELD3_FIELD_DESC = new org.apache.thrift.protocol.TField("field3", org.apache.thrift.protocol.TType.STRING, (short)3); - private static final org.apache.thrift.protocol.TField FIELD4_FIELD_DESC = new org.apache.thrift.protocol.TField("field4", org.apache.thrift.protocol.TType.STRING, (short)4); - private static final org.apache.thrift.protocol.TField FIELD5_FIELD_DESC = new org.apache.thrift.protocol.TField("field5", org.apache.thrift.protocol.TType.STRING, (short)5); - - private static final Map, SchemeFactory> schemes = new HashMap, SchemeFactory>(); - static { - schemes.put(StandardScheme.class, new ThriftTestObjectStandardSchemeFactory()); - schemes.put(TupleScheme.class, new ThriftTestObjectTupleSchemeFactory()); - } - - public String field1; // required - public String field2; // required - public String field3; // required - public String field4; // required - public String field5; // required - - /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ - public enum _Fields implements org.apache.thrift.TFieldIdEnum { - FIELD1((short)1, "field1"), - FIELD2((short)2, "field2"), - FIELD3((short)3, "field3"), - FIELD4((short)4, "field4"), - FIELD5((short)5, "field5"); - - private static final Map byName = new HashMap(); - - static { - for (_Fields field : EnumSet.allOf(_Fields.class)) { - byName.put(field.getFieldName(), field); - } - } - - /** - * Find the _Fields constant that matches fieldId, or null if its not found. - */ - public static _Fields findByThriftId(int fieldId) { - switch(fieldId) { - case 1: // FIELD1 - return FIELD1; - case 2: // FIELD2 - return FIELD2; - case 3: // FIELD3 - return FIELD3; - case 4: // FIELD4 - return FIELD4; - case 5: // FIELD5 - return FIELD5; - default: - return null; - } - } - - /** - * Find the _Fields constant that matches fieldId, throwing an exception - * if it is not found. - */ - public static _Fields findByThriftIdOrThrow(int fieldId) { - _Fields fields = findByThriftId(fieldId); - if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!"); - return fields; - } - - /** - * Find the _Fields constant that matches name, or null if its not found. - */ - public static _Fields findByName(String name) { - return byName.get(name); - } - - private final short _thriftId; - private final String _fieldName; - - _Fields(short thriftId, String fieldName) { - _thriftId = thriftId; - _fieldName = fieldName; - } - - public short getThriftFieldId() { - return _thriftId; - } - - public String getFieldName() { - return _fieldName; - } - } - - // isset id assignments - public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; - static { - Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); - tmpMap.put(_Fields.FIELD1, new org.apache.thrift.meta_data.FieldMetaData("field1", org.apache.thrift.TFieldRequirementType.DEFAULT, - new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); - tmpMap.put(_Fields.FIELD2, new org.apache.thrift.meta_data.FieldMetaData("field2", org.apache.thrift.TFieldRequirementType.DEFAULT, - new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); - tmpMap.put(_Fields.FIELD3, new org.apache.thrift.meta_data.FieldMetaData("field3", org.apache.thrift.TFieldRequirementType.DEFAULT, - new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); - tmpMap.put(_Fields.FIELD4, new org.apache.thrift.meta_data.FieldMetaData("field4", org.apache.thrift.TFieldRequirementType.DEFAULT, - new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); - tmpMap.put(_Fields.FIELD5, new org.apache.thrift.meta_data.FieldMetaData("field5", org.apache.thrift.TFieldRequirementType.DEFAULT, - new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); - metaDataMap = Collections.unmodifiableMap(tmpMap); - org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(ThriftTestObject.class, metaDataMap); - } - - public ThriftTestObject() { - } - - public ThriftTestObject( - String field1, - String field2, - String field3, - String field4, - String field5) - { - this(); - this.field1 = field1; - this.field2 = field2; - this.field3 = field3; - this.field4 = field4; - this.field5 = field5; - } - - /** - * Performs a deep copy on other. - */ - public ThriftTestObject(ThriftTestObject other) { - if (other.isSetField1()) { - this.field1 = other.field1; - } - if (other.isSetField2()) { - this.field2 = other.field2; - } - if (other.isSetField3()) { - this.field3 = other.field3; - } - if (other.isSetField4()) { - this.field4 = other.field4; - } - if (other.isSetField5()) { - this.field5 = other.field5; - } - } - - public ThriftTestObject deepCopy() { - return new ThriftTestObject(this); - } - - @Override - public void clear() { - this.field1 = null; - this.field2 = null; - this.field3 = null; - this.field4 = null; - this.field5 = null; - } - - public String getField1() { - return this.field1; - } - - public ThriftTestObject setField1(String field1) { - this.field1 = field1; - return this; - } - - public void unsetField1() { - this.field1 = null; - } - - /** Returns true if field field1 is set (has been assigned a value) and false otherwise */ - public boolean isSetField1() { - return this.field1 != null; - } - - public void setField1IsSet(boolean value) { - if (!value) { - this.field1 = null; - } - } - - public String getField2() { - return this.field2; - } - - public ThriftTestObject setField2(String field2) { - this.field2 = field2; - return this; - } - - public void unsetField2() { - this.field2 = null; - } - - /** Returns true if field field2 is set (has been assigned a value) and false otherwise */ - public boolean isSetField2() { - return this.field2 != null; - } - - public void setField2IsSet(boolean value) { - if (!value) { - this.field2 = null; - } - } - - public String getField3() { - return this.field3; - } - - public ThriftTestObject setField3(String field3) { - this.field3 = field3; - return this; - } - - public void unsetField3() { - this.field3 = null; - } - - /** Returns true if field field3 is set (has been assigned a value) and false otherwise */ - public boolean isSetField3() { - return this.field3 != null; - } - - public void setField3IsSet(boolean value) { - if (!value) { - this.field3 = null; - } - } - - public String getField4() { - return this.field4; - } - - public ThriftTestObject setField4(String field4) { - this.field4 = field4; - return this; - } - - public void unsetField4() { - this.field4 = null; - } - - /** Returns true if field field4 is set (has been assigned a value) and false otherwise */ - public boolean isSetField4() { - return this.field4 != null; - } - - public void setField4IsSet(boolean value) { - if (!value) { - this.field4 = null; - } - } - - public String getField5() { - return this.field5; - } - - public ThriftTestObject setField5(String field5) { - this.field5 = field5; - return this; - } - - public void unsetField5() { - this.field5 = null; - } - - /** Returns true if field field5 is set (has been assigned a value) and false otherwise */ - public boolean isSetField5() { - return this.field5 != null; - } - - public void setField5IsSet(boolean value) { - if (!value) { - this.field5 = null; - } - } - - public void setFieldValue(_Fields field, Object value) { - switch (field) { - case FIELD1: - if (value == null) { - unsetField1(); - } else { - setField1((String)value); - } - break; - - case FIELD2: - if (value == null) { - unsetField2(); - } else { - setField2((String)value); - } - break; - - case FIELD3: - if (value == null) { - unsetField3(); - } else { - setField3((String)value); - } - break; - - case FIELD4: - if (value == null) { - unsetField4(); - } else { - setField4((String)value); - } - break; - - case FIELD5: - if (value == null) { - unsetField5(); - } else { - setField5((String)value); - } - break; - - } - } - - public Object getFieldValue(_Fields field) { - switch (field) { - case FIELD1: - return getField1(); - - case FIELD2: - return getField2(); - - case FIELD3: - return getField3(); - - case FIELD4: - return getField4(); - - case FIELD5: - return getField5(); - - } - throw new IllegalStateException(); - } - - /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ - public boolean isSet(_Fields field) { - if (field == null) { - throw new IllegalArgumentException(); - } - - switch (field) { - case FIELD1: - return isSetField1(); - case FIELD2: - return isSetField2(); - case FIELD3: - return isSetField3(); - case FIELD4: - return isSetField4(); - case FIELD5: - return isSetField5(); - } - throw new IllegalStateException(); - } - - @Override - public boolean equals(Object that) { - if (that == null) - return false; - if (that instanceof ThriftTestObject) - return this.equals((ThriftTestObject)that); - return false; - } - - public boolean equals(ThriftTestObject that) { - if (that == null) - return false; - - boolean this_present_field1 = true && this.isSetField1(); - boolean that_present_field1 = true && that.isSetField1(); - if (this_present_field1 || that_present_field1) { - if (!(this_present_field1 && that_present_field1)) - return false; - if (!this.field1.equals(that.field1)) - return false; - } - - boolean this_present_field2 = true && this.isSetField2(); - boolean that_present_field2 = true && that.isSetField2(); - if (this_present_field2 || that_present_field2) { - if (!(this_present_field2 && that_present_field2)) - return false; - if (!this.field2.equals(that.field2)) - return false; - } - - boolean this_present_field3 = true && this.isSetField3(); - boolean that_present_field3 = true && that.isSetField3(); - if (this_present_field3 || that_present_field3) { - if (!(this_present_field3 && that_present_field3)) - return false; - if (!this.field3.equals(that.field3)) - return false; - } - - boolean this_present_field4 = true && this.isSetField4(); - boolean that_present_field4 = true && that.isSetField4(); - if (this_present_field4 || that_present_field4) { - if (!(this_present_field4 && that_present_field4)) - return false; - if (!this.field4.equals(that.field4)) - return false; - } - - boolean this_present_field5 = true && this.isSetField5(); - boolean that_present_field5 = true && that.isSetField5(); - if (this_present_field5 || that_present_field5) { - if (!(this_present_field5 && that_present_field5)) - return false; - if (!this.field5.equals(that.field5)) - return false; - } - - return true; - } - - @Override - public int hashCode() { - return 0; - } - - @Override - public int compareTo(ThriftTestObject other) { - if (!getClass().equals(other.getClass())) { - return getClass().getName().compareTo(other.getClass().getName()); - } - - int lastComparison = 0; - - lastComparison = Boolean.valueOf(isSetField1()).compareTo(other.isSetField1()); - if (lastComparison != 0) { - return lastComparison; - } - if (isSetField1()) { - lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.field1, other.field1); - if (lastComparison != 0) { - return lastComparison; - } - } - lastComparison = Boolean.valueOf(isSetField2()).compareTo(other.isSetField2()); - if (lastComparison != 0) { - return lastComparison; - } - if (isSetField2()) { - lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.field2, other.field2); - if (lastComparison != 0) { - return lastComparison; - } - } - lastComparison = Boolean.valueOf(isSetField3()).compareTo(other.isSetField3()); - if (lastComparison != 0) { - return lastComparison; - } - if (isSetField3()) { - lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.field3, other.field3); - if (lastComparison != 0) { - return lastComparison; - } - } - lastComparison = Boolean.valueOf(isSetField4()).compareTo(other.isSetField4()); - if (lastComparison != 0) { - return lastComparison; - } - if (isSetField4()) { - lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.field4, other.field4); - if (lastComparison != 0) { - return lastComparison; - } - } - lastComparison = Boolean.valueOf(isSetField5()).compareTo(other.isSetField5()); - if (lastComparison != 0) { - return lastComparison; - } - if (isSetField5()) { - lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.field5, other.field5); - if (lastComparison != 0) { - return lastComparison; - } - } - return 0; - } - - public _Fields fieldForId(int fieldId) { - return _Fields.findByThriftId(fieldId); - } - - public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { - schemes.get(iprot.getScheme()).getScheme().read(iprot, this); - } - - public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { - schemes.get(oprot.getScheme()).getScheme().write(oprot, this); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("ThriftTestObject("); - boolean first = true; - - sb.append("field1:"); - if (this.field1 == null) { - sb.append("null"); - } else { - sb.append(this.field1); - } - first = false; - if (!first) sb.append(", "); - sb.append("field2:"); - if (this.field2 == null) { - sb.append("null"); - } else { - sb.append(this.field2); - } - first = false; - if (!first) sb.append(", "); - sb.append("field3:"); - if (this.field3 == null) { - sb.append("null"); - } else { - sb.append(this.field3); - } - first = false; - if (!first) sb.append(", "); - sb.append("field4:"); - if (this.field4 == null) { - sb.append("null"); - } else { - sb.append(this.field4); - } - first = false; - if (!first) sb.append(", "); - sb.append("field5:"); - if (this.field5 == null) { - sb.append("null"); - } else { - sb.append(this.field5); - } - first = false; - sb.append(")"); - return sb.toString(); - } - - public void validate() throws org.apache.thrift.TException { - // check for required fields - // check for sub-struct validity - } - - private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { - try { - write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); - } catch (org.apache.thrift.TException te) { - throw new java.io.IOException(te); - } - } - - private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { - try { - read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); - } catch (org.apache.thrift.TException te) { - throw new java.io.IOException(te); - } - } - - private static class ThriftTestObjectStandardSchemeFactory implements SchemeFactory { - public ThriftTestObjectStandardScheme getScheme() { - return new ThriftTestObjectStandardScheme(); - } - } - - private static class ThriftTestObjectStandardScheme extends StandardScheme { - - public void read(org.apache.thrift.protocol.TProtocol iprot, ThriftTestObject struct) throws org.apache.thrift.TException { - org.apache.thrift.protocol.TField schemeField; - iprot.readStructBegin(); - while (true) - { - schemeField = iprot.readFieldBegin(); - if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { - break; - } - switch (schemeField.id) { - case 1: // FIELD1 - if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { - struct.field1 = iprot.readString(); - struct.setField1IsSet(true); - } else { - org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); - } - break; - case 2: // FIELD2 - if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { - struct.field2 = iprot.readString(); - struct.setField2IsSet(true); - } else { - org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); - } - break; - case 3: // FIELD3 - if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { - struct.field3 = iprot.readString(); - struct.setField3IsSet(true); - } else { - org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); - } - break; - case 4: // FIELD4 - if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { - struct.field4 = iprot.readString(); - struct.setField4IsSet(true); - } else { - org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); - } - break; - case 5: // FIELD5 - if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { - struct.field5 = iprot.readString(); - struct.setField5IsSet(true); - } else { - org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); - } - break; - default: - org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); - } - iprot.readFieldEnd(); - } - iprot.readStructEnd(); - - // check for required fields of primitive type, which can't be checked in the validate method - struct.validate(); - } - - public void write(org.apache.thrift.protocol.TProtocol oprot, ThriftTestObject struct) throws org.apache.thrift.TException { - struct.validate(); - - oprot.writeStructBegin(STRUCT_DESC); - if (struct.field1 != null) { - oprot.writeFieldBegin(FIELD1_FIELD_DESC); - oprot.writeString(struct.field1); - oprot.writeFieldEnd(); - } - if (struct.field2 != null) { - oprot.writeFieldBegin(FIELD2_FIELD_DESC); - oprot.writeString(struct.field2); - oprot.writeFieldEnd(); - } - if (struct.field3 != null) { - oprot.writeFieldBegin(FIELD3_FIELD_DESC); - oprot.writeString(struct.field3); - oprot.writeFieldEnd(); - } - if (struct.field4 != null) { - oprot.writeFieldBegin(FIELD4_FIELD_DESC); - oprot.writeString(struct.field4); - oprot.writeFieldEnd(); - } - if (struct.field5 != null) { - oprot.writeFieldBegin(FIELD5_FIELD_DESC); - oprot.writeString(struct.field5); - oprot.writeFieldEnd(); - } - oprot.writeFieldStop(); - oprot.writeStructEnd(); - } - - } - - private static class ThriftTestObjectTupleSchemeFactory implements SchemeFactory { - public ThriftTestObjectTupleScheme getScheme() { - return new ThriftTestObjectTupleScheme(); - } - } - - private static class ThriftTestObjectTupleScheme extends TupleScheme { - - @Override - public void write(org.apache.thrift.protocol.TProtocol prot, ThriftTestObject struct) throws org.apache.thrift.TException { - TTupleProtocol oprot = (TTupleProtocol) prot; - BitSet optionals = new BitSet(); - if (struct.isSetField1()) { - optionals.set(0); - } - if (struct.isSetField2()) { - optionals.set(1); - } - if (struct.isSetField3()) { - optionals.set(2); - } - if (struct.isSetField4()) { - optionals.set(3); - } - if (struct.isSetField5()) { - optionals.set(4); - } - oprot.writeBitSet(optionals, 5); - if (struct.isSetField1()) { - oprot.writeString(struct.field1); - } - if (struct.isSetField2()) { - oprot.writeString(struct.field2); - } - if (struct.isSetField3()) { - oprot.writeString(struct.field3); - } - if (struct.isSetField4()) { - oprot.writeString(struct.field4); - } - if (struct.isSetField5()) { - oprot.writeString(struct.field5); - } - } - - @Override - public void read(org.apache.thrift.protocol.TProtocol prot, ThriftTestObject struct) throws org.apache.thrift.TException { - TTupleProtocol iprot = (TTupleProtocol) prot; - BitSet incoming = iprot.readBitSet(5); - if (incoming.get(0)) { - struct.field1 = iprot.readString(); - struct.setField1IsSet(true); - } - if (incoming.get(1)) { - struct.field2 = iprot.readString(); - struct.setField2IsSet(true); - } - if (incoming.get(2)) { - struct.field3 = iprot.readString(); - struct.setField3IsSet(true); - } - if (incoming.get(3)) { - struct.field4 = iprot.readString(); - struct.setField4IsSet(true); - } - if (incoming.get(4)) { - struct.field5 = iprot.readString(); - struct.setField5IsSet(true); - } - } - } - -} - diff --git a/demo/src/test/resources/large.json b/demo/src/test/resources/large.json deleted file mode 100644 index 065d8c8b..00000000 --- a/demo/src/test/resources/large.json +++ /dev/null @@ -1,227 +0,0 @@ -[ - { - "friends": [ - { - "id": 0, - "name": "Briana Gardner" - }, - { - "id": 1, - "name": "Ratliff Byrd" - }, - { - "id": 2, - "name": "Marks Bell" - } - ], - "_id": "58659f97246aa21396247468", - "index": 0, - "guid": "73c7235f-121e-4871-89c2-97c986d2dc6c", - "isActive": true, - "balance": "$1,246.00", - "picture": "http://placehold.it/32x32", - "age": 35, - "eyeColor": "blue", - "name": "Winters Sharp", - "gender": "male", - "company": "POLARIA", - "email": "winterssharp@polaria.com", - "phone": "+1 (878) 493-3899", - "address": "449 Troutman Street, Rockingham, Puerto Rico, 8953", - "about": "Adipisicing eiusmod est amet pariatur velit laborum ea anim enim deserunt. Velit et do duis reprehenderit do Lorem do. Ipsum fugiat id commodo aliqua nulla. Laborum voluptate officia eiusmod dolor laborum labore nisi velit laboris aliqua ut labore adipisicing. Cupidatat tempor aute commodo fugiat nostrud nulla voluptate culpa anim do adipisicing sunt et.\r\n", - "registered": "2016-05-13T03:23:52 -08:00", - "latitude": -86.869083, - "longitude": -42.636359, - "tags": [ - "pariatur", - "dolor", - "exercitation", - "non", - "irure", - "sint", - "cupidatat" - ], - "greeting": "Hello, Winters Sharp! You have 7 unread messages.", - "favoriteFruit": "banana" - }, - { - "friends": [ - { - "id": 0, - "name": "Patti Bird" - }, - { - "id": 1, - "name": "Vinson Golden" - }, - { - "id": 2, - "name": "Jessie Gallegos" - } - ], - "_id": "58659f97fda793c4f321294b", - "index": 1, - "guid": "a1d948c6-a635-40d1-a4cc-75d8c54d90b7", - "isActive": true, - "balance": "$2,954.82", - "picture": "http://placehold.it/32x32", - "age": 23, - "eyeColor": "green", - "name": "Ann Reilly", - "gender": "female", - "company": "ACCUPHARM", - "email": "annreilly@accupharm.com", - "phone": "+1 (879) 560-2394", - "address": "906 Johnson Avenue, Bagtown, Georgia, 6009", - "about": "Commodo dolor in magna occaecat nostrud cillum tempor magna exercitation aliqua. Lorem qui voluptate ut nisi irure laborum. Quis sunt sint eu quis mollit aliquip sunt nostrud deserunt ipsum dolore exercitation esse tempor. Dolore tempor magna fugiat officia culpa qui minim deserunt incididunt duis. Occaecat consectetur commodo et fugiat tempor enim laborum. Amet pariatur occaecat est pariatur ut.\r\n", - "registered": "2015-10-06T10:11:21 -08:00", - "latitude": 28.347215, - "longitude": 119.881559, - "tags": [ - "excepteur", - "sunt", - "adipisicing", - "excepteur", - "qui", - "quis", - "veniam" - ], - "greeting": "Hello, Ann Reilly! You have 8 unread messages.", - "favoriteFruit": "banana" - }, - { - "friends": [ - { - "id": 0, - "name": "Nichols Wooten" - }, - { - "id": 1, - "name": "Catalina Welch" - }, - { - "id": 2, - "name": "Stevens Vazquez" - } - ], - "_id": "58659f97a722760b7fb775bd", - "index": 2, - "guid": "f75031dc-f80f-4144-888d-9fb78c50c218", - "isActive": false, - "balance": "$3,578.66", - "picture": "http://placehold.it/32x32", - "age": 32, - "eyeColor": "brown", - "name": "Doreen Rowland", - "gender": "female", - "company": "COMTRACT", - "email": "doreenrowland@comtract.com", - "phone": "+1 (874) 444-2553", - "address": "554 Voorhies Avenue, Odessa, Northern Mariana Islands, 4525", - "about": "Qui consequat adipisicing ea non aute eu magna veniam nostrud. Aliqua sint id eiusmod sint eu irure occaecat sit Lorem eu. Aliquip nostrud pariatur cupidatat minim pariatur ullamco dolore deserunt fugiat. Culpa laboris anim exercitation quis eu. Ea aute officia irure laborum sint amet quis labore aliqua ex exercitation culpa quis. Proident amet aliquip esse eiusmod sit. Veniam aute ea non cillum eiusmod deserunt magna commodo incididunt elit sint ut.\r\n", - "registered": "2014-12-31T05:58:46 -08:00", - "latitude": -79.177177, - "longitude": -106.982224, - "tags": [ - "voluptate", - "deserunt", - "adipisicing", - "proident", - "exercitation", - "proident", - "est" - ], - "greeting": "Hello, Doreen Rowland! You have 4 unread messages.", - "favoriteFruit": "apple" - }, - { - "friends": [ - { - "id": 0, - "name": "Bryant Beard" - }, - { - "id": 1, - "name": "Josephine Glover" - }, - { - "id": 2, - "name": "Socorro Koch" - } - ], - "_id": "58659f9779501e6cd634508b", - "index": 3, - "guid": "9e52955b-2365-4136-b972-39e72b01b0b6", - "isActive": false, - "balance": "$1,612.42", - "picture": "http://placehold.it/32x32", - "age": 30, - "eyeColor": "green", - "name": "Leach Peck", - "gender": "male", - "company": "SLOFAST", - "email": "leachpeck@slofast.com", - "phone": "+1 (889) 464-3474", - "address": "659 Carroll Street, Balm, New Jersey, 883", - "about": "Nisi officia esse eiusmod exercitation. Fugiat sunt labore Lorem nisi aliqua deserunt fugiat nisi nisi tempor duis. Consequat nostrud anim nisi commodo eiusmod sit ex do occaecat dolor consequat incididunt Lorem duis. Est dolore elit adipisicing laborum nostrud sit labore aliqua officia quis amet ut laboris. Quis anim incididunt exercitation tempor consectetur sunt cupidatat exercitation veniam. Consequat Lorem in eu duis ut incididunt excepteur id qui anim id consectetur commodo.\r\n", - "registered": "2014-03-04T02:41:37 -08:00", - "latitude": 55.648581, - "longitude": 152.684842, - "tags": [ - "laborum", - "ex", - "reprehenderit", - "eu", - "esse", - "officia", - "Lorem" - ], - "greeting": "Hello, Leach Peck! You have 2 unread messages.", - "favoriteFruit": "banana" - }, - { - "_id": "58659f976f045f9c69c53efb", - "index": 4, - "guid": "3cbaef3d-25ab-48d0-8807-1974f6aad336", - "isActive": true, - "balance": "$1,854.63", - "picture": "http://placehold.it/32x32", - "age": 26, - "eyeColor": "brown", - "name": "Briggs Larson", - "gender": "male", - "company": "ZOXY", - "email": "briggslarson@zoxy.com", - "phone": "+1 (807) 588-3350", - "address": "994 Nichols Avenue, Allamuchy, Guam, 3824", - "about": "Sunt velit ullamco consequat velit ad nisi in sint qui qui ut eiusmod eu. Et ut aliqua mollit cupidatat et proident tempor do est enim exercitation amet aliquip. Non exercitation proident do duis non ullamco do esse dolore in occaecat. Magna ea labore aliqua laborum ad amet est incididunt et quis cillum nulla. Adipisicing veniam nisi esse officia dolor labore. Proident fugiat consequat ullamco fugiat. Est et adipisicing eiusmod excepteur deserunt pariatur aute commodo dolore occaecat veniam dolore.\r\n", - "registered": "2014-07-21T03:28:39 -08:00", - "latitude": -59.741245, - "longitude": -9.657004, - "friends": [ - { - "id": 0, - "name": "Herminia Mcknight" - }, - { - "id": 1, - "name": "Leann Harding" - }, - { - "id": 2, - "name": "Marisol Sykes" - } - ], - "tags": [ - "ea", - "velit", - "sunt", - "fugiat", - "do", - "Lorem", - "nostrud" - ], - "greeting": "Hello, Briggs Larson! You have 3 unread messages.", - "favoriteFruit": "apple" - } -] \ No newline at end of file diff --git a/pom.xml b/pom.xml index 9513c7d8..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.9 + 0.9.24-SNAPSHOT jsoniter json iterator jsoniter (json-iterator) is fast and flexible JSON parser available in Java and Go @@ -46,15 +46,40 @@ 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.9.5 + true + + + com.google.code.gson + gson + 2.8.3 + true + + + + org.openjdk.jmh + jmh-core + 1.20 + test + + + org.openjdk.jmh + jmh-generator-annprocess + 1.20 + test + @@ -79,10 +104,22 @@ + + org.codehaus.mojo + cobertura-maven-plugin + 2.7 + + + html + xml + + + + org.apache.maven.plugins maven-compiler-plugin - 3.6.0 + 3.7.0 1.6 1.6 @@ -92,7 +129,7 @@ org.apache.maven.plugins maven-source-plugin - 2.2.1 + 3.0.1 attach-sources @@ -105,7 +142,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.9.1 + 3.0.0 attach-javadocs @@ -121,7 +158,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.5 + 1.6 sign-artifacts @@ -135,7 +172,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 + 1.6.8 true ossrh @@ -157,7 +194,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.19.1 + 2.21.0 methods 1 @@ -181,4 +218,4 @@ https://oss.sonatype.org/content/repositories/snapshots - \ No newline at end of file + diff --git a/src/main/java/com/jsoniter/Codegen.java b/src/main/java/com/jsoniter/Codegen.java index d4b1a182..7cf7318d 100644 --- a/src/main/java/com/jsoniter/Codegen.java +++ b/src/main/java/com/jsoniter/Codegen.java @@ -8,24 +8,14 @@ import java.io.OutputStreamWriter; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; import java.util.*; class Codegen { // only read/write when generating code with synchronized protection private final static Set generatedClassNames = new HashSet(); - static boolean isDoingStaticCodegen = false; - static DecodingMode mode = DecodingMode.REFLECTION_MODE; - static { - String envMode = System.getenv("JSONITER_DECODING_MODE"); - if (envMode != null) { - mode = DecodingMode.valueOf(envMode); - } - } - - public static void setMode(DecodingMode mode) { - Codegen.mode = mode; - } + static CodegenAccess.StaticCodegenTarget isDoingStaticCodegen = null; static Decoder getDecoder(String cacheKey, Type type) { Decoder decoder = JsoniterSpi.getDecoder(cacheKey); @@ -52,58 +42,77 @@ private synchronized static Decoder gen(String cacheKey, Type type) { return decoder; } } - Type[] typeArgs = new Type[0]; - Class clazz; - if (type instanceof ParameterizedType) { - ParameterizedType pType = (ParameterizedType) type; - clazz = (Class) pType.getRawType(); - typeArgs = pType.getActualTypeArguments(); - } else { - clazz = (Class) type; - } - decoder = CodegenImplNative.NATIVE_DECODERS.get(clazz); + ClassInfo classInfo = new ClassInfo(type); + decoder = CodegenImplNative.NATIVE_DECODERS.get(classInfo.clazz); if (decoder != null) { return decoder; } - if (mode == DecodingMode.REFLECTION_MODE) { - decoder = ReflectionDecoderFactory.create(clazz, typeArgs); - JsoniterSpi.addNewDecoder(cacheKey, decoder); - return decoder; - } - if (!isDoingStaticCodegen) { - try { - decoder = (Decoder) Class.forName(cacheKey).newInstance(); - JsoniterSpi.addNewDecoder(cacheKey, decoder); + addPlaceholderDecoderToSupportRecursiveStructure(cacheKey); + try { + Config currentConfig = JsoniterSpi.getCurrentConfig(); + DecodingMode mode = currentConfig.decodingMode(); + if (mode == DecodingMode.REFLECTION_MODE) { + decoder = ReflectionDecoderFactory.create(classInfo); return decoder; - } catch (Exception e) { - if (mode == DecodingMode.STATIC_MODE) { - throw new JsonException("static gen should provide the decoder we need, but failed to create the decoder", e); + } + if (isDoingStaticCodegen == null) { + try { + decoder = (Decoder) Class.forName(cacheKey).newInstance(); + return decoder; + } catch (Exception e) { + if (mode == DecodingMode.STATIC_MODE) { + throw new JsonException("static gen should provide the decoder we need, but failed to create the decoder", e); + } } } - } - String source = genSource(clazz, typeArgs); - source = "public static java.lang.Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { " - + source + "}"; - if ("true".equals(System.getenv("JSONITER_DEBUG"))) { - System.out.println(">>> " + cacheKey); - System.out.println(source); - } - try { - generatedClassNames.add(cacheKey); - if (isDoingStaticCodegen) { - staticGen(cacheKey, source); - } else { - decoder = DynamicCodegen.gen(cacheKey, source); + String source = genSource(mode, classInfo); + source = "public static java.lang.Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { " + + source + "}"; + if ("true".equals(System.getenv("JSONITER_DEBUG"))) { + System.out.println(">>> " + cacheKey); + System.out.println(source); } + try { + generatedClassNames.add(cacheKey); + if (isDoingStaticCodegen == null) { + decoder = DynamicCodegen.gen(cacheKey, source); + } else { + staticGen(cacheKey, source); + } + return decoder; + } catch (Exception e) { + String msg = "failed to generate decoder for: " + classInfo + " with " + Arrays.toString(classInfo.typeArgs) + ", exception: " + e; + msg = msg + "\n" + source; + throw new JsonException(msg, e); + } + } finally { JsoniterSpi.addNewDecoder(cacheKey, decoder); - return decoder; - } catch (Exception e) { - String msg = "failed to generate decoder for: " + type + " with " + Arrays.toString(typeArgs) + ", exception: " + e; - msg = msg + "\n" + source; - throw new JsonException(msg, e); } } + private static void addPlaceholderDecoderToSupportRecursiveStructure(final String cacheKey) { + JsoniterSpi.addNewDecoder(cacheKey, new Decoder() { + @Override + public Object decode(JsonIterator iter) throws IOException { + Decoder decoder = JsoniterSpi.getDecoder(cacheKey); + if (this == decoder) { + for(int i = 0; (i < 30) && (this == decoder); i++) { + decoder = JsoniterSpi.getDecoder(cacheKey); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new JsonException(e); + } + } + if (this == decoder) { + throw new JsonException("internal error: placeholder is not replaced with real decoder"); + } + } + return decoder.decode(iter); + } + }); + } + public static boolean canStaticAccess(String cacheKey) { return generatedClassNames.contains(cacheKey); } @@ -115,6 +124,8 @@ private static Type chooseImpl(Type type) { ParameterizedType pType = (ParameterizedType) type; clazz = (Class) pType.getRawType(); typeArgs = pType.getActualTypeArguments(); + } else if (type instanceof WildcardType) { + return Object.class; } else { clazz = (Class) type; } @@ -135,7 +146,7 @@ private static Type chooseImpl(Type type) { } else if (clazz == Set.class) { clazz = implClazz == null ? HashSet.class : implClazz; } - return new ParameterizedTypeImpl(new Type[]{compType}, null, clazz); + return GenericsHelper.createParameterizedType(new Type[]{compType}, null, clazz); } if (Map.class.isAssignableFrom(clazz)) { Type keyType = String.class; @@ -150,19 +161,20 @@ private static Type chooseImpl(Type type) { "can not bind to generic collection without argument types, " + "try syntax like TypeLiteral>{}"); } - if (keyType != String.class) { - throw new IllegalArgumentException("map key must be String"); - } if (clazz == Map.class) { clazz = implClazz == null ? HashMap.class : implClazz; } - return new ParameterizedTypeImpl(new Type[]{keyType, valueType}, null, clazz); + if (keyType == Object.class) { + keyType = String.class; + } + MapKeyDecoders.registerOrGetExisting(keyType); + return GenericsHelper.createParameterizedType(new Type[]{keyType, valueType}, null, clazz); } if (implClazz != null) { if (typeArgs.length == 0) { return implClazz; } else { - return new ParameterizedTypeImpl(typeArgs, null, implClazz); + return GenericsHelper.createParameterizedType(typeArgs, null, implClazz); } } return type; @@ -171,7 +183,7 @@ private static Type chooseImpl(Type type) { private static void staticGen(String cacheKey, String source) throws IOException { createDir(cacheKey); String fileName = cacheKey.replace('.', '/') + ".java"; - FileOutputStream fileOutputStream = new FileOutputStream(fileName); + FileOutputStream fileOutputStream = new FileOutputStream(new File(isDoingStaticCodegen.outputDir, fileName)); try { OutputStreamWriter writer = new OutputStreamWriter(fileOutputStream); try { @@ -198,7 +210,7 @@ private static void staticGen(String cacheKey, OutputStreamWriter writer, String private static void createDir(String cacheKey) { String[] parts = cacheKey.split("\\."); - File parent = new File("."); + File parent = new File(isDoingStaticCodegen.outputDir); for (int i = 0; i < parts.length - 1; i++) { String part = parts[i]; File current = new File(parent, part); @@ -207,28 +219,28 @@ private static void createDir(String cacheKey) { } } - private static String genSource(Class clazz, Type[] typeArgs) { - if (clazz.isArray()) { - return CodegenImplArray.genArray(clazz); + private static String genSource(DecodingMode mode, ClassInfo classInfo) { + if (classInfo.clazz.isArray()) { + return CodegenImplArray.genArray(classInfo); } - if (Map.class.isAssignableFrom(clazz)) { - return CodegenImplMap.genMap(clazz, typeArgs); + if (Map.class.isAssignableFrom(classInfo.clazz)) { + return CodegenImplMap.genMap(classInfo); } - if (Collection.class.isAssignableFrom(clazz)) { - return CodegenImplArray.genCollection(clazz, typeArgs); + if (Collection.class.isAssignableFrom(classInfo.clazz)) { + return CodegenImplArray.genCollection(classInfo); } - if (clazz.isEnum()) { - return CodegenImplEnum.genEnum(clazz); + if (classInfo.clazz.isEnum()) { + return CodegenImplEnum.genEnum(classInfo); } - ClassDescriptor desc = JsoniterSpi.getDecodingClassDescriptor(clazz, false); - if (shouldUseStrictMode(desc)) { - return CodegenImplObjectStrict.genObjectUsingStrict(clazz, desc); + ClassDescriptor desc = ClassDescriptor.getDecodingClassDescriptor(classInfo, false); + if (shouldUseStrictMode(mode, desc)) { + return CodegenImplObjectStrict.genObjectUsingStrict(desc); } else { - return CodegenImplObjectHash.genObjectUsingHash(clazz, desc); + return CodegenImplObjectHash.genObjectUsingHash(desc); } } - private static boolean shouldUseStrictMode(ClassDescriptor desc) { + private static boolean shouldUseStrictMode(DecodingMode mode, ClassDescriptor desc) { if (mode == DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_STRICTLY) { return true; } @@ -243,14 +255,24 @@ private static boolean shouldUseStrictMode(ClassDescriptor desc) { // only slice support unknown field tracking return true; } - if (allBindings.isEmpty()) { + if (!desc.keyValueTypeWrappers.isEmpty()) { + return true; + } + boolean hasBinding = false; + for (Binding allBinding : allBindings) { + if (allBinding.fromNames.length > 0) { + hasBinding = true; + } + } + if (!hasBinding) { + // empty object can only be handled by strict mode return true; } return false; } - public static void staticGenDecoders(TypeLiteral[] typeLiterals) { - isDoingStaticCodegen = true; + public static void staticGenDecoders(TypeLiteral[] typeLiterals, CodegenAccess.StaticCodegenTarget staticCodegenTarget) { + isDoingStaticCodegen = staticCodegenTarget; for (TypeLiteral typeLiteral : typeLiterals) { gen(typeLiteral.getDecoderCacheKey(), typeLiteral.getType()); } diff --git a/src/main/java/com/jsoniter/CodegenAccess.java b/src/main/java/com/jsoniter/CodegenAccess.java index 7633a35e..bc4f3cb7 100644 --- a/src/main/java/com/jsoniter/CodegenAccess.java +++ b/src/main/java/com/jsoniter/CodegenAccess.java @@ -1,9 +1,6 @@ package com.jsoniter; -import com.jsoniter.spi.Decoder; -import com.jsoniter.spi.JsonException; -import com.jsoniter.spi.JsoniterSpi; -import com.jsoniter.spi.TypeLiteral; +import com.jsoniter.spi.*; import java.io.IOException; import java.util.Collection; @@ -144,6 +141,15 @@ public static final Slice readSlice(JsonIterator iter) throws IOException { return IterImpl.readSlice(iter); } + public static final Object readMapKey(String cacheKey, JsonIterator iter) throws IOException { + 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 { for (int i = iter.head; i < iter.tail; i++) { byte c = iter.buf[i]; @@ -160,8 +166,8 @@ final static boolean skipWhitespacesWithoutLoadMore(JsonIterator iter) throws IO return true; } - public static void staticGenDecoders(TypeLiteral[] typeLiterals) { - Codegen.staticGenDecoders(typeLiterals); + public static void staticGenDecoders(TypeLiteral[] typeLiterals, StaticCodegenTarget staticCodegenTarget) { + Codegen.staticGenDecoders(typeLiterals, staticCodegenTarget); } public static int head(JsonIterator iter) { @@ -183,4 +189,12 @@ public static int calcHash(String str) { public static void skipFixedBytes(JsonIterator iter, int n) throws IOException { IterImpl.skipFixedBytes(iter, n); } + + public static class StaticCodegenTarget { + public String outputDir; + + public StaticCodegenTarget(String outputDir) { + this.outputDir = outputDir; + } + } } diff --git a/src/main/java/com/jsoniter/CodegenImplArray.java b/src/main/java/com/jsoniter/CodegenImplArray.java index 645bbcf0..a80ef7af 100644 --- a/src/main/java/com/jsoniter/CodegenImplArray.java +++ b/src/main/java/com/jsoniter/CodegenImplArray.java @@ -1,5 +1,7 @@ package com.jsoniter; +import com.jsoniter.spi.ClassInfo; + import java.lang.reflect.Type; import java.util.*; @@ -11,10 +13,10 @@ class CodegenImplArray { add(Vector.class); }}; - public static String genArray(Class clazz) { - Class compType = clazz.getComponentType(); + public static String genArray(ClassInfo classInfo) { + Class compType = classInfo.clazz.getComponentType(); if (compType.isArray()) { - throw new IllegalArgumentException("nested array not supported: " + clazz.getCanonicalName()); + throw new IllegalArgumentException("nested array not supported: " + classInfo.clazz.getCanonicalName()); } StringBuilder lines = new StringBuilder(); append(lines, "com.jsoniter.CodegenAccess.resetExistingObject(iter);"); @@ -77,11 +79,11 @@ public static String genArray(Class clazz) { "{{op}}", CodegenImplNative.genReadOp(compType)); } - public static String genCollection(Class clazz, Type[] typeArgs) { - if (WITH_CAPACITY_COLLECTION_CLASSES.contains(clazz)) { - return CodegenImplArray.genCollectionWithCapacity(clazz, typeArgs[0]); + public static String genCollection(ClassInfo classInfo) { + if (WITH_CAPACITY_COLLECTION_CLASSES.contains(classInfo.clazz)) { + return CodegenImplArray.genCollectionWithCapacity(classInfo.clazz, classInfo.typeArgs[0]); } else { - return CodegenImplArray.genCollectionWithoutCapacity(clazz, typeArgs[0]); + return CodegenImplArray.genCollectionWithoutCapacity(classInfo.clazz, classInfo.typeArgs[0]); } } diff --git a/src/main/java/com/jsoniter/CodegenImplEnum.java b/src/main/java/com/jsoniter/CodegenImplEnum.java index b80eb5a8..8e9d084f 100644 --- a/src/main/java/com/jsoniter/CodegenImplEnum.java +++ b/src/main/java/com/jsoniter/CodegenImplEnum.java @@ -1,16 +1,18 @@ package com.jsoniter; +import com.jsoniter.spi.ClassInfo; + import java.util.*; class CodegenImplEnum { - public static String genEnum(Class clazz) { + public static String genEnum(ClassInfo classInfo) { StringBuilder lines = new StringBuilder(); append(lines, "if (iter.readNull()) { return null; }"); - append(lines, "com.jsoniter.Slice field = com.jsoniter.CodegenAccess.readSlice(iter);"); + append(lines, "com.jsoniter.spi.Slice field = com.jsoniter.CodegenAccess.readSlice(iter);"); append(lines, "switch (field.len()) {"); - append(lines, renderTriTree(buildTriTree(Arrays.asList(clazz.getEnumConstants())))); + append(lines, renderTriTree(buildTriTree(Arrays.asList(classInfo.clazz.getEnumConstants())))); append(lines, "}"); // end of switch - append(lines, String.format("throw iter.reportError(\"decode enum\", field + \" is not valid enum for %s\");", clazz.getName())); + append(lines, String.format("throw iter.reportError(\"decode enum\", field + \" is not valid enum for %s\");", classInfo.clazz.getName())); return lines.toString(); } diff --git a/src/main/java/com/jsoniter/CodegenImplMap.java b/src/main/java/com/jsoniter/CodegenImplMap.java index d22e8347..4f64a77a 100644 --- a/src/main/java/com/jsoniter/CodegenImplMap.java +++ b/src/main/java/com/jsoniter/CodegenImplMap.java @@ -1,13 +1,15 @@ package com.jsoniter; +import com.jsoniter.spi.ClassInfo; +import com.jsoniter.spi.TypeLiteral; + import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.Map; class CodegenImplMap { - public static String genMap(Class clazz, Type[] typeArgs) { - Type valueType = typeArgs[1]; + public static String genMap(ClassInfo classInfo) { + Type keyType = classInfo.typeArgs[0]; + Type valueType = classInfo.typeArgs[1]; StringBuilder lines = new StringBuilder(); append(lines, "{{clazz}} map = ({{clazz}})com.jsoniter.CodegenAccess.resetExistingObject(iter);"); append(lines, "if (iter.readNull()) { return null; }"); @@ -16,11 +18,18 @@ public static String genMap(Class clazz, Type[] typeArgs) { append(lines, "return map;"); append(lines, "}"); append(lines, "do {"); - append(lines, "String field = com.jsoniter.CodegenAccess.readObjectFieldAsString(iter);"); - append(lines, "map.put(field, {{op}});"); + if (keyType == String.class) { + append(lines, "java.lang.Object mapKey = com.jsoniter.CodegenAccess.readObjectFieldAsString(iter);"); + } else { + append(lines, "java.lang.Object mapKey = com.jsoniter.CodegenAccess.readMapKey(\"" + + TypeLiteral.create(keyType).getDecoderCacheKey() +"\", iter);"); + } + append(lines, "map.put(mapKey, {{op}});"); append(lines, "} while (com.jsoniter.CodegenAccess.nextToken(iter) == ',');"); append(lines, "return map;"); - return lines.toString().replace("{{clazz}}", clazz.getName()).replace("{{op}}", CodegenImplNative.genReadOp(valueType)); + return lines.toString() + .replace("{{clazz}}", classInfo.clazz.getName()) + .replace("{{op}}", CodegenImplNative.genReadOp(valueType)); } private static void append(StringBuilder lines, String str) { diff --git a/src/main/java/com/jsoniter/CodegenImplNative.java b/src/main/java/com/jsoniter/CodegenImplNative.java index e49be435..156ce8f2 100644 --- a/src/main/java/com/jsoniter/CodegenImplNative.java +++ b/src/main/java/com/jsoniter/CodegenImplNative.java @@ -6,6 +6,7 @@ import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; import java.math.BigDecimal; import java.math.BigInteger; import java.util.HashMap; @@ -21,14 +22,14 @@ class CodegenImplNative { put("int", "iter.readInt()"); put("char", "iter.readInt()"); put("long", "iter.readLong()"); - put(Float.class.getName(), "java.lang.Float.valueOf(iter.readFloat())"); - put(Double.class.getName(), "java.lang.Double.valueOf(iter.readDouble())"); - put(Boolean.class.getName(), "java.lang.Boolean.valueOf(iter.readBoolean())"); - put(Byte.class.getName(), "java.lang.Byte.valueOf((byte)iter.readShort())"); - put(Character.class.getName(), "java.lang.Character.valueOf((char)iter.readShort())"); - put(Short.class.getName(), "java.lang.Short.valueOf(iter.readShort())"); - put(Integer.class.getName(), "java.lang.Integer.valueOf(iter.readInt())"); - put(Long.class.getName(), "java.lang.Long.valueOf(iter.readLong())"); + put(Float.class.getName(), "(iter.readNull() ? null : java.lang.Float.valueOf(iter.readFloat()))"); + put(Double.class.getName(), "(iter.readNull() ? null : java.lang.Double.valueOf(iter.readDouble()))"); + put(Boolean.class.getName(), "(iter.readNull() ? null : java.lang.Boolean.valueOf(iter.readBoolean()))"); + put(Byte.class.getName(), "(iter.readNull() ? null : java.lang.Byte.valueOf((byte)iter.readShort()))"); + put(Character.class.getName(), "(iter.readNull() ? null : java.lang.Character.valueOf((char)iter.readShort()))"); + put(Short.class.getName(), "(iter.readNull() ? null : java.lang.Short.valueOf(iter.readShort()))"); + put(Integer.class.getName(), "(iter.readNull() ? null : java.lang.Integer.valueOf(iter.readInt()))"); + put(Long.class.getName(), "(iter.readNull() ? null : java.lang.Long.valueOf(iter.readLong()))"); put(BigDecimal.class.getName(), "iter.readBigDecimal()"); put(BigInteger.class.getName(), "iter.readBigInteger()"); put(String.class.getName(), "iter.readString()"); @@ -45,7 +46,7 @@ public Object decode(JsonIterator iter) throws IOException { put(Float.class, new Decoder() { @Override public Object decode(JsonIterator iter) throws IOException { - return iter.readFloat(); + return iter.readNull() ? null : iter.readFloat(); } }); put(double.class, new Decoder() { @@ -57,7 +58,7 @@ public Object decode(JsonIterator iter) throws IOException { put(Double.class, new Decoder() { @Override public Object decode(JsonIterator iter) throws IOException { - return iter.readDouble(); + return iter.readNull() ? null : iter.readDouble(); } }); put(boolean.class, new Decoder() { @@ -69,19 +70,19 @@ public Object decode(JsonIterator iter) throws IOException { put(Boolean.class, new Decoder() { @Override public Object decode(JsonIterator iter) throws IOException { - return iter.readBoolean(); + return iter.readNull() ? null : iter.readBoolean(); } }); put(byte.class, new Decoder() { @Override public Object decode(JsonIterator iter) throws IOException { - return iter.readShort(); + return Byte.valueOf((byte) iter.readShort()); } }); put(Byte.class, new Decoder() { @Override public Object decode(JsonIterator iter) throws IOException { - return iter.readShort(); + return iter.readNull() ? null : (byte)iter.readShort(); } }); put(short.class, new Decoder() { @@ -93,7 +94,7 @@ public Object decode(JsonIterator iter) throws IOException { put(Short.class, new Decoder() { @Override public Object decode(JsonIterator iter) throws IOException { - return iter.readShort(); + return iter.readNull() ? null : iter.readShort(); } }); put(int.class, new Decoder() { @@ -105,19 +106,19 @@ public Object decode(JsonIterator iter) throws IOException { put(Integer.class, new Decoder() { @Override public Object decode(JsonIterator iter) throws IOException { - return iter.readInt(); + return iter.readNull() ? null : iter.readInt(); } }); put(char.class, new Decoder() { @Override public Object decode(JsonIterator iter) throws IOException { - return iter.readInt(); + return (char)iter.readInt(); } }); put(Character.class, new Decoder() { @Override public Object decode(JsonIterator iter) throws IOException { - return iter.readInt(); + return iter.readNull() ? null : (char)iter.readInt(); } }); put(long.class, new Decoder() { @@ -129,7 +130,7 @@ public Object decode(JsonIterator iter) throws IOException { put(Long.class, new Decoder() { @Override public Object decode(JsonIterator iter) throws IOException { - return iter.readLong(); + return iter.readNull() ? null : iter.readLong(); } }); put(BigDecimal.class, new Decoder() { @@ -177,6 +178,8 @@ public static String getTypeName(Type fieldType) { ParameterizedType pType = (ParameterizedType) fieldType; Class clazz = (Class) pType.getRawType(); return clazz.getCanonicalName(); + } else if (fieldType instanceof WildcardType) { + return Object.class.getCanonicalName(); } else { throw new JsonException("unsupported type: " + fieldType); } @@ -204,6 +207,8 @@ private static String genReadOp(String cacheKey, Type valueType) { if (nativeRead != null) { return nativeRead; } + } else if (valueType instanceof WildcardType) { + return NATIVE_READS.get(Object.class.getCanonicalName()); } Codegen.getDecoder(cacheKey, valueType); if (Codegen.canStaticAccess(cacheKey)) { diff --git a/src/main/java/com/jsoniter/CodegenImplObjectHash.java b/src/main/java/com/jsoniter/CodegenImplObjectHash.java index 1bd590cd..e0982ec0 100644 --- a/src/main/java/com/jsoniter/CodegenImplObjectHash.java +++ b/src/main/java/com/jsoniter/CodegenImplObjectHash.java @@ -4,10 +4,11 @@ import java.util.*; -public class CodegenImplObjectHash { +class CodegenImplObjectHash { // the implementation is from dsljson, it is the fastest although has the risk not matching field strictly - public static String genObjectUsingHash(Class clazz, ClassDescriptor desc) { + public static String genObjectUsingHash(ClassDescriptor desc) { + Class clazz = desc.clazz; StringBuilder lines = new StringBuilder(); // === if null, return null append(lines, "java.lang.Object existingObj = com.jsoniter.CodegenAccess.resetExistingObject(iter);"); @@ -43,12 +44,15 @@ public static String genObjectUsingHash(Class clazz, ClassDescriptor desc) { append(lines, "} // end of if end"); append(lines, "} else { com.jsoniter.CodegenAccess.unreadByte(iter); }// end of if not quote"); for (Binding field : desc.fields) { + if (field.fromNames.length == 0) { + continue; + } appendVarDef(lines, field); } for (Binding setter : desc.setters) { appendVarDef(lines, setter); } - for (WrapperDescriptor setter : desc.wrappers) { + for (WrapperDescriptor setter : desc.bindingTypeWrappers) { for (Binding param : setter.parameters) { appendVarDef(lines, param); } @@ -77,11 +81,11 @@ public int compare(String o1, String o2) { int intHash = calcHash(fromName); if (intHash == 0) { // hash collision, 0 can not be used as sentinel - return CodegenImplObjectStrict.genObjectUsingStrict(clazz, desc); + return CodegenImplObjectStrict.genObjectUsingStrict(desc); } if (knownHashes.contains(intHash)) { // hash collision with other field can not be used as sentinel - return CodegenImplObjectStrict.genObjectUsingStrict(clazz, desc); + return CodegenImplObjectStrict.genObjectUsingStrict(desc); } knownHashes.add(intHash); append(lines, "case " + intHash + ": "); @@ -93,12 +97,15 @@ public int compare(String o1, String o2) { append(lines, "} while (com.jsoniter.CodegenAccess.nextTokenIsComma(iter));"); append(lines, CodegenImplNative.getTypeName(clazz) + " obj = {{newInst}};"); for (Binding field : desc.fields) { + if (field.fromNames.length == 0) { + continue; + } append(lines, String.format("obj.%s = _%s_;", field.field.getName(), field.name)); } for (Binding setter : desc.setters) { append(lines, String.format("obj.%s(_%s_);", setter.method.getName(), setter.name)); } - appendWrappers(desc.wrappers, lines); + appendWrappers(desc.bindingTypeWrappers, lines); append(lines, "return obj;"); return lines.toString() .replace("{{clazz}}", clazz.getCanonicalName()) diff --git a/src/main/java/com/jsoniter/CodegenImplObjectStrict.java b/src/main/java/com/jsoniter/CodegenImplObjectStrict.java index 91f75b37..ba87eaa2 100644 --- a/src/main/java/com/jsoniter/CodegenImplObjectStrict.java +++ b/src/main/java/com/jsoniter/CodegenImplObjectStrict.java @@ -2,6 +2,7 @@ import com.jsoniter.spi.*; +import java.lang.reflect.Method; import java.util.*; import static com.jsoniter.CodegenImplObjectHash.appendVarDef; @@ -20,7 +21,7 @@ class CodegenImplObjectStrict { put("long", "0"); }}; - public static String genObjectUsingStrict(Class clazz, ClassDescriptor desc) { + public static String genObjectUsingStrict(ClassDescriptor desc) { List allBindings = desc.allDecoderBindings(); int lastRequiredIdx = assignMaskForRequiredProperties(allBindings); boolean hasRequiredBinding = lastRequiredIdx > 0; @@ -67,22 +68,25 @@ public static String genObjectUsingStrict(Class clazz, ClassDescriptor desc) { } append(lines, "}"); for (Binding field : desc.fields) { + if (field.fromNames.length == 0) { + continue; + } appendVarDef(lines, field); } for (Binding setter : desc.setters) { appendVarDef(lines, setter); } } - for (WrapperDescriptor wrapper : desc.wrappers) { + for (WrapperDescriptor wrapper : desc.bindingTypeWrappers) { for (Binding param : wrapper.parameters) { appendVarDef(lines, param); } } // === bind first field - if (desc.onExtraProperties != null) { + if (desc.onExtraProperties != null || !desc.keyValueTypeWrappers.isEmpty()) { append(lines, "java.util.Map extra = null;"); } - append(lines, "com.jsoniter.Slice field = com.jsoniter.CodegenAccess.readObjectFieldAsSlice(iter);"); + append(lines, "com.jsoniter.spi.Slice field = com.jsoniter.CodegenAccess.readObjectFieldAsSlice(iter);"); append(lines, "boolean once = true;"); append(lines, "while (once) {"); append(lines, "once = false;"); @@ -90,6 +94,9 @@ public static String genObjectUsingStrict(Class clazz, ClassDescriptor desc) { if (desc.ctor.parameters.isEmpty()) { // if not field or setter, the value will set to temp variable for (Binding field : desc.fields) { + if (field.fromNames.length == 0) { + continue; + } rendered = updateBindingSetOp(rendered, field); } for (Binding setter : desc.setters) { @@ -121,25 +128,43 @@ public static String genObjectUsingStrict(Class clazz, ClassDescriptor desc) { if (desc.onExtraProperties != null) { appendSetExtraProperteis(lines, desc); } + if (!desc.keyValueTypeWrappers.isEmpty()) { + appendSetExtraToKeyValueTypeWrappers(lines, desc); + } if (!desc.ctor.parameters.isEmpty()) { - append(lines, String.format("%s obj = {{newInst}};", CodegenImplNative.getTypeName(clazz))); + append(lines, String.format("%s obj = {{newInst}};", CodegenImplNative.getTypeName(desc.clazz))); for (Binding field : desc.fields) { + if (field.fromNames.length == 0) { + continue; + } append(lines, String.format("obj.%s = _%s_;", field.field.getName(), field.name)); } for (Binding setter : desc.setters) { append(lines, String.format("obj.%s(_%s_);", setter.method.getName(), setter.name)); } } - appendWrappers(desc.wrappers, lines); + appendWrappers(desc.bindingTypeWrappers, lines); append(lines, "return obj;"); return lines.toString() - .replace("{{clazz}}", clazz.getCanonicalName()) - .replace("{{newInst}}", CodegenImplObjectHash.genNewInstCode(clazz, desc.ctor)); + .replace("{{clazz}}", desc.clazz.getCanonicalName()) + .replace("{{newInst}}", CodegenImplObjectHash.genNewInstCode(desc.clazz, desc.ctor)); + } + + private static void appendSetExtraToKeyValueTypeWrappers(StringBuilder lines, ClassDescriptor desc) { + append(lines, "java.util.Iterator extraIter = extra.entrySet().iterator();"); + append(lines, "while(extraIter.hasNext()) {"); + for (Method wrapper : desc.keyValueTypeWrappers) { + append(lines, "java.util.Map.Entry entry = (java.util.Map.Entry)extraIter.next();"); + append(lines, "String key = entry.getKey().toString();"); + append(lines, "com.jsoniter.any.Any value = (com.jsoniter.any.Any)entry.getValue();"); + append(lines, String.format("obj.%s(key, value.object());", wrapper.getName())); + } + append(lines, "}"); } private static void appendSetExtraProperteis(StringBuilder lines, ClassDescriptor desc) { Binding onExtraProperties = desc.onExtraProperties; - if (ParameterizedTypeImpl.isSameClass(onExtraProperties.valueType, Map.class)) { + if (GenericsHelper.isSameClass(onExtraProperties.valueType, Map.class)) { if (onExtraProperties.field != null) { append(lines, String.format("obj.%s = extra;", onExtraProperties.field.getName())); } else { @@ -175,6 +200,9 @@ private static int assignMaskForRequiredProperties(List allBindings) { } private static String updateBindingSetOp(String rendered, Binding binding) { + if (binding.fromNames.length == 0) { + return rendered; + } while (true) { String marker = "_" + binding.name + "_"; int start = rendered.indexOf(marker); @@ -230,15 +258,15 @@ private static void appendMissingRequiredProperties(StringBuilder lines, ClassDe } private static void appendOnUnknownField(StringBuilder lines, ClassDescriptor desc) { - if (desc.asExtraForUnknownProperties) { - if (desc.onExtraProperties == null) { - append(lines, "throw new com.jsoniter.spi.JsonException('extra property: ' + field.toString());".replace('\'', '"')); - } else { + if (desc.asExtraForUnknownProperties && desc.onExtraProperties == null) { + append(lines, "throw new com.jsoniter.spi.JsonException('extra property: ' + field.toString());".replace('\'', '"')); + } else { + if (desc.asExtraForUnknownProperties || !desc.keyValueTypeWrappers.isEmpty()) { append(lines, "if (extra == null) { extra = new java.util.HashMap(); }"); append(lines, "extra.put(field.toString(), iter.readAny());"); + } else { + append(lines, "iter.skip();"); } - } else { - append(lines, "iter.skip();"); } } @@ -338,7 +366,7 @@ public static String genObjectUsingSkip(Class clazz, ConstructorDescriptor ctor) .replace("{{clazz}}", clazz.getCanonicalName()) .replace("{{newInst}}", CodegenImplObjectHash.genNewInstCode(clazz, ctor)); } - + static void append(StringBuilder lines, String str) { lines.append(str); lines.append("\n"); diff --git a/src/main/java/com/jsoniter/IterImpl.java b/src/main/java/com/jsoniter/IterImpl.java index 3583aa38..ad779fd8 100644 --- a/src/main/java/com/jsoniter/IterImpl.java +++ b/src/main/java/com/jsoniter/IterImpl.java @@ -2,11 +2,18 @@ import com.jsoniter.any.Any; import com.jsoniter.spi.JsonException; +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) != '"') { @@ -52,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 @@ -78,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 @@ -118,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; } @@ -209,6 +216,7 @@ public final static boolean loadMore(JsonIterator iter) throws IOException { public final static int readStringSlowPath(JsonIterator iter, int j) throws IOException { try { + boolean isExpectingLowSurrogate = false; for (int i = iter.head; i < iter.tail; ) { int bc = iter.buf[i++]; if (bc == '"') { @@ -242,6 +250,23 @@ public final static int readStringSlowPath(JsonIterator iter, int j) throws IOEx (IterImplString.translateHex(iter.buf[i++]) << 8) + (IterImplString.translateHex(iter.buf[i++]) << 4) + IterImplString.translateHex(iter.buf[i++]); + if (Character.isHighSurrogate((char) bc)) { + if (isExpectingLowSurrogate) { + throw new JsonException("invalid surrogate"); + } else { + isExpectingLowSurrogate = true; + } + } else if (Character.isLowSurrogate((char) bc)) { + if (isExpectingLowSurrogate) { + isExpectingLowSurrogate = false; + } else { + throw new JsonException("invalid surrogate"); + } + } else { + if (isExpectingLowSurrogate) { + throw new JsonException("invalid surrogate"); + } + } break; default: @@ -304,143 +329,158 @@ public static int updateStringCopyBound(final JsonIterator iter, final int bound return bound; } - static final int readPositiveInt(final JsonIterator iter, byte c) throws IOException { + static final int readInt(final JsonIterator iter, final byte c) throws IOException { int ind = IterImplNumber.intDigits[c]; if (ind == 0) { + IterImplForStreaming.assertNotLeadingZero(iter); return 0; } if (ind == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { - throw iter.reportError("readPositiveInt", "expect 0~9"); + throw iter.reportError("readInt", "expect 0~9"); } if (iter.tail - iter.head > 9) { int i = iter.head; int ind2 = IterImplNumber.intDigits[iter.buf[i]]; if (ind2 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { iter.head = i; - return ind; + return -ind; } int ind3 = IterImplNumber.intDigits[iter.buf[++i]]; if (ind3 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { iter.head = i; - return ind * 10 + ind2; + ind = ind * 10 + ind2; + return -ind; } int ind4 = IterImplNumber.intDigits[iter.buf[++i]]; if (ind4 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { iter.head = i; - return ind * 100 + ind2 * 10 + ind3; + ind = ind * 100 + ind2 * 10 + ind3; + return -ind; } int ind5 = IterImplNumber.intDigits[iter.buf[++i]]; if (ind5 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { iter.head = i; - return ind * 1000 + ind2 * 100 + ind3 * 10 + ind4; + ind = ind * 1000 + ind2 * 100 + ind3 * 10 + ind4; + return -ind; } int ind6 = IterImplNumber.intDigits[iter.buf[++i]]; if (ind6 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { iter.head = i; - return ind * 10000 + ind2 * 1000 + ind3 * 100 + ind4 * 10 + ind5; + ind = ind * 10000 + ind2 * 1000 + ind3 * 100 + ind4 * 10 + ind5; + return -ind; } int ind7 = IterImplNumber.intDigits[iter.buf[++i]]; if (ind7 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { iter.head = i; - return ind * 100000 + ind2 * 10000 + ind3 * 1000 + ind4 * 100 + ind5 * 10 + ind6; + ind = ind * 100000 + ind2 * 10000 + ind3 * 1000 + ind4 * 100 + ind5 * 10 + ind6; + return -ind; } int ind8 = IterImplNumber.intDigits[iter.buf[++i]]; if (ind8 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { iter.head = i; - return ind * 1000000 + ind2 * 100000 + ind3 * 10000 + ind4 * 1000 + ind5 * 100 + ind6 * 10 + ind7; + ind = ind * 1000000 + ind2 * 100000 + ind3 * 10000 + ind4 * 1000 + ind5 * 100 + ind6 * 10 + ind7; + return -ind; } int ind9 = IterImplNumber.intDigits[iter.buf[++i]]; ind = ind * 10000000 + ind2 * 1000000 + ind3 * 100000 + ind4 * 10000 + ind5 * 1000 + ind6 * 100 + ind7 * 10 + ind8; iter.head = i; if (ind9 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { - return ind; + return -ind; } } return IterImplForStreaming.readIntSlowPath(iter, ind); } - static final long readPositiveLong(final JsonIterator iter, byte c) throws IOException { + static final long readLong(final JsonIterator iter, final byte c) throws IOException { long ind = IterImplNumber.intDigits[c]; - if (ind == 0) { - return 0; - } if (ind == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { - throw iter.reportError("readPositiveLong", "expect 0~9"); + throw iter.reportError("readLong", "expect 0~9"); } if (iter.tail - iter.head > 9) { int i = iter.head; int ind2 = IterImplNumber.intDigits[iter.buf[i]]; if (ind2 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { iter.head = i; - return ind; + return -ind; } int ind3 = IterImplNumber.intDigits[iter.buf[++i]]; if (ind3 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { iter.head = i; - return ind * 10 + ind2; + ind = ind * 10 + ind2; + return -ind; } int ind4 = IterImplNumber.intDigits[iter.buf[++i]]; if (ind4 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { iter.head = i; - return ind * 100 + ind2 * 10 + ind3; + ind = ind * 100 + ind2 * 10 + ind3; + return -ind; } int ind5 = IterImplNumber.intDigits[iter.buf[++i]]; if (ind5 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { iter.head = i; - return ind * 1000 + ind2 * 100 + ind3 * 10 + ind4; + ind = ind * 1000 + ind2 * 100 + ind3 * 10 + ind4; + return -ind; } int ind6 = IterImplNumber.intDigits[iter.buf[++i]]; if (ind6 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { iter.head = i; - return ind * 10000 + ind2 * 1000 + ind3 * 100 + ind4 * 10 + ind5; + ind = ind * 10000 + ind2 * 1000 + ind3 * 100 + ind4 * 10 + ind5; + return -ind; } int ind7 = IterImplNumber.intDigits[iter.buf[++i]]; if (ind7 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { iter.head = i; - return ind * 100000 + ind2 * 10000 + ind3 * 1000 + ind4 * 100 + ind5 * 10 + ind6; + ind = ind * 100000 + ind2 * 10000 + ind3 * 1000 + ind4 * 100 + ind5 * 10 + ind6; + return -ind; } int ind8 = IterImplNumber.intDigits[iter.buf[++i]]; if (ind8 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { iter.head = i; - return ind * 1000000 + ind2 * 100000 + ind3 * 10000 + ind4 * 1000 + ind5 * 100 + ind6 * 10 + ind7; + ind = ind * 1000000 + ind2 * 100000 + ind3 * 10000 + ind4 * 1000 + ind5 * 100 + ind6 * 10 + ind7; + return -ind; } int ind9 = IterImplNumber.intDigits[iter.buf[++i]]; ind = ind * 10000000 + ind2 * 1000000 + ind3 * 100000 + ind4 * 10000 + ind5 * 1000 + ind6 * 100 + ind7 * 10 + ind8; iter.head = i; if (ind9 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { - return ind; + return -ind; } } return IterImplForStreaming.readLongSlowPath(iter, ind); } - static final double readPositiveDouble(final JsonIterator iter) throws IOException { + static final double readDouble(final JsonIterator iter) throws IOException { int oldHead = iter.head; try { - long value = IterImplNumber.readLong(iter); // without the dot - if (iter.head == iter.tail) { - return value; - } - byte c = iter.buf[iter.head]; - if (c == '.') { - iter.head++; - int start = iter.head; - c = iter.buf[iter.head++]; - long decimalPart = readPositiveLong(iter, c); - int decimalPlaces = iter.head - start; - if (decimalPlaces > 0 && decimalPlaces < IterImplNumber.POW10.length && (iter.head - oldHead) < 10) { - value = value * IterImplNumber.POW10[decimalPlaces] + decimalPart; - return value / (double) IterImplNumber.POW10[decimalPlaces]; + try { + long value = IterImplNumber.readLong(iter); // without the dot & sign + if (iter.head == iter.tail) { + return value; + } + byte c = iter.buf[iter.head]; + if (c == '.') { + iter.head++; + int start = iter.head; + c = iter.buf[iter.head++]; + long decimalPart = readLong(iter, c); + if (decimalPart == Long.MIN_VALUE) { + return IterImplForStreaming.readDoubleSlowPath(iter); + } + decimalPart = -decimalPart; + int decimalPlaces = iter.head - start; + if (decimalPlaces > 0 && decimalPlaces < IterImplNumber.POW10.length && (iter.head - oldHead) < 10) { + return value + (decimalPart / (double) IterImplNumber.POW10[decimalPlaces]); + } else { + iter.head = oldHead; + return IterImplForStreaming.readDoubleSlowPath(iter); + } } else { - iter.head = oldHead; - return IterImplForStreaming.readDoubleSlowPath(iter); + return value; } - } else { - if (iter.head < iter.tail && iter.buf[iter.head] == 'e') { + } finally { + if (iter.head < iter.tail && (iter.buf[iter.head] == 'e' || iter.buf[iter.head] == 'E')) { iter.head = oldHead; return IterImplForStreaming.readDoubleSlowPath(iter); - } else { - return value; } } } catch (JsonException e) { diff --git a/src/main/java/com/jsoniter/IterImplForStreaming.java b/src/main/java/com/jsoniter/IterImplForStreaming.java index ed362b2b..2cef3a16 100644 --- a/src/main/java/com/jsoniter/IterImplForStreaming.java +++ b/src/main/java/com/jsoniter/IterImplForStreaming.java @@ -1,6 +1,8 @@ package com.jsoniter; import com.jsoniter.any.Any; +import com.jsoniter.spi.JsonException; +import com.jsoniter.spi.Slice; import java.io.IOException; @@ -69,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 @@ -99,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 @@ -145,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; @@ -177,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; } @@ -272,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) { @@ -299,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)) { @@ -381,6 +389,7 @@ public static int updateStringCopyBound(final JsonIterator iter, final int bound } public final static int readStringSlowPath(JsonIterator iter, int j) throws IOException { + boolean isExpectingLowSurrogate = false; for (;;) { int bc = readByte(iter); if (bc == '"') { @@ -413,6 +422,23 @@ public final static int readStringSlowPath(JsonIterator iter, int j) throws IOEx (IterImplString.translateHex(readByte(iter)) << 8) + (IterImplString.translateHex(readByte(iter)) << 4) + IterImplString.translateHex(readByte(iter)); + if (Character.isHighSurrogate((char) bc)) { + if (isExpectingLowSurrogate) { + throw new JsonException("invalid surrogate"); + } else { + isExpectingLowSurrogate = true; + } + } else if (Character.isLowSurrogate((char) bc)) { + if (isExpectingLowSurrogate) { + isExpectingLowSurrogate = false; + } else { + throw new JsonException("invalid surrogate"); + } + } else { + if (isExpectingLowSurrogate) { + throw new JsonException("invalid surrogate"); + } + } break; default: @@ -467,7 +493,9 @@ public final static int readStringSlowPath(JsonIterator iter, int j) throws IOEx } } - static long readLongSlowPath(JsonIterator iter, long value) throws IOException { + static long readLongSlowPath(final JsonIterator iter, long value) throws IOException { + value = -value; // add negatives to avoid redundant checks for Long.MIN_VALUE on each iteration + long multmin = -922337203685477580L; // limit / 10 for (; ; ) { for (int i = iter.head; i < iter.tail; i++) { int ind = IterImplNumber.intDigits[iter.buf[i]]; @@ -475,16 +503,12 @@ static long readLongSlowPath(JsonIterator iter, long value) throws IOException { iter.head = i; return value; } - value = (value << 3) + (value << 1) + ind; - if (value < 0) { - // overflow - if (value == Long.MIN_VALUE) { - // if there is more number following, subsequent read will fail anyway - iter.head = i; - return value; - } else { - throw iter.reportError("readPositiveLong", "value is too large for long"); - } + if (value < multmin) { + throw iter.reportError("readLongSlowPath", "value is too large for long"); + } + value = (value << 3) + (value << 1) - ind; + if (value >= 0) { + throw iter.reportError("readLongSlowPath", "value is too large for long"); } } if (!IterImpl.loadMore(iter)) { @@ -494,7 +518,9 @@ static long readLongSlowPath(JsonIterator iter, long value) throws IOException { } } - static int readIntSlowPath(JsonIterator iter, int value) throws IOException { + static int readIntSlowPath(final JsonIterator iter, int value) throws IOException { + value = -value; // add negatives to avoid redundant checks for Integer.MIN_VALUE on each iteration + int multmin = -214748364; // limit / 10 for (; ; ) { for (int i = iter.head; i < iter.tail; i++) { int ind = IterImplNumber.intDigits[iter.buf[i]]; @@ -502,16 +528,12 @@ static int readIntSlowPath(JsonIterator iter, int value) throws IOException { iter.head = i; return value; } - value = (value << 3) + (value << 1) + ind; - if (value < 0) { - // overflow - if (value == Integer.MIN_VALUE) { - // if there is more number following, subsequent read will fail anyway - iter.head = i; - return value; - } else { - throw iter.reportError("readPositiveInt", "value is too large for int"); - } + if (value < multmin) { + throw iter.reportError("readIntSlowPath", "value is too large for int"); + } + value = (value << 3) + (value << 1) - ind; + if (value >= 0) { + throw iter.reportError("readIntSlowPath", "value is too large for int"); } } if (!IterImpl.loadMore(iter)) { @@ -523,14 +545,32 @@ static int readIntSlowPath(JsonIterator iter, int value) throws IOException { public static final double readDoubleSlowPath(final JsonIterator iter) throws IOException { try { - return Double.valueOf(readNumber(iter)); + 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) { @@ -540,10 +580,13 @@ public static final String readNumber(final JsonIterator iter) throws IOExceptio } byte c = iter.buf[i]; switch (c) { - case '-': case '.': case 'e': case 'E': + dotFound = true; + // fallthrough + case '-': + case '+': case '0': case '1': case '2': @@ -558,41 +601,63 @@ 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; } } } - - static final double readPositiveDouble(final JsonIterator iter) throws IOException { + static final double readDouble(final JsonIterator iter) throws IOException { return readDoubleSlowPath(iter); } - - static final long readPositiveLong(final JsonIterator iter, byte c) throws IOException { + static final long readLong(final JsonIterator iter, final byte c) throws IOException { long ind = IterImplNumber.intDigits[c]; if (ind == 0) { + assertNotLeadingZero(iter); return 0; } if (ind == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { - throw iter.reportError("readPositiveLong", "expect 0~9"); + throw iter.reportError("readLong", "expect 0~9"); } return IterImplForStreaming.readLongSlowPath(iter, ind); } - static final int readPositiveInt(final JsonIterator iter, byte c) throws IOException { + static final int readInt(final JsonIterator iter, final byte c) throws IOException { int ind = IterImplNumber.intDigits[c]; if (ind == 0) { + assertNotLeadingZero(iter); return 0; } if (ind == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { - throw iter.reportError("readPositiveInt", "expect 0~9"); + throw iter.reportError("readInt", "expect 0~9"); } return IterImplForStreaming.readIntSlowPath(iter, ind); } + + static void assertNotLeadingZero(JsonIterator iter) throws IOException { + try { + byte nextByte = iter.buf[iter.head]; + int ind2 = IterImplNumber.intDigits[nextByte]; + if (ind2 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) { + return; + } + throw iter.reportError("assertNotLeadingZero", "leading zero is invalid"); + } catch (ArrayIndexOutOfBoundsException e) { + iter.head = iter.tail; + return; + } + } } diff --git a/src/main/java/com/jsoniter/IterImplNumber.java b/src/main/java/com/jsoniter/IterImplNumber.java index deeef5e0..c521cdd5 100644 --- a/src/main/java/com/jsoniter/IterImplNumber.java +++ b/src/main/java/com/jsoniter/IterImplNumber.java @@ -64,39 +64,49 @@ class IterImplNumber { public static final double readDouble(final JsonIterator iter) throws IOException { final byte c = IterImpl.nextToken(iter); if (c == '-') { - return -IterImpl.readPositiveDouble(iter); + return -IterImpl.readDouble(iter); } else { iter.unreadByte(); - return IterImpl.readPositiveDouble(iter); + return IterImpl.readDouble(iter); } } public static final float readFloat(final JsonIterator iter) throws IOException { - final byte c = IterImpl.nextToken(iter); - if (c == '-') { - return (float)-IterImpl.readPositiveDouble(iter); - } else { - iter.unreadByte(); - return (float) IterImpl.readPositiveDouble(iter); - } + return (float) IterImplNumber.readDouble(iter); } public static final int readInt(final JsonIterator iter) throws IOException { byte c = IterImpl.nextToken(iter); if (c == '-') { - return -IterImpl.readPositiveInt(iter, IterImpl.readByte(iter)); + return IterImpl.readInt(iter, IterImpl.readByte(iter)); } else { - return IterImpl.readPositiveInt(iter, c); + int val = IterImpl.readInt(iter, c); + if (val == Integer.MIN_VALUE) { + throw iter.reportError("readInt", "value is too large for int"); + } + return -val; } } public static final long readLong(JsonIterator iter) throws IOException { byte c = IterImpl.nextToken(iter); if (c == '-') { - return -IterImpl.readPositiveLong(iter, IterImpl.readByte(iter)); + c = IterImpl.readByte(iter); + if (IterImplNumber.intDigits[c] == 0) { + IterImplForStreaming.assertNotLeadingZero(iter); + return 0; + } + return IterImpl.readLong(iter, c); } else { - return IterImpl.readPositiveLong(iter, c); + 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"); + } + return -val; } } - } diff --git a/src/main/java/com/jsoniter/IterImplString.java b/src/main/java/com/jsoniter/IterImplString.java index cafa8ae8..c6a0b452 100644 --- a/src/main/java/com/jsoniter/IterImplString.java +++ b/src/main/java/com/jsoniter/IterImplString.java @@ -53,11 +53,13 @@ class IterImplString { } public static final String readString(JsonIterator iter) throws IOException { - byte c = IterImpl.readByte(iter); + byte c = IterImpl.nextToken(iter); if (c != '"') { - if (readStringIsNull(iter, c)) { + if (c == 'n') { + IterImpl.skipFixedBytes(iter, 3); return null; } + throw iter.reportError("readString", "expect string or null, but " + (char) c); } int j = parse(iter); return new String(iter.reusableChars, 0, j); @@ -92,20 +94,6 @@ private static int parse(JsonIterator iter) throws IOException { return IterImpl.readStringSlowPath(iter, alreadyCopied); } - private static boolean readStringIsNull(JsonIterator iter, byte c) throws IOException { - if (c == 'n') { - IterImpl.skipFixedBytes(iter, 3); - return true; - } else { - c = IterImpl.nextToken(iter); - if (c == 'n') { - IterImpl.skipFixedBytes(iter, 3); - return true; - } - } - return false; - } - public static int translateHex(final byte b) { int val = hexDigits[b]; if (val == -1) { diff --git a/src/main/java/com/jsoniter/JsonIterator.java b/src/main/java/com/jsoniter/JsonIterator.java index 26e91a20..c198540b 100644 --- a/src/main/java/com/jsoniter/JsonIterator.java +++ b/src/main/java/com/jsoniter/JsonIterator.java @@ -1,13 +1,12 @@ package com.jsoniter; -import com.jsoniter.annotation.JsoniterAnnotationSupport; import com.jsoniter.any.Any; -import com.jsoniter.spi.JsonException; -import com.jsoniter.spi.TypeLiteral; +import com.jsoniter.spi.*; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; @@ -17,10 +16,14 @@ public class JsonIterator implements Closeable { + public Config configCache; private static boolean isStreamingEnabled = false; 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) { @@ -104,6 +116,7 @@ public final void reset(Slice value) { } public final void reset(InputStream in) { + JsonIterator.enableStreamingSupport(); this.in = in; this.head = 0; this.tail = 0; @@ -188,6 +201,11 @@ public final boolean readArray() throws IOException { return IterImplArray.readArray(this); } + public String readNumberAsString() throws IOException { + IterImplForStreaming.numberChars numberChars = IterImplForStreaming.readNumber(this); + return new String(numberChars.chars, 0, numberChars.charsLength); + } + public static interface ReadArrayCallback { boolean handle(JsonIterator iter, Object attachment) throws IOException; } @@ -226,18 +244,30 @@ public final double readDouble() throws IOException { public final BigDecimal readBigDecimal() throws IOException { // skip whitespace by read next - if (whatIsNext() != ValueType.NUMBER) { + ValueType valueType = whatIsNext(); + if (valueType == ValueType.NULL) { + skip(); + return null; + } + 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 { // skip whitespace by read next - if (whatIsNext() != ValueType.NUMBER) { + ValueType valueType = whatIsNext(); + if (valueType == ValueType.NULL) { + skip(); + return null; + } + 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 { @@ -273,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; @@ -307,12 +351,20 @@ public final T read(T existingObject) throws IOException { try { this.existingObject = existingObject; Class clazz = existingObject.getClass(); - return (T) Codegen.getDecoder(TypeLiteral.create(clazz).getDecoderCacheKey(), clazz).decode(this); + String cacheKey = currentConfig().getDecoderCacheKey(clazz); + return (T) Codegen.getDecoder(cacheKey, clazz).decode(this); } catch (ArrayIndexOutOfBoundsException e) { throw reportError("read", "premature end"); } } + private Config currentConfig() { + if (configCache == null) { + configCache = JsoniterSpi.getCurrentConfig(); + } + return configCache; + } + /** * try to bind to existing object, returned object might not the same instance * @@ -325,24 +377,25 @@ public final T read(T existingObject) throws IOException { public final T read(TypeLiteral typeLiteral, T existingObject) throws IOException { try { this.existingObject = existingObject; - return (T) Codegen.getDecoder(typeLiteral.getDecoderCacheKey(), typeLiteral.getType()).decode(this); + String cacheKey = currentConfig().getDecoderCacheKey(typeLiteral.getType()); + return (T) Codegen.getDecoder(cacheKey, typeLiteral.getType()).decode(this); } catch (ArrayIndexOutOfBoundsException e) { throw reportError("read", "premature end"); } } public final T read(Class clazz) throws IOException { - try { - return (T) Codegen.getDecoder(TypeLiteral.create(clazz).getDecoderCacheKey(), clazz).decode(this); - } catch (ArrayIndexOutOfBoundsException e) { - throw reportError("read", "premature end"); - } + return (T) read((Type) clazz); } public final T read(TypeLiteral typeLiteral) throws IOException { + return (T) read(typeLiteral.getType()); + } + + public final Object read(Type type) throws IOException { try { - String cacheKey = typeLiteral.getDecoderCacheKey(); - return (T) Codegen.getDecoder(cacheKey, typeLiteral.getType()).decode(this); + String cacheKey = currentConfig().getDecoderCacheKey(type); + return Codegen.getDecoder(cacheKey, type).decode(this); } catch (ArrayIndexOutOfBoundsException e) { throw reportError("read", "premature end"); } @@ -358,52 +411,48 @@ public void skip() throws IOException { IterImplSkip.skip(this); } - public static ThreadLocal tlsIter = new ThreadLocal() { - @Override - protected JsonIterator initialValue() { - return new JsonIterator(); + public static final T deserialize(Config config, String input, Class clazz) { + JsoniterSpi.setCurrentConfig(config); + try { + return deserialize(input.getBytes(), clazz); + } finally { + JsoniterSpi.clearCurrentConfig(); } - }; + } public static final T deserialize(String input, Class clazz) { - JsonIterator iter = tlsIter.get(); - byte[] bytes = input.getBytes(); - iter.reset(bytes); + return deserialize(input.getBytes(), clazz); + } + + public static final T deserialize(Config config, String input, TypeLiteral typeLiteral) { + JsoniterSpi.setCurrentConfig(config); try { - T val = iter.read(clazz); - if (iter.head != bytes.length) { - throw iter.reportError("deserialize", "trailing garbage found"); - } - return val; - } catch (ArrayIndexOutOfBoundsException e) { - throw iter.reportError("deserialize", "premature end"); - } catch (IOException e) { - throw new JsonException(e); + return deserialize(input.getBytes(), typeLiteral); + } finally { + JsoniterSpi.clearCurrentConfig(); } } public static final T deserialize(String input, TypeLiteral typeLiteral) { - JsonIterator iter = tlsIter.get(); - iter.reset(input.getBytes()); + return deserialize(input.getBytes(), typeLiteral); + } + + public static final T deserialize(Config config, byte[] input, Class clazz) { + JsoniterSpi.setCurrentConfig(config); try { - T val = iter.read(typeLiteral); - if (IterImpl.nextToken(iter) != 0) { - throw iter.reportError("deserialize", "trailing garbage found"); - } - return val; - } catch (ArrayIndexOutOfBoundsException e) { - throw iter.reportError("deserialize", "premature end"); - } catch (IOException e) { - throw new JsonException(e); + return deserialize(input, clazz); + } finally { + JsoniterSpi.clearCurrentConfig(); } } public static final T deserialize(byte[] input, Class clazz) { - JsonIterator iter = tlsIter.get(); - iter.reset(input); + int lastNotSpacePos = findLastNotSpacePos(input); + JsonIterator iter = JsonIteratorPool.borrowJsonIterator(); + iter.reset(input, 0, lastNotSpacePos); try { T val = iter.read(clazz); - if (IterImpl.nextToken(iter) != 0) { + if (iter.head != lastNotSpacePos) { throw iter.reportError("deserialize", "trailing garbage found"); } return val; @@ -411,15 +460,27 @@ public static final T deserialize(byte[] input, Class clazz) { throw iter.reportError("deserialize", "premature end"); } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); + } + } + + public static final T deserialize(Config config, byte[] input, TypeLiteral typeLiteral) { + JsoniterSpi.setCurrentConfig(config); + try { + return deserialize(input, typeLiteral); + } finally { + JsoniterSpi.clearCurrentConfig(); } } public static final T deserialize(byte[] input, TypeLiteral typeLiteral) { - JsonIterator iter = tlsIter.get(); - iter.reset(input); + int lastNotSpacePos = findLastNotSpacePos(input); + JsonIterator iter = JsonIteratorPool.borrowJsonIterator(); + iter.reset(input, 0, lastNotSpacePos); try { T val = iter.read(typeLiteral); - if (IterImpl.nextToken(iter) != 0) { + if (iter.head != lastNotSpacePos) { throw iter.reportError("deserialize", "trailing garbage found"); } return val; @@ -427,6 +488,17 @@ public static final T deserialize(byte[] input, TypeLiteral typeLiteral) throw iter.reportError("deserialize", "premature end"); } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); + } + } + + public static final Any deserialize(Config config, String input) { + JsoniterSpi.setCurrentConfig(config); + try { + return deserialize(input.getBytes()); + } finally { + JsoniterSpi.clearCurrentConfig(); } } @@ -434,12 +506,22 @@ public static final Any deserialize(String input) { return deserialize(input.getBytes()); } + public static final Any deserialize(Config config, byte[] input) { + JsoniterSpi.setCurrentConfig(config); + try { + return deserialize(input); + } finally { + JsoniterSpi.clearCurrentConfig(); + } + } + public static final Any deserialize(byte[] input) { - JsonIterator iter = tlsIter.get(); - iter.reset(input); + int lastNotSpacePos = findLastNotSpacePos(input); + JsonIterator iter = JsonIteratorPool.borrowJsonIterator(); + iter.reset(input, 0, lastNotSpacePos); try { Any val = iter.readAny(); - if (iter.head != input.length) { + if (iter.head != lastNotSpacePos) { throw iter.reportError("deserialize", "trailing garbage found"); } return val; @@ -447,11 +529,25 @@ public static final Any deserialize(byte[] input) { throw iter.reportError("deserialize", "premature end"); } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } } + private static int findLastNotSpacePos(byte[] input) { + for (int i = input.length - 1; i >= 0; i--) { + byte c = input[i]; + if (c != ' ' && c != '\t' && c != '\n' && c != '\r') { + return i + 1; + } + } + return 0; + } + public static void setMode(DecodingMode mode) { - Codegen.setMode(mode); + Config newConfig = JsoniterSpi.getDefaultConfig().copyBuilder().decodingMode(mode).build(); + JsoniterSpi.setDefaultConfig(newConfig); + JsoniterSpi.setCurrentConfig(newConfig); } public static void enableStreamingSupport() { @@ -461,12 +557,10 @@ public static void enableStreamingSupport() { isStreamingEnabled = true; try { DynamicCodegen.enableStreamingSupport(); + } catch (JsonException e) { + throw e; } catch (Exception e) { throw new JsonException(e); } } - - public static void enableAnnotationSupport() { - JsoniterAnnotationSupport.enable(); - } } diff --git a/src/main/java/com/jsoniter/JsonIteratorPool.java b/src/main/java/com/jsoniter/JsonIteratorPool.java new file mode 100644 index 00000000..f0324d02 --- /dev/null +++ b/src/main/java/com/jsoniter/JsonIteratorPool.java @@ -0,0 +1,35 @@ +package com.jsoniter; + +public class JsonIteratorPool { + + private static ThreadLocal slot1 = new ThreadLocal(); + private static ThreadLocal slot2 = new ThreadLocal(); + + public static JsonIterator borrowJsonIterator() { + JsonIterator iter = slot1.get(); + if (iter != null) { + slot1.set(null); + return iter; + } + iter = slot2.get(); + if (iter != null) { + slot2.set(null); + return iter; + } + iter = JsonIterator.parse(new byte[512], 0, 0); + return iter; + } + + public static void returnJsonIterator(JsonIterator iter) { + iter.configCache = null; + iter.existingObject = null; + if (slot1.get() == null) { + slot1.set(iter); + return; + } + if (slot2.get() == null) { + slot2.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/README.md b/src/main/java/com/jsoniter/README.md index 307c933c..422ab7e4 100644 --- a/src/main/java/com/jsoniter/README.md +++ b/src/main/java/com/jsoniter/README.md @@ -13,4 +13,5 @@ there are 7 packages, listed in abstraction level order * annotation: make spi accessible with annotation. everything here can be done using code * fuzzy: pre-defined decoders to work with messy input -* extra: extra encoders/decoders, useful for a lot of people, but not all of them \ No newline at end of file +* extra: extra encoders/decoders, useful for a lot of people, but not all of them +* static_codegen: command to generate encoder/decoder statically \ No newline at end of file diff --git a/src/main/java/com/jsoniter/ReflectionCollectionDecoder.java b/src/main/java/com/jsoniter/ReflectionCollectionDecoder.java index cee98e66..b82dfe91 100644 --- a/src/main/java/com/jsoniter/ReflectionCollectionDecoder.java +++ b/src/main/java/com/jsoniter/ReflectionCollectionDecoder.java @@ -26,6 +26,8 @@ public ReflectionCollectionDecoder(Class clazz, Type[] typeArgs) { public Object decode(JsonIterator iter) throws IOException { try { return decode_(iter); + } catch (JsonException e) { + throw e; } catch (Exception e) { throw new JsonException(e); } diff --git a/src/main/java/com/jsoniter/ReflectionDecoderFactory.java b/src/main/java/com/jsoniter/ReflectionDecoderFactory.java index 2e31bab3..65dee380 100644 --- a/src/main/java/com/jsoniter/ReflectionDecoderFactory.java +++ b/src/main/java/com/jsoniter/ReflectionDecoderFactory.java @@ -1,13 +1,16 @@ package com.jsoniter; +import com.jsoniter.spi.ClassInfo; import com.jsoniter.spi.Decoder; import java.lang.reflect.Type; import java.util.Collection; import java.util.Map; -public class ReflectionDecoderFactory { - public static Decoder create(Class clazz, Type... typeArgs) { +class ReflectionDecoderFactory { + public static Decoder create(ClassInfo classAndArgs) { + Class clazz = classAndArgs.clazz; + Type[] typeArgs = classAndArgs.typeArgs; if (clazz.isArray()) { return new ReflectionArrayDecoder(clazz); } @@ -20,6 +23,6 @@ public static Decoder create(Class clazz, Type... typeArgs) { if (clazz.isEnum()) { return new ReflectionEnumDecoder(clazz); } - return new ReflectionObjectDecoder(clazz).create(); + return new ReflectionObjectDecoder(classAndArgs).create(); } } diff --git a/src/main/java/com/jsoniter/ReflectionEnumDecoder.java b/src/main/java/com/jsoniter/ReflectionEnumDecoder.java index d2a089e5..7667e9f4 100644 --- a/src/main/java/com/jsoniter/ReflectionEnumDecoder.java +++ b/src/main/java/com/jsoniter/ReflectionEnumDecoder.java @@ -1,6 +1,7 @@ package com.jsoniter; import com.jsoniter.spi.Decoder; +import com.jsoniter.spi.Slice; import java.io.IOException; import java.util.HashMap; diff --git a/src/main/java/com/jsoniter/ReflectionMapDecoder.java b/src/main/java/com/jsoniter/ReflectionMapDecoder.java index f7aab830..7e5f220a 100644 --- a/src/main/java/com/jsoniter/ReflectionMapDecoder.java +++ b/src/main/java/com/jsoniter/ReflectionMapDecoder.java @@ -1,8 +1,6 @@ package com.jsoniter; -import com.jsoniter.spi.Decoder; -import com.jsoniter.spi.JsonException; -import com.jsoniter.spi.TypeLiteral; +import com.jsoniter.spi.*; import java.io.IOException; import java.lang.reflect.Constructor; @@ -13,6 +11,7 @@ class ReflectionMapDecoder implements Decoder { private final Constructor ctor; private final Decoder valueTypeDecoder; + private final Decoder mapKeyDecoder; public ReflectionMapDecoder(Class clazz, Type[] typeArgs) { try { @@ -20,6 +19,8 @@ public ReflectionMapDecoder(Class clazz, Type[] typeArgs) { } catch (NoSuchMethodException e) { throw new JsonException(e); } + Type keyType = typeArgs[0]; + mapKeyDecoder = MapKeyDecoders.registerOrGetExisting(keyType); TypeLiteral valueTypeLiteral = TypeLiteral.create(typeArgs[1]); valueTypeDecoder = Codegen.getDecoder(valueTypeLiteral.getDecoderCacheKey(), typeArgs[1]); } @@ -28,6 +29,8 @@ public ReflectionMapDecoder(Class clazz, Type[] typeArgs) { public Object decode(JsonIterator iter) throws IOException { try { return decode_(iter); + } catch (JsonException e) { + throw e; } catch (Exception e) { throw new JsonException(e); } @@ -45,9 +48,17 @@ private Object decode_(JsonIterator iter) throws Exception { return map; } do { - String field = CodegenAccess.readObjectFieldAsString(iter); - map.put(field, valueTypeDecoder.decode(iter)); + Object decodedMapKey = readMapKey(iter); + map.put(decodedMapKey, valueTypeDecoder.decode(iter)); } while(CodegenAccess.nextToken(iter) == ','); return map; } + + private Object readMapKey(JsonIterator iter) throws IOException { + Object key = mapKeyDecoder.decode(iter); + if (':' != IterImpl.nextToken(iter)) { + throw iter.reportError("readMapKey", "expect :"); + } + return key; + } } diff --git a/src/main/java/com/jsoniter/ReflectionObjectDecoder.java b/src/main/java/com/jsoniter/ReflectionObjectDecoder.java index f134dfa8..e1e76f73 100644 --- a/src/main/java/com/jsoniter/ReflectionObjectDecoder.java +++ b/src/main/java/com/jsoniter/ReflectionObjectDecoder.java @@ -1,8 +1,10 @@ package com.jsoniter; +import com.jsoniter.any.Any; import com.jsoniter.spi.*; import java.io.IOException; +import java.lang.reflect.Method; import java.util.*; class ReflectionObjectDecoder { @@ -22,46 +24,52 @@ public String toString() { private int tempIdx; private ClassDescriptor desc; - public ReflectionObjectDecoder(Class clazz) { + public ReflectionObjectDecoder(ClassInfo classInfo) { try { - init(clazz); + init(classInfo); + } catch (JsonException e) { + throw e; } catch (Exception e) { throw new JsonException(e); } } - private final void init(Class clazz) throws Exception { - ClassDescriptor desc = JsoniterSpi.getDecodingClassDescriptor(clazz, true); + private final void init(ClassInfo classInfo) throws Exception { + Class clazz = classInfo.clazz; + ClassDescriptor desc = ClassDescriptor.getDecodingClassDescriptor(classInfo, true); for (Binding param : desc.ctor.parameters) { - addBinding(clazz, param); + addBinding(classInfo, param); } this.desc = desc; if (desc.ctor.objectFactory == null && desc.ctor.ctor == null && desc.ctor.staticFactory == null) { throw new JsonException("no constructor for: " + desc.clazz); } for (Binding field : desc.fields) { - addBinding(clazz, field); + addBinding(classInfo, field); } for (Binding setter : desc.setters) { - addBinding(clazz, setter); + addBinding(classInfo, setter); } - for (WrapperDescriptor setter : desc.wrappers) { + for (WrapperDescriptor setter : desc.bindingTypeWrappers) { for (Binding param : setter.parameters) { - addBinding(clazz, param); + addBinding(classInfo, param); } } if (requiredIdx > 63) { throw new JsonException("too many required properties to track"); } expectedTracker = Long.MAX_VALUE >> (63 - requiredIdx); - if (!desc.ctor.parameters.isEmpty() || !desc.wrappers.isEmpty()) { + if (!desc.ctor.parameters.isEmpty() || !desc.bindingTypeWrappers.isEmpty()) { tempCount = tempIdx; tempCacheKey = "temp@" + clazz.getCanonicalName(); ctorArgsCacheKey = "ctor@" + clazz.getCanonicalName(); } } - private void addBinding(Class clazz, final Binding binding) { + private void addBinding(ClassInfo classInfo, final Binding binding) { + if (binding.fromNames.length == 0) { + return; + } if (binding.asMissingWhenNotPresent) { binding.mask = 1L << requiredIdx; requiredIdx++; @@ -85,7 +93,7 @@ public Object decode(JsonIterator iter) throws IOException { for (String fromName : binding.fromNames) { Slice slice = Slice.make(fromName); if (allBindings.containsKey(slice)) { - throw new JsonException("name conflict found in " + clazz + ": " + fromName); + throw new JsonException("name conflict found in " + classInfo.clazz + ": " + fromName); } allBindings.put(slice, binding); } @@ -94,10 +102,10 @@ public Object decode(JsonIterator iter) throws IOException { public Decoder create() { if (desc.ctor.parameters.isEmpty()) { - if (desc.wrappers.isEmpty()) { + if (desc.bindingTypeWrappers.isEmpty()) { return new OnlyField(); } else { - return new WithSetter(); + return new WithWrapper(); } } else { return new WithCtor(); @@ -109,6 +117,8 @@ public class OnlyField implements Decoder { public Object decode(JsonIterator iter) throws IOException { try { return decode_(iter); + } catch (RuntimeException e) { + throw e; } catch (Exception e) { throw new JsonException(e); } @@ -172,6 +182,8 @@ public class WithCtor implements Decoder { public Object decode(JsonIterator iter) throws IOException { try { return decode_(iter); + } catch (RuntimeException e) { + throw e; } catch (Exception e) { throw new JsonException(e); } @@ -228,7 +240,7 @@ private Object decode_(JsonIterator iter) throws Exception { setExtra(obj, extra); for (Binding field : desc.fields) { Object val = temp[field.idx]; - if (val != NOT_SET) { + if (val != NOT_SET && field.fromNames.length > 0) { field.field.set(obj, val); } } @@ -243,12 +255,14 @@ private Object decode_(JsonIterator iter) throws Exception { } } - public class WithSetter implements Decoder { + public class WithWrapper implements Decoder { @Override public Object decode(JsonIterator iter) throws IOException { try { return decode_(iter); + } catch (RuntimeException e) { + throw e; } catch (Exception e) { throw new JsonException(e); } @@ -333,8 +347,23 @@ private void setToBinding(Object obj, Binding binding, Object value) throws Exce } private void setExtra(Object obj, Map extra) throws Exception { - if (desc.onExtraProperties != null) { - setToBinding(obj, desc.onExtraProperties, extra); + if (extra == null) { + return; + } + if (desc.asExtraForUnknownProperties) { + if (desc.onExtraProperties == null) { + for (String fieldName : extra.keySet()) { + throw new JsonException("unknown property: " + fieldName); + } + } else { + setToBinding(obj, desc.onExtraProperties, extra); + } + } + for (Method wrapper : desc.keyValueTypeWrappers) { + for (Map.Entry entry : extra.entrySet()) { + Any value = (Any) entry.getValue(); + wrapper.invoke(obj, entry.getKey(), value.object()); + } } } @@ -356,15 +385,13 @@ private Object decodeBinding(JsonIterator iter, Object obj, Binding binding) thr } private Map onUnknownProperty(JsonIterator iter, Slice fieldName, Map extra) throws IOException { - if (desc.asExtraForUnknownProperties) { - if (desc.onExtraProperties == null) { - throw new JsonException("unknown property: " + fieldName.toString()); - } else { - if (extra == null) { - extra = new HashMap(); - } - extra.put(fieldName.toString(), iter.readAny()); + boolean shouldReadValue = desc.asExtraForUnknownProperties || !desc.keyValueTypeWrappers.isEmpty(); + if (shouldReadValue) { + Any value = iter.readAny(); + if (extra == null) { + extra = new HashMap(); } + extra.put(fieldName.toString(), value); } else { iter.skip(); } @@ -383,10 +410,13 @@ private List collectMissingFields(long tracker) { } private void applyWrappers(Object[] temp, Object obj) throws Exception { - for (WrapperDescriptor wrapper : desc.wrappers) { + for (WrapperDescriptor wrapper : desc.bindingTypeWrappers) { Object[] args = new Object[wrapper.parameters.size()]; for (int i = 0; i < wrapper.parameters.size(); i++) { - args[i] = temp[wrapper.parameters.get(i).idx]; + Object arg = temp[wrapper.parameters.get(i).idx]; + if (arg != NOT_SET) { + args[i] = arg; + } } wrapper.method.invoke(obj, args); } diff --git a/src/main/java/com/jsoniter/StaticCodeGenerator.java b/src/main/java/com/jsoniter/StaticCodeGenerator.java deleted file mode 100644 index 8818b305..00000000 --- a/src/main/java/com/jsoniter/StaticCodeGenerator.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.jsoniter; - -import com.jsoniter.output.EncodingMode; -import com.jsoniter.output.JsonStream; -import com.jsoniter.spi.CodegenConfig; -import com.jsoniter.spi.JsonException; - -import java.io.File; - -public class StaticCodeGenerator { - public static void main(String[] args) throws Exception { - String configClassName = args[0]; - String configJavaFile = configClassName.replace('.', '/') + ".java"; - if (!new File(configJavaFile).exists()) { - throw new JsonException("must execute static code generator in the java source code directory which contains: " + configJavaFile); - } - Class clazz = Class.forName(configClassName); - CodegenConfig config = (CodegenConfig) clazz.newInstance(); - JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); - JsonStream.setMode(EncodingMode.DYNAMIC_MODE); - config.setup(); - CodegenAccess.staticGenDecoders(config.whatToCodegen()); - com.jsoniter.output.CodegenAccess.staticGenEncoders(config.whatToCodegen()); - } -} diff --git a/src/main/java/com/jsoniter/annotation/JsonIgnore.java b/src/main/java/com/jsoniter/annotation/JsonIgnore.java index 68d19207..bcb04cd2 100644 --- a/src/main/java/com/jsoniter/annotation/JsonIgnore.java +++ b/src/main/java/com/jsoniter/annotation/JsonIgnore.java @@ -8,5 +8,6 @@ @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface JsonIgnore { - boolean value() default true; + boolean ignoreDecoding() default true; + boolean ignoreEncoding() default true; } diff --git a/src/main/java/com/jsoniter/annotation/JsonProperty.java b/src/main/java/com/jsoniter/annotation/JsonProperty.java index a0bab421..2bd545e5 100644 --- a/src/main/java/com/jsoniter/annotation/JsonProperty.java +++ b/src/main/java/com/jsoniter/annotation/JsonProperty.java @@ -60,7 +60,11 @@ boolean collectionValueNullable() default true; /** - * @return if true, do not write the field altogether if value is null + * @return the default value to omit + * null, to omit null value + * \"xxx\", to omit string value + * 123, to omit number + * void, to always encode this field, ignore global config */ - boolean omitNull() default true; + String defaultValueToOmit() default ""; } diff --git a/src/main/java/com/jsoniter/annotation/JsonWrapper.java b/src/main/java/com/jsoniter/annotation/JsonWrapper.java index 0749ab7b..7fdb4e87 100644 --- a/src/main/java/com/jsoniter/annotation/JsonWrapper.java +++ b/src/main/java/com/jsoniter/annotation/JsonWrapper.java @@ -8,4 +8,5 @@ @Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface JsonWrapper { + JsonWrapperType value() default JsonWrapperType.BINDING; } diff --git a/src/main/java/com/jsoniter/annotation/JsonWrapperType.java b/src/main/java/com/jsoniter/annotation/JsonWrapperType.java new file mode 100644 index 00000000..011c1a26 --- /dev/null +++ b/src/main/java/com/jsoniter/annotation/JsonWrapperType.java @@ -0,0 +1,6 @@ +package com.jsoniter.annotation; + +public enum JsonWrapperType { + BINDING, + KEY_VALUE +} diff --git a/src/main/java/com/jsoniter/annotation/JsoniterAnnotationSupport.java b/src/main/java/com/jsoniter/annotation/JsoniterAnnotationSupport.java deleted file mode 100644 index b15e0a92..00000000 --- a/src/main/java/com/jsoniter/annotation/JsoniterAnnotationSupport.java +++ /dev/null @@ -1,257 +0,0 @@ -package com.jsoniter.annotation; - -import com.jsoniter.spi.JsonException; -import com.jsoniter.spi.*; - -import java.lang.annotation.Annotation; -import java.lang.reflect.*; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class JsoniterAnnotationSupport extends EmptyExtension { - - private static boolean enabled = false; - - public static void enable() { - if (enabled) { - return; - } - enabled = true; - JsoniterSpi.registerExtension(new JsoniterAnnotationSupport()); - } - - @Override - public void updateClassDescriptor(ClassDescriptor desc) { - JsonObject jsonObject = (JsonObject) desc.clazz.getAnnotation(JsonObject.class); - if (jsonObject != null) { - if (jsonObject.asExtraForUnknownProperties()) { - desc.asExtraForUnknownProperties = true; - } - for (String fieldName : jsonObject.unknownPropertiesWhitelist()) { - Binding binding = new Binding(desc.clazz, desc.lookup, Object.class); - binding.name = fieldName; - binding.shouldSkip = true; - desc.fields.add(binding); - } - for (String fieldName : jsonObject.unknownPropertiesBlacklist()) { - Binding binding = new Binding(desc.clazz, desc.lookup, Object.class); - binding.name = fieldName; - binding.asExtraWhenPresent = true; - desc.fields.add(binding); - } - } - List allMethods = new ArrayList(); - Class current = desc.clazz; - while (current != null) { - allMethods.addAll(Arrays.asList(current.getDeclaredMethods())); - current = current.getSuperclass(); - } - updateBindings(desc); - detectCtor(desc); - detectStaticFactory(desc, allMethods); - detectWrappers(desc, allMethods); - detectUnwrappers(desc, allMethods); - } - - private void detectUnwrappers(ClassDescriptor desc, List allMethods) { - for (Method method : allMethods) { - if (Modifier.isStatic(method.getModifiers())) { - continue; - } - if (method.getAnnotation(JsonUnwrapper.class) == null) { - continue; - } - desc.unWrappers.add(method); - } - } - - private void detectWrappers(ClassDescriptor desc, List allMethods) { - for (Method method : allMethods) { - if (Modifier.isStatic(method.getModifiers())) { - continue; - } - if (method.getAnnotation(JsonWrapper.class) == null) { - continue; - } - Annotation[][] annotations = method.getParameterAnnotations(); - String[] paramNames = getParamNames(method, annotations.length); - WrapperDescriptor setter = new WrapperDescriptor(); - setter.method = method; - for (int i = 0; i < annotations.length; i++) { - Annotation[] paramAnnotations = annotations[i]; - Binding binding = new Binding(desc.clazz, desc.lookup, method.getGenericParameterTypes()[i]); - JsonProperty jsonProperty = getJsonProperty(paramAnnotations); - if (jsonProperty != null) { - updateBindingWithJsonProperty(binding, jsonProperty); - } - if (binding.name == null || binding.name.length() == 0) { - binding.name = paramNames[i]; - } - binding.annotations = paramAnnotations; - setter.parameters.add(binding); - } - desc.wrappers.add(setter); - } - } - - private String[] getParamNames(Object obj, int paramCount) { - String[] paramNames = new String[paramCount]; - try { - Object params = reflectCall(obj, "getParameters"); - for (int i = 0; i < paramNames.length; i++) { - paramNames[i] = (String) reflectCall(Array.get(params, i), "getName"); - } - } catch (Exception e) { - } - return paramNames; - } - - private Object reflectCall(Object obj, String methodName, Object... args) throws Exception { - Method method = obj.getClass().getMethod(methodName); - return method.invoke(obj, args); - } - - private void detectStaticFactory(ClassDescriptor desc, List allMethods) { - for (Method method : allMethods) { - if (!Modifier.isStatic(method.getModifiers())) { - continue; - } - JsonCreator jsonCreator = getJsonCreator(method.getAnnotations()); - if (jsonCreator == null) { - continue; - } - desc.ctor.staticMethodName = method.getName(); - desc.ctor.staticFactory = method; - desc.ctor.ctor = null; - Annotation[][] annotations = method.getParameterAnnotations(); - String[] paramNames = getParamNames(method, annotations.length); - for (int i = 0; i < annotations.length; i++) { - Annotation[] paramAnnotations = annotations[i]; - JsonProperty jsonProperty = getJsonProperty(paramAnnotations); - Binding binding = new Binding(desc.clazz, desc.lookup, method.getGenericParameterTypes()[i]); - if (jsonProperty != null) { - updateBindingWithJsonProperty(binding, jsonProperty); - } - if (binding.name == null || binding.name.length() == 0) { - binding.name = paramNames[i]; - } - binding.annotations = paramAnnotations; - desc.ctor.parameters.add(binding); - } - } - } - - private void detectCtor(ClassDescriptor desc) { - for (Constructor ctor : desc.clazz.getDeclaredConstructors()) { - JsonCreator jsonCreator = getJsonCreator(ctor.getAnnotations()); - if (jsonCreator == null) { - continue; - } - desc.ctor.staticMethodName = null; - desc.ctor.ctor = ctor; - desc.ctor.staticFactory = null; - Annotation[][] annotations = ctor.getParameterAnnotations(); - String[] paramNames = getParamNames(ctor, annotations.length); - for (int i = 0; i < annotations.length; i++) { - Annotation[] paramAnnotations = annotations[i]; - JsonProperty jsonProperty = getJsonProperty(paramAnnotations); - Binding binding = new Binding(desc.clazz, desc.lookup, ctor.getGenericParameterTypes()[i]); - if (jsonProperty != null) { - updateBindingWithJsonProperty(binding, jsonProperty); - } - if (binding.name == null || binding.name.length() == 0) { - binding.name = paramNames[i]; - } - binding.annotations = paramAnnotations; - desc.ctor.parameters.add(binding); - } - } - } - - private void updateBindings(ClassDescriptor desc) { - for (Binding binding : desc.allBindings()) { - JsonIgnore jsonIgnore = getJsonIgnore(binding.annotations); - if (jsonIgnore != null && jsonIgnore.value()) { - binding.fromNames = new String[0]; - binding.toNames = new String[0]; - } - JsonProperty jsonProperty = getJsonProperty(binding.annotations); - if (jsonProperty != null) { - updateBindingWithJsonProperty(binding, jsonProperty); - } - if (getAnnotation(binding.annotations, JsonMissingProperties.class) != null) { - // this binding will not bind from json - // instead it will be set by jsoniter with missing property names - binding.fromNames = new String[0]; - desc.onMissingProperties = binding; - } - if (getAnnotation(binding.annotations, JsonExtraProperties.class) != null) { - // this binding will not bind from json - // instead it will be set by jsoniter with extra properties - binding.fromNames = new String[0]; - desc.onExtraProperties = binding; - } - } - } - - private void updateBindingWithJsonProperty(Binding binding, JsonProperty jsonProperty) { - binding.asMissingWhenNotPresent = jsonProperty.required(); - binding.isNullable = jsonProperty.nullable(); - binding.isCollectionValueNullable = jsonProperty.collectionValueNullable(); - binding.shouldOmitNull = jsonProperty.omitNull(); - String altName = jsonProperty.value(); - if (!altName.isEmpty()) { - binding.name = altName; - binding.fromNames = new String[]{altName}; - } - if (jsonProperty.from().length > 0) { - binding.fromNames = jsonProperty.from(); - } - if (jsonProperty.to().length > 0) { - binding.toNames = jsonProperty.to(); - } - if (jsonProperty.decoder() != Decoder.class) { - try { - binding.decoder = jsonProperty.decoder().newInstance(); - } catch (Exception e) { - throw new JsonException(e); - } - } - if (jsonProperty.encoder() != Encoder.class) { - try { - binding.encoder = jsonProperty.encoder().newInstance(); - } catch (Exception e) { - throw new JsonException(e); - } - } - if (jsonProperty.implementation() != Object.class) { - binding.valueType = ParameterizedTypeImpl.useImpl(binding.valueType, jsonProperty.implementation()); - binding.valueTypeLiteral = TypeLiteral.create(binding.valueType); - } - } - - protected JsonCreator getJsonCreator(Annotation[] annotations) { - return getAnnotation(annotations, JsonCreator.class); - } - - protected JsonProperty getJsonProperty(Annotation[] annotations) { - return getAnnotation(annotations, JsonProperty.class); - } - - protected JsonIgnore getJsonIgnore(Annotation[] annotations) { - return getAnnotation(annotations, JsonIgnore.class); - } - - protected static T getAnnotation(Annotation[] annotations, Class annotationClass) { - if (annotations == null) { - return null; - } - for (Annotation annotation : annotations) { - if (annotationClass.isAssignableFrom(annotation.getClass())) { - return (T) annotation; - } - } - return null; - } -} diff --git a/src/main/java/com/jsoniter/any/Any.java b/src/main/java/com/jsoniter/any/Any.java index f67f0d88..8159f1c2 100644 --- a/src/main/java/com/jsoniter/any/Any.java +++ b/src/main/java/com/jsoniter/any/Any.java @@ -2,19 +2,24 @@ import com.jsoniter.output.CodegenAccess; import com.jsoniter.spi.JsonException; -import com.jsoniter.JsonIterator; import com.jsoniter.ValueType; import com.jsoniter.output.JsonStream; import com.jsoniter.spi.Encoder; 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 { static { - Encoder anyEncoder = new Encoder() { + registerEncoders(); + } + + public static void registerEncoders() { + Encoder.ReflectionEncoder anyEncoder = new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { Any any = (Any) obj; @@ -171,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(); } @@ -181,7 +194,15 @@ public int size() { return 0; } - public Set keys() { + public Any mustBeValid() { + if(this instanceof NotFoundAny) { + throw ((NotFoundAny) this).exception; + } else { + return this; + } + } + + public Set keys() { return EMPTY_KEYS; } @@ -233,10 +254,6 @@ public Any set(String newVal) { return wrap(newVal); } - public JsonIterator parse() { - throw new UnsupportedOperationException(); - } - public abstract void writeTo(JsonStream stream) throws IOException; protected JsonException reportUnexpectedType(ValueType toType) { @@ -337,4 +354,22 @@ public static Any rewrap(Map val) { protected boolean isWildcard(Object key) { return wildcardHashCode == key.hashCode() && wildcard.equals(key); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Any any = (Any) o; + + Object obj = this.object(); + Object thatObj = any.object(); + return obj != null ? obj.equals(thatObj) : thatObj == null; + } + + @Override + public int hashCode() { + Object obj = this.object(); + return obj != null ? obj.hashCode() : 0; + } } 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 67091d26..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; @@ -35,10 +37,13 @@ public Object object() { @Override public boolean toBoolean() { + JsonIterator iter = parse(); try { - return CodegenAccess.readArrayStart(parse()); + return CodegenAccess.readArrayStart(iter); } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } } @@ -62,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(); @@ -119,8 +134,8 @@ private void fillCache() { if (cache == null) { cache = new ArrayList(4); } + JsonIterator iter = JsonIteratorPool.borrowJsonIterator(); try { - JsonIterator iter = JsonIterator.tlsIter.get(); iter.reset(data, lastParsedPos, tail); if (lastParsedPos == head) { if (!CodegenAccess.readArrayStart(iter)) { @@ -135,6 +150,8 @@ private void fillCache() { lastParsedPos = tail; } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } } @@ -149,8 +166,8 @@ private Any fillCacheUntil(int target) { if (target < i) { return cache.get(target); } + JsonIterator iter = JsonIteratorPool.borrowJsonIterator(); try { - JsonIterator iter = JsonIterator.tlsIter.get(); iter.reset(data, lastParsedPos, tail); if (lastParsedPos == head) { if (!CodegenAccess.readArrayStart(iter)) { @@ -176,6 +193,8 @@ private Any fillCacheUntil(int target) { lastParsedPos = tail; } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } throw new IndexOutOfBoundsException(); } @@ -187,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 b9937843..64bcb88d 100644 --- a/src/main/java/com/jsoniter/any/DoubleAny.java +++ b/src/main/java/com/jsoniter/any/DoubleAny.java @@ -2,9 +2,10 @@ import com.jsoniter.ValueType; import com.jsoniter.output.JsonStream; -import com.jsoniter.spi.TypeLiteral; import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; class DoubleAny extends Any { @@ -49,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 bfd20a79..edc32774 100644 --- a/src/main/java/com/jsoniter/any/DoubleLazyAny.java +++ b/src/main/java/com/jsoniter/any/DoubleLazyAny.java @@ -1,9 +1,13 @@ package com.jsoniter.any; +import com.jsoniter.JsonIterator; +import com.jsoniter.JsonIteratorPool; import com.jsoniter.spi.JsonException; import com.jsoniter.ValueType; import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; class DoubleLazyAny extends LazyAny { @@ -55,12 +59,25 @@ 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(); try { - cache = parse().readDouble(); + cache = iter.readDouble(); } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } isCached = true; } 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 8f9d2b84..d088241e 100644 --- a/src/main/java/com/jsoniter/any/LazyAny.java +++ b/src/main/java/com/jsoniter/any/LazyAny.java @@ -1,5 +1,6 @@ package com.jsoniter.any; +import com.jsoniter.JsonIteratorPool; import com.jsoniter.spi.JsonException; import com.jsoniter.JsonIterator; import com.jsoniter.ValueType; @@ -23,43 +24,55 @@ public LazyAny(byte[] data, int head, int tail) { public abstract ValueType valueType(); public final T bindTo(T obj) { + JsonIterator iter = parse(); try { - return parse().read(obj); + return iter.read(obj); } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } } public final T bindTo(TypeLiteral typeLiteral, T obj) { + JsonIterator iter = parse(); try { - return parse().read(typeLiteral, obj); + return iter.read(typeLiteral, obj); } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } } public final T as(Class clazz) { + JsonIterator iter = parse(); try { - return parse().read(clazz); + return iter.read(clazz); } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } } public final T as(TypeLiteral typeLiteral) { + JsonIterator iter = parse(); try { - return parse().read(typeLiteral); + return iter.read(typeLiteral); } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } } public String toString() { - return new String(data, head, tail - head); + return new String(data, head, tail - head).trim(); } - public final JsonIterator parse() { - JsonIterator iter = JsonIterator.tlsIter.get(); + protected final JsonIterator parse() { + JsonIterator iter = JsonIteratorPool.borrowJsonIterator(); iter.reset(data, head, tail); return iter; } 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 283b2f7c..76f683eb 100644 --- a/src/main/java/com/jsoniter/any/LongAny.java +++ b/src/main/java/com/jsoniter/any/LongAny.java @@ -2,9 +2,10 @@ import com.jsoniter.ValueType; import com.jsoniter.output.JsonStream; -import com.jsoniter.spi.TypeLiteral; import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; class LongAny extends Any { @@ -49,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 91544c80..3a60dc74 100644 --- a/src/main/java/com/jsoniter/any/LongLazyAny.java +++ b/src/main/java/com/jsoniter/any/LongLazyAny.java @@ -1,9 +1,13 @@ package com.jsoniter.any; +import com.jsoniter.JsonIterator; +import com.jsoniter.JsonIteratorPool; import com.jsoniter.spi.JsonException; import com.jsoniter.ValueType; import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; class LongLazyAny extends LazyAny { @@ -55,12 +59,25 @@ 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(); try { - cache = parse().readLong(); + cache = iter.readLong(); } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } isCached = true; } 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 3cc996f4..1a5439ea 100644 --- a/src/main/java/com/jsoniter/any/NotFoundAny.java +++ b/src/main/java/com/jsoniter/any/NotFoundAny.java @@ -5,11 +5,13 @@ 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 { - private final JsonException exception; + protected final JsonException exception; public NotFoundAny(Object[] keys, int idx, Object obj) { this.exception = new JsonException(String.format("Value not found: failed to get path %s, because #%s section of the path ( %s ) not found in %s", @@ -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 6f8cf81f..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; @@ -36,7 +38,12 @@ public Object object() { @Override public boolean toBoolean() { try { - return CodegenAccess.readObjectStart(parse()); + JsonIterator iter = parse(); + try { + return CodegenAccess.readObjectStart(iter); + } finally { + JsonIteratorPool.returnJsonIterator(iter); + } } catch (IOException e) { throw new JsonException(e); } @@ -62,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(); @@ -118,8 +135,8 @@ private Any fillCacheUntil(Object target) { if (value != null) { return value; } + JsonIterator iter = JsonIteratorPool.borrowJsonIterator(); try { - JsonIterator iter = JsonIterator.tlsIter.get(); iter.reset(data, lastParsedPos, tail); if (lastParsedPos == head) { if (!CodegenAccess.readObjectStart(iter)) { @@ -147,6 +164,8 @@ private Any fillCacheUntil(Object target) { return null; } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } } @@ -157,8 +176,8 @@ private void fillCache() { if (cache == null) { cache = new HashMap(4); } + JsonIterator iter = JsonIteratorPool.borrowJsonIterator(); try { - JsonIterator iter = JsonIterator.tlsIter.get(); iter.reset(data, lastParsedPos, tail); if (lastParsedPos == head) { if (!CodegenAccess.readObjectStart(iter)) { @@ -175,6 +194,8 @@ private void fillCache() { lastParsedPos = tail; } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } } @@ -197,12 +218,16 @@ public LazyIterator() { mapIter = new HashMap(cache).entrySet().iterator(); try { if (lastParsedPos == head) { - JsonIterator iter = JsonIterator.tlsIter.get(); - iter.reset(data, lastParsedPos, tail); - if (!CodegenAccess.readObjectStart(iter)) { - lastParsedPos = tail; - } else { - lastParsedPos = CodegenAccess.head(iter); + JsonIterator iter = JsonIteratorPool.borrowJsonIterator(); + try { + iter.reset(data, lastParsedPos, tail); + if (!CodegenAccess.readObjectStart(iter)) { + lastParsedPos = tail; + } else { + lastParsedPos = CodegenAccess.head(iter); + } + } finally { + JsonIteratorPool.returnJsonIterator(iter); } } } catch (IOException e) { @@ -225,8 +250,8 @@ public boolean next() { mapIter = null; } } + JsonIterator iter = JsonIteratorPool.borrowJsonIterator(); try { - JsonIterator iter = JsonIterator.tlsIter.get(); iter.reset(data, lastParsedPos, tail); key = CodegenAccess.readObjectFieldAsString(iter); value = iter.readAny(); @@ -238,6 +263,8 @@ public boolean next() { } } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } return true; } 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 0cf88a75..4e9f3bab 100644 --- a/src/main/java/com/jsoniter/any/StringLazyAny.java +++ b/src/main/java/com/jsoniter/any/StringLazyAny.java @@ -1,11 +1,14 @@ package com.jsoniter.any; import com.jsoniter.CodegenAccess; -import com.jsoniter.spi.JsonException; import com.jsoniter.JsonIterator; +import com.jsoniter.JsonIteratorPool; import com.jsoniter.ValueType; +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"; @@ -52,48 +55,66 @@ public boolean toBoolean() { @Override public int toInt() { + JsonIterator iter = parse(); try { - JsonIterator iter = parse(); CodegenAccess.nextToken(iter); return iter.readInt(); } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } } @Override public long toLong() { + JsonIterator iter = parse(); try { - JsonIterator iter = parse(); CodegenAccess.nextToken(iter); return iter.readLong(); } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } } @Override public float toFloat() { + JsonIterator iter = parse(); try { - JsonIterator iter = parse(); CodegenAccess.nextToken(iter); return iter.readFloat(); } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } } @Override public double toDouble() { + JsonIterator iter = parse(); try { - JsonIterator iter = parse(); CodegenAccess.nextToken(iter); return iter.readDouble(); } catch (IOException e) { throw new JsonException(e); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } } + @Override + public BigInteger toBigInteger() { + return new BigInteger(toString()); + } + + @Override + public BigDecimal toBigDecimal() { + return new BigDecimal(toString()); + } + @Override public String toString() { fillCache(); @@ -102,10 +123,13 @@ public String toString() { private void fillCache() { if (cache == null) { + JsonIterator iter = parse(); try { - cache = parse().readString(); + cache = iter.readString(); } catch (IOException e) { throw new JsonException(); + } finally { + JsonIteratorPool.returnJsonIterator(iter); } } } 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/Base64.java b/src/main/java/com/jsoniter/extra/Base64.java index e6f07599..e09d910b 100644 --- a/src/main/java/com/jsoniter/extra/Base64.java +++ b/src/main/java/com/jsoniter/extra/Base64.java @@ -1,7 +1,7 @@ package com.jsoniter.extra; import com.jsoniter.JsonIterator; -import com.jsoniter.Slice; +import com.jsoniter.spi.Slice; import com.jsoniter.output.JsonStream; import java.io.IOException; diff --git a/src/main/java/com/jsoniter/extra/Base64FloatSupport.java b/src/main/java/com/jsoniter/extra/Base64FloatSupport.java index 6753889a..b2fdf732 100644 --- a/src/main/java/com/jsoniter/extra/Base64FloatSupport.java +++ b/src/main/java/com/jsoniter/extra/Base64FloatSupport.java @@ -2,7 +2,7 @@ import com.jsoniter.CodegenAccess; import com.jsoniter.JsonIterator; -import com.jsoniter.Slice; +import com.jsoniter.spi.Slice; import com.jsoniter.any.Any; import com.jsoniter.output.JsonStream; import com.jsoniter.spi.Decoder; @@ -51,7 +51,7 @@ public static synchronized void enableEncodersAndDecoders() { } enabled = true; enableDecoders(); - JsoniterSpi.registerTypeEncoder(Double.class, new Encoder() { + JsoniterSpi.registerTypeEncoder(Double.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { Double number = (Double) obj; @@ -72,7 +72,7 @@ public void encodeDouble(double obj, JsonStream stream) throws IOException { Base64.encodeLongBits(bits, stream); } }); - JsoniterSpi.registerTypeEncoder(Float.class, new Encoder() { + JsoniterSpi.registerTypeEncoder(Float.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { Float number = (Float) obj; diff --git a/src/main/java/com/jsoniter/extra/Base64Support.java b/src/main/java/com/jsoniter/extra/Base64Support.java index 9833697e..676178f7 100644 --- a/src/main/java/com/jsoniter/extra/Base64Support.java +++ b/src/main/java/com/jsoniter/extra/Base64Support.java @@ -1,8 +1,7 @@ package com.jsoniter.extra; import com.jsoniter.JsonIterator; -import com.jsoniter.Slice; -import com.jsoniter.any.Any; +import com.jsoniter.spi.Slice; import com.jsoniter.output.JsonStream; import com.jsoniter.spi.Decoder; import com.jsoniter.spi.Encoder; @@ -12,7 +11,7 @@ import java.io.IOException; /** - * byte[] <=> base64 + * byte[] <=> base64 */ public class Base64Support { private static boolean enabled; @@ -36,11 +35,6 @@ public void encode(Object obj, JsonStream stream) throws IOException { Base64.encodeToBytes(bytes, stream); stream.write('"'); } - - @Override - public Any wrap(Object obj) { - return null; - } }); } } diff --git a/src/main/java/com/jsoniter/extra/GsonCompatibilityMode.java b/src/main/java/com/jsoniter/extra/GsonCompatibilityMode.java new file mode 100644 index 00000000..7d6b6a63 --- /dev/null +++ b/src/main/java/com/jsoniter/extra/GsonCompatibilityMode.java @@ -0,0 +1,611 @@ +package com.jsoniter.extra; + +import com.google.gson.ExclusionStrategy; +import com.google.gson.FieldAttributes; +import com.google.gson.FieldNamingPolicy; +import com.google.gson.FieldNamingStrategy; +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import com.google.gson.annotations.Since; +import com.google.gson.annotations.Until; +import com.jsoniter.JsonIterator; +import com.jsoniter.ValueType; +import com.jsoniter.annotation.JsonIgnore; +import com.jsoniter.annotation.JsonProperty; +import com.jsoniter.output.JsonStream; +import com.jsoniter.spi.*; + +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +public class GsonCompatibilityMode extends Config { + + private final static int SURR1_FIRST = 0xD800; + private final static int SURR1_LAST = 0xDBFF; + private final static int SURR2_FIRST = 0xDC00; + private final static int SURR2_LAST = 0xDFFF; + private static final String[] REPLACEMENT_CHARS; + private static final String[] HTML_SAFE_REPLACEMENT_CHARS; + + static { + REPLACEMENT_CHARS = new String[128]; + for (int i = 0; i <= 0x1f; i++) { + REPLACEMENT_CHARS[i] = String.format("\\u%04x", (int) i); + } + REPLACEMENT_CHARS['"'] = "\\\""; + REPLACEMENT_CHARS['\\'] = "\\\\"; + REPLACEMENT_CHARS['\t'] = "\\t"; + REPLACEMENT_CHARS['\b'] = "\\b"; + REPLACEMENT_CHARS['\n'] = "\\n"; + REPLACEMENT_CHARS['\r'] = "\\r"; + REPLACEMENT_CHARS['\f'] = "\\f"; + HTML_SAFE_REPLACEMENT_CHARS = REPLACEMENT_CHARS.clone(); + HTML_SAFE_REPLACEMENT_CHARS['<'] = "\\u003c"; + HTML_SAFE_REPLACEMENT_CHARS['>'] = "\\u003e"; + HTML_SAFE_REPLACEMENT_CHARS['&'] = "\\u0026"; + HTML_SAFE_REPLACEMENT_CHARS['='] = "\\u003d"; + HTML_SAFE_REPLACEMENT_CHARS['\''] = "\\u0027"; + } + + private GsonCompatibilityMode(String configName, Builder builder) { + super(configName, builder); + } + + protected Builder builder() { + return (Builder) super.builder(); + } + + public static class Builder extends Config.Builder { + private boolean excludeFieldsWithoutExposeAnnotation = false; + private boolean disableHtmlEscaping = false; + private ThreadLocal dateFormat = new ThreadLocal() { + @Override + protected DateFormat initialValue() { + return DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US); + } + }; + private FieldNamingStrategy fieldNamingStrategy; + private Double version; + private Set serializationExclusionStrategies = new HashSet(); + private Set deserializationExclusionStrategies = new HashSet(); + + public Builder() { + omitDefaultValue(true); + } + + public Builder excludeFieldsWithoutExposeAnnotation() { + excludeFieldsWithoutExposeAnnotation = true; + return this; + } + + public Builder serializeNulls() { + omitDefaultValue(false); + return this; + } + + public Builder setDateFormat(int dateStyle) { + // no op, same as gson + return this; + } + + public Builder setDateFormat(final int dateStyle, final int timeStyle) { + dateFormat = new ThreadLocal() { + @Override + protected DateFormat initialValue() { + return DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US); + } + }; + return this; + } + + public Builder setDateFormat(final String pattern) { + dateFormat = new ThreadLocal() { + @Override + protected DateFormat initialValue() { + return new SimpleDateFormat(pattern, Locale.US); + } + }; + return this; + } + + public Builder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) { + this.fieldNamingStrategy = fieldNamingStrategy; + return this; + } + + public Builder setFieldNamingPolicy(FieldNamingPolicy namingConvention) { + this.fieldNamingStrategy = namingConvention; + return this; + } + + public Builder setPrettyPrinting() { + indentionStep(2); + return this; + } + + public Builder disableHtmlEscaping() { + disableHtmlEscaping = true; + return this; + } + + public Builder setVersion(double version) { + this.version = version; + return this; + } + + public Builder setExclusionStrategies(ExclusionStrategy... strategies) { + for (ExclusionStrategy strategy : strategies) { + addSerializationExclusionStrategy(strategy); + } + return this; + } + + public Builder addSerializationExclusionStrategy(ExclusionStrategy exclusionStrategy) { + serializationExclusionStrategies.add(exclusionStrategy); + return this; + } + + public Builder addDeserializationExclusionStrategy(ExclusionStrategy exclusionStrategy) { + deserializationExclusionStrategies.add(exclusionStrategy); + return this; + } + + public GsonCompatibilityMode build() { + escapeUnicode(false); + return (GsonCompatibilityMode) super.build(); + } + + @Override + protected Config doBuild(String configName) { + return new GsonCompatibilityMode(configName, this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + + Builder builder = (Builder) o; + + return excludeFieldsWithoutExposeAnnotation == builder.excludeFieldsWithoutExposeAnnotation && + disableHtmlEscaping == builder.disableHtmlEscaping && + dateFormat.get().equals(builder.dateFormat.get()) && + (fieldNamingStrategy != null ? fieldNamingStrategy.equals(builder.fieldNamingStrategy) : + builder.fieldNamingStrategy == null) && + (version != null ? version.equals(builder.version) : builder.version == null) && + (serializationExclusionStrategies != null ? + serializationExclusionStrategies.equals(builder.serializationExclusionStrategies) : + builder.serializationExclusionStrategies == null) && + (deserializationExclusionStrategies != null ? + deserializationExclusionStrategies.equals(builder.deserializationExclusionStrategies) : + builder.deserializationExclusionStrategies == null); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (excludeFieldsWithoutExposeAnnotation ? 1 : 0); + result = 31 * result + (disableHtmlEscaping ? 1 : 0); + result = 31 * result + dateFormat.get().hashCode(); + result = 31 * result + (fieldNamingStrategy != null ? fieldNamingStrategy.hashCode() : 0); + result = 31 * result + (version != null ? version.hashCode() : 0); + result = 31 * result + (serializationExclusionStrategies != null ? serializationExclusionStrategies.hashCode() : 0); + result = 31 * result + (deserializationExclusionStrategies != null ? deserializationExclusionStrategies.hashCode() : 0); + return result; + } + + @Override + public Config.Builder copy() { + Builder copied = (Builder) super.copy(); + copied.excludeFieldsWithoutExposeAnnotation = excludeFieldsWithoutExposeAnnotation; + copied.disableHtmlEscaping = disableHtmlEscaping; + copied.dateFormat = dateFormat; + copied.fieldNamingStrategy = fieldNamingStrategy; + copied.version = version; + copied.serializationExclusionStrategies = new HashSet(serializationExclusionStrategies); + copied.deserializationExclusionStrategies = new HashSet(deserializationExclusionStrategies); + return copied; + } + + @Override + public String toString() { + return super.toString() + " => GsonCompatibilityMode{" + + "excludeFieldsWithoutExposeAnnotation=" + excludeFieldsWithoutExposeAnnotation + + ", disableHtmlEscaping=" + disableHtmlEscaping + + ", dateFormat=" + dateFormat + + ", fieldNamingStrategy=" + fieldNamingStrategy + + ", version=" + version + + ", serializationExclusionStrategies=" + serializationExclusionStrategies + + ", deserializationExclusionStrategies=" + deserializationExclusionStrategies + + '}'; + } + } + + @Override + protected OmitValue createOmitValue(Type valueType) { + if (valueType instanceof Class) { + Class clazz = (Class) valueType; + if (clazz.isPrimitive()) { + return null; // gson do not omit primitive zero + } + } + return super.createOmitValue(valueType); + } + + @Override + public Encoder createEncoder(String cacheKey, Type type) { + if (Date.class == type) { + return new Encoder() { + @Override + public void encode(Object obj, JsonStream stream) throws IOException { + DateFormat dateFormat = builder().dateFormat.get(); + stream.writeVal(dateFormat.format(obj)); + } + }; + } else if (String.class == type) { + final String[] replacements; + if (builder().disableHtmlEscaping) { + replacements = REPLACEMENT_CHARS; + } else { + replacements = HTML_SAFE_REPLACEMENT_CHARS; + } + return new Encoder() { + @Override + public void encode(Object obj, JsonStream stream) throws IOException { + String value = (String) obj; + stream.write('"'); + int _surrogate; + for (int i = 0; i < value.length(); i++) { + int c = value.charAt(i); + String replacement; + if (c < 128) { + replacement = replacements[c]; + if (replacement == null) { + stream.write(c); + } else { + stream.writeRaw(replacement); + } + } else if (c == '\u2028') { + stream.writeRaw("\\u2028"); + } else if (c == '\u2029') { + stream.writeRaw("\\u2029"); + } else { + if (c < 0x800) { // 2-byte + stream.write( + (byte) (0xc0 | (c >> 6)), + (byte) (0x80 | (c & 0x3f)) + ); + } else { // 3 or 4 bytes + // Surrogates? + if (c < SURR1_FIRST || c > SURR2_LAST) { + stream.write( + (byte) (0xe0 | (c >> 12)), + (byte) (0x80 | ((c >> 6) & 0x3f)), + (byte) (0x80 | (c & 0x3f)) + ); + continue; + } + // Yup, a surrogate: + if (c > SURR1_LAST) { // must be from first range + throw new JsonException("illegalSurrogate"); + } + _surrogate = c; + // and if so, followed by another from next range + if (i >= value.length()) { // unless we hit the end? + break; + } + i++; + c = value.charAt(i); + int firstPart = _surrogate; + _surrogate = 0; + // Ok, then, is the second part valid? + if (c < SURR2_FIRST || c > SURR2_LAST) { + throw new JsonException("Broken surrogate pair: first char 0x" + Integer.toHexString(firstPart) + ", second 0x" + Integer.toHexString(c) + "; illegal combination"); + } + c = 0x10000 + ((firstPart - SURR1_FIRST) << 10) + (c - SURR2_FIRST); + if (c > 0x10FFFF) { // illegal in JSON as well as in XML + throw new JsonException("illegalSurrogate"); + } + stream.write( + (byte) (0xf0 | (c >> 18)), + (byte) (0x80 | ((c >> 12) & 0x3f)), + (byte) (0x80 | ((c >> 6) & 0x3f)), + (byte) (0x80 | (c & 0x3f)) + ); + } + } + } + stream.write('"'); + } + }; + } + return super.createEncoder(cacheKey, type); + } + + @Override + public Decoder createDecoder(String cacheKey, Type type) { + if (Date.class == type) { + return new Decoder() { + @Override + public Object decode(JsonIterator iter) throws IOException { + DateFormat dateFormat = builder().dateFormat.get(); + try { + String input = iter.readString(); + return dateFormat.parse(input); + } catch (ParseException e) { + throw new JsonException(e); + } + } + }; + } else if (String.class == type) { + return new Decoder() { + @Override + public Object decode(JsonIterator iter) throws IOException { + ValueType valueType = iter.whatIsNext(); + if (valueType == ValueType.STRING) { + return iter.readString(); + } else if (valueType == ValueType.NUMBER) { + return iter.readNumberAsString(); + } else if (valueType == ValueType.BOOLEAN) { + return iter.readBoolean() ? "true" : "false"; + } else if (valueType == ValueType.NULL) { + iter.skip(); + return null; + } else { + throw new JsonException("expect string, but found " + valueType); + } + } + }; + } else if (boolean.class == type) { + return new Decoder.BooleanDecoder() { + @Override + public boolean decodeBoolean(JsonIterator iter) throws IOException { + ValueType valueType = iter.whatIsNext(); + if (valueType == ValueType.BOOLEAN) { + return iter.readBoolean(); + } else if (valueType == ValueType.NULL) { + iter.skip(); + return false; + } else { + throw new JsonException("expect boolean, but found " + valueType); + } + } + }; + } else if (long.class == type) { + return new Decoder.LongDecoder() { + @Override + public long decodeLong(JsonIterator iter) throws IOException { + ValueType valueType = iter.whatIsNext(); + if (valueType == ValueType.NUMBER) { + return iter.readLong(); + } else if (valueType == ValueType.NULL) { + iter.skip(); + return 0; + } else { + throw new JsonException("expect long, but found " + valueType); + } + } + }; + } else if (int.class == type) { + return new Decoder.IntDecoder() { + @Override + public int decodeInt(JsonIterator iter) throws IOException { + ValueType valueType = iter.whatIsNext(); + if (valueType == ValueType.NUMBER) { + return iter.readInt(); + } else if (valueType == ValueType.NULL) { + iter.skip(); + return 0; + } else { + throw new JsonException("expect int, but found " + valueType); + } + } + }; + } else if (float.class == type) { + return new Decoder.FloatDecoder() { + @Override + public float decodeFloat(JsonIterator iter) throws IOException { + ValueType valueType = iter.whatIsNext(); + if (valueType == ValueType.NUMBER) { + return iter.readFloat(); + } else if (valueType == ValueType.NULL) { + iter.skip(); + return 0.0f; + } else { + throw new JsonException("expect float, but found " + valueType); + } + } + }; + } else if (double.class == type) { + return new Decoder.DoubleDecoder() { + @Override + public double decodeDouble(JsonIterator iter) throws IOException { + ValueType valueType = iter.whatIsNext(); + if (valueType == ValueType.NUMBER) { + return iter.readDouble(); + } else if (valueType == ValueType.NULL) { + iter.skip(); + return 0.0d; + } else { + throw new JsonException("expect float, but found " + valueType); + } + } + }; + } + return super.createDecoder(cacheKey, type); + } + + @Override + public void updateClassDescriptor(ClassDescriptor desc) { + FieldNamingStrategy fieldNamingStrategy = builder().fieldNamingStrategy; + for (Binding binding : desc.allBindings()) { + if (binding.method != null) { + binding.toNames = new String[0]; + binding.fromNames = new String[0]; + } + if (fieldNamingStrategy != null && binding.field != null) { + String translated = fieldNamingStrategy.translateName(binding.field); + binding.toNames = new String[]{translated}; + binding.fromNames = new String[]{translated}; + } + if (builder().version != null) { + Since since = binding.getAnnotation(Since.class); + if (since != null && builder().version < since.value()) { + binding.toNames = new String[0]; + binding.fromNames = new String[0]; + } + Until until = binding.getAnnotation(Until.class); + if (until != null && builder().version >= until.value()) { + binding.toNames = new String[0]; + binding.fromNames = new String[0]; + } + } + for (ExclusionStrategy strategy : builder().serializationExclusionStrategies) { + if (strategy.shouldSkipClass(binding.clazz)) { + binding.toNames = new String[0]; + continue; + } + if (strategy.shouldSkipField(new FieldAttributes(binding.field))) { + binding.toNames = new String[0]; + } + } + for (ExclusionStrategy strategy : builder().deserializationExclusionStrategies) { + if (strategy.shouldSkipClass(binding.clazz)) { + binding.fromNames = new String[0]; + continue; + } + if (strategy.shouldSkipField(new FieldAttributes(binding.field))) { + binding.fromNames = new String[0]; + } + } + } + super.updateClassDescriptor(desc); + } + + @Override + protected JsonProperty getJsonProperty(Annotation[] annotations) { + JsonProperty jsoniterObj = super.getJsonProperty(annotations); + if (jsoniterObj != null) { + return jsoniterObj; + } + final SerializedName gsonObj = getAnnotation( + annotations, SerializedName.class); + if (gsonObj == null) { + return null; + } + return new JsonProperty() { + + @Override + public String value() { + return ""; + } + + @Override + public String[] from() { + return new String[]{gsonObj.value()}; + } + + @Override + public String[] to() { + return new String[]{gsonObj.value()}; + } + + @Override + public boolean required() { + return false; + } + + @Override + public Class decoder() { + return Decoder.class; + } + + @Override + public Class implementation() { + return Object.class; + } + + @Override + public Class encoder() { + return Encoder.class; + } + + @Override + public boolean nullable() { + return true; + } + + @Override + public boolean collectionValueNullable() { + return true; + } + + @Override + public String defaultValueToOmit() { + return ""; + } + + @Override + public Class annotationType() { + return JsonProperty.class; + } + }; + } + + @Override + protected JsonIgnore getJsonIgnore(Annotation[] annotations) { + + JsonIgnore jsoniterObj = super.getJsonIgnore(annotations); + if (jsoniterObj != null) { + return jsoniterObj; + } + if (builder().excludeFieldsWithoutExposeAnnotation) { + final Expose gsonObj = getAnnotation( + annotations, Expose.class); + if (gsonObj != null) { + return new JsonIgnore() { + @Override + public boolean ignoreDecoding() { + return !gsonObj.deserialize(); + } + + @Override + public boolean ignoreEncoding() { + return !gsonObj.serialize(); + } + + @Override + public Class annotationType() { + return JsonIgnore.class; + } + }; + } + return new JsonIgnore() { + @Override + public boolean ignoreDecoding() { + return true; + } + + @Override + public boolean ignoreEncoding() { + return true; + } + + @Override + public Class annotationType() { + return JsonIgnore.class; + } + }; + } + return null; + } +} diff --git a/src/main/java/com/jsoniter/annotation/JacksonAnnotationSupport.java b/src/main/java/com/jsoniter/extra/JacksonCompatibilityMode.java similarity index 56% rename from src/main/java/com/jsoniter/annotation/JacksonAnnotationSupport.java rename to src/main/java/com/jsoniter/extra/JacksonCompatibilityMode.java index a92cf99f..1935b3c1 100644 --- a/src/main/java/com/jsoniter/annotation/JacksonAnnotationSupport.java +++ b/src/main/java/com/jsoniter/extra/JacksonCompatibilityMode.java @@ -1,15 +1,34 @@ -package com.jsoniter.annotation; +package com.jsoniter.extra; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.jsoniter.annotation.*; import com.jsoniter.spi.Decoder; import com.jsoniter.spi.Encoder; -import com.jsoniter.spi.JsoniterSpi; +import com.jsoniter.spi.Config; import java.lang.annotation.Annotation; -public class JacksonAnnotationSupport extends JsoniterAnnotationSupport { +public class JacksonCompatibilityMode extends Config { - public static void enable() { - JsoniterSpi.registerExtension(new JacksonAnnotationSupport()); + public static class Builder extends Config.Builder { + public JacksonCompatibilityMode build() { + return (JacksonCompatibilityMode) super.build(); + } + + @Override + protected Config doBuild(String configName) { + return new JacksonCompatibilityMode(configName, this); + } + + @Override + public String toString() { + return super.toString() + " => JacksonCompatibilityMode{}"; + } + } + + private JacksonCompatibilityMode(String configName, Builder builder) { + super(configName, builder); } @Override @@ -25,7 +44,12 @@ protected JsonIgnore getJsonIgnore(Annotation[] annotations) { } return new JsonIgnore() { @Override - public boolean value() { + public boolean ignoreDecoding() { + return jacksonObj.value(); + } + + @Override + public boolean ignoreEncoding() { return jacksonObj.value(); } @@ -50,17 +74,17 @@ protected JsonProperty getJsonProperty(Annotation[] annotations) { return new JsonProperty() { @Override public String value() { - return jacksonObj.value(); + return ""; } @Override public String[] from() { - return new String[0]; + return new String[]{jacksonObj.value()}; } @Override public String[] to() { - return new String[0]; + return new String[]{jacksonObj.value()}; } @Override @@ -94,8 +118,8 @@ public boolean collectionValueNullable() { } @Override - public boolean omitNull() { - return true; + public String defaultValueToOmit() { + return ""; } @Override @@ -123,4 +147,45 @@ public Class annotationType() { } }; } + + @Override + protected JsonUnwrapper getJsonUnwrapper(Annotation[] annotations) { + JsonUnwrapper jsoniterObj = super.getJsonUnwrapper(annotations); + if (jsoniterObj != null) { + return jsoniterObj; + } + JsonAnyGetter jacksonObj = getAnnotation(annotations, JsonAnyGetter.class); + if (jacksonObj == null) { + return null; + } + return new JsonUnwrapper() { + @Override + public Class annotationType() { + return JsonUnwrapper.class; + } + }; + } + + @Override + protected JsonWrapper getJsonWrapper(Annotation[] annotations) { + JsonWrapper jsoniterObj = super.getJsonWrapper(annotations); + if (jsoniterObj != null) { + return jsoniterObj; + } + JsonAnySetter jacksonObj = getAnnotation(annotations, JsonAnySetter.class); + if (jacksonObj == null) { + return null; + } + return new JsonWrapper() { + @Override + public JsonWrapperType value() { + return JsonWrapperType.KEY_VALUE; + } + + @Override + public Class annotationType() { + return JsonWrapper.class; + } + }; + } } diff --git a/src/main/java/com/jsoniter/extra/JdkDatetimeSupport.java b/src/main/java/com/jsoniter/extra/JdkDatetimeSupport.java index 25155c0a..f66c0504 100644 --- a/src/main/java/com/jsoniter/extra/JdkDatetimeSupport.java +++ b/src/main/java/com/jsoniter/extra/JdkDatetimeSupport.java @@ -31,7 +31,7 @@ public static synchronized void enable(String pattern) { throw new JsonException("JdkDatetimeSupport.enable can only be called once"); } JdkDatetimeSupport.pattern = pattern; - JsoniterSpi.registerTypeEncoder(Date.class, new Encoder() { + JsoniterSpi.registerTypeEncoder(Date.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal(sdf.get().format(obj)); diff --git a/src/main/java/com/jsoniter/extra/NamingStrategySupport.java b/src/main/java/com/jsoniter/extra/NamingStrategySupport.java index 64ab31da..4a0219ff 100644 --- a/src/main/java/com/jsoniter/extra/NamingStrategySupport.java +++ b/src/main/java/com/jsoniter/extra/NamingStrategySupport.java @@ -19,7 +19,9 @@ public static synchronized void enable(final NamingStrategy namingStrategy) { @Override public void updateClassDescriptor(ClassDescriptor desc) { for (Binding binding : desc.allBindings()) { - binding.name = namingStrategy.translate(binding.name); + String translated = namingStrategy.translate(binding.name); + binding.toNames = new String[]{translated}; + binding.fromNames = new String[]{translated}; } } }); diff --git a/src/main/java/com/jsoniter/extra/PreciseFloatSupport.java b/src/main/java/com/jsoniter/extra/PreciseFloatSupport.java index 2d39c066..8352b39b 100644 --- a/src/main/java/com/jsoniter/extra/PreciseFloatSupport.java +++ b/src/main/java/com/jsoniter/extra/PreciseFloatSupport.java @@ -20,7 +20,7 @@ public static synchronized void enable() { throw new JsonException("PreciseFloatSupport.enable can only be called once"); } enabled = true; - JsoniterSpi.registerTypeEncoder(Double.class, new Encoder() { + JsoniterSpi.registerTypeEncoder(Double.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeRaw(obj.toString()); @@ -38,7 +38,7 @@ public void encodeDouble(double obj, JsonStream stream) throws IOException { stream.writeRaw(Double.toString(obj)); } }); - JsoniterSpi.registerTypeEncoder(Float.class, new Encoder() { + JsoniterSpi.registerTypeEncoder(Float.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeRaw(obj.toString()); diff --git a/src/main/java/com/jsoniter/fuzzy/MaybeEmptyArrayDecoder.java b/src/main/java/com/jsoniter/fuzzy/MaybeEmptyArrayDecoder.java index f8e8a94f..aa4effb1 100644 --- a/src/main/java/com/jsoniter/fuzzy/MaybeEmptyArrayDecoder.java +++ b/src/main/java/com/jsoniter/fuzzy/MaybeEmptyArrayDecoder.java @@ -2,12 +2,19 @@ import com.jsoniter.JsonIterator; import com.jsoniter.ValueType; +import com.jsoniter.spi.Binding; import com.jsoniter.spi.Decoder; import java.io.IOException; public class MaybeEmptyArrayDecoder implements Decoder { + private Binding binding; + + public MaybeEmptyArrayDecoder(Binding binding) { + this.binding = binding; + } + @Override public Object decode(JsonIterator iter) throws IOException { if (iter.whatIsNext() == ValueType.ARRAY) { @@ -18,7 +25,7 @@ public Object decode(JsonIterator iter) throws IOException { return null; } } else { - return iter.read(iter); + return iter.read(binding.valueTypeLiteral); } } } diff --git a/src/main/java/com/jsoniter/output/AsciiOutputStream.java b/src/main/java/com/jsoniter/output/AsciiOutputStream.java deleted file mode 100644 index a2fc78ce..00000000 --- a/src/main/java/com/jsoniter/output/AsciiOutputStream.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.jsoniter.output; - -import java.io.IOException; -import java.io.OutputStream; - -class AsciiOutputStream extends OutputStream { - private char[] buf = new char[4096]; - private int count = 0; - - @Override - public void write(byte[] b, int off, int len) throws IOException { - int i = off; - for (; ; ) { - for (; i < off + len && count < buf.length; i++) { - buf[count++] = (char) b[i]; - } - if (count == buf.length) { - char[] newBuf = new char[buf.length * 2]; - System.arraycopy(buf, 0, newBuf, 0, buf.length); - buf = newBuf; - } else { - break; - } - } - } - - @Override - public void write(int b) throws IOException { - if (count == buf.length) { - char[] newBuf = new char[buf.length * 2]; - System.arraycopy(buf, 0, newBuf, 0, buf.length); - buf = newBuf; - } - buf[count++] = (char) b; - } - - @Override - public String toString() { - return new String(buf, 0, count); - } - - public void reset() { - count = 0; - } -} diff --git a/src/main/java/com/jsoniter/output/Codegen.java b/src/main/java/com/jsoniter/output/Codegen.java index d8dbcda1..7680244d 100644 --- a/src/main/java/com/jsoniter/output/Codegen.java +++ b/src/main/java/com/jsoniter/output/Codegen.java @@ -1,10 +1,6 @@ package com.jsoniter.output; -import com.jsoniter.spi.JsonException; -import com.jsoniter.spi.Encoder; -import com.jsoniter.spi.Extension; -import com.jsoniter.spi.JsoniterSpi; -import com.jsoniter.spi.TypeLiteral; +import com.jsoniter.spi.*; import java.io.File; import java.io.FileOutputStream; @@ -17,26 +13,13 @@ class Codegen { - static EncodingMode mode = EncodingMode.REFLECTION_MODE; - static boolean isDoingStaticCodegen; + static CodegenAccess.StaticCodegenTarget isDoingStaticCodegen; // only read/write when generating code with synchronized protection private final static Map generatedSources = new HashMap(); - private volatile static Map reflectionEncoders = new HashMap(); + private volatile static Map reflectionEncoders = new HashMap(); - static { - String envMode = System.getenv("JSONITER_ENCODING_MODE"); - if (envMode != null) { - mode = EncodingMode.valueOf(envMode); - } - } - - public static void setMode(EncodingMode mode) { - Codegen.mode = mode; - } - - - public static Encoder getReflectionEncoder(String cacheKey, Type type) { - Encoder encoder = CodegenImplNative.NATIVE_ENCODERS.get(type); + public static Encoder.ReflectionEncoder getReflectionEncoder(String cacheKey, Type type) { + Encoder.ReflectionEncoder encoder = CodegenImplNative.NATIVE_ENCODERS.get(type); if (encoder != null) { return encoder; } @@ -49,17 +32,9 @@ public static Encoder getReflectionEncoder(String cacheKey, Type type) { if (encoder != null) { return encoder; } - Type[] typeArgs = new Type[0]; - Class clazz; - if (type instanceof ParameterizedType) { - ParameterizedType pType = (ParameterizedType) type; - clazz = (Class) pType.getRawType(); - typeArgs = pType.getActualTypeArguments(); - } else { - clazz = (Class) type; - } - encoder = ReflectionEncoderFactory.create(clazz, typeArgs); - HashMap copy = new HashMap(reflectionEncoders); + ClassInfo classInfo = new ClassInfo(type); + encoder = ReflectionEncoderFactory.create(classInfo); + HashMap copy = new HashMap(reflectionEncoders); copy.put(cacheKey, encoder); reflectionEncoders = copy; return encoder; @@ -74,7 +49,7 @@ public static Encoder getEncoder(String cacheKey, Type type) { return gen(cacheKey, type); } - private static synchronized Encoder gen(String cacheKey, Type type) { + private static synchronized Encoder gen(final String cacheKey, Type type) { Encoder encoder = JsoniterSpi.getEncoder(cacheKey); if (encoder != null) { return encoder; @@ -92,6 +67,81 @@ private static synchronized Encoder gen(String cacheKey, Type type) { JsoniterSpi.addNewEncoder(cacheKey, encoder); return encoder; } + addPlaceholderEncoderToSupportRecursiveStructure(cacheKey); + try { + EncodingMode mode = JsoniterSpi.getCurrentConfig().encodingMode(); + if (mode != EncodingMode.REFLECTION_MODE) { + Type originalType = type; + type = chooseAccessibleSuper(type); + if (Object.class == type) { + throw new JsonException("dynamic code can not serialize private class: " + originalType); + } + } + ClassInfo classInfo = new ClassInfo(type); + if (Map.class.isAssignableFrom(classInfo.clazz) && classInfo.typeArgs.length > 1) { + MapKeyEncoders.registerOrGetExisting(classInfo.typeArgs[0]); + } + if (mode == EncodingMode.REFLECTION_MODE) { + encoder = ReflectionEncoderFactory.create(classInfo); + return encoder; + } + if (isDoingStaticCodegen == null) { + try { + encoder = (Encoder) Class.forName(cacheKey).newInstance(); + return encoder; + } catch (Exception e) { + if (mode == EncodingMode.STATIC_MODE) { + throw new JsonException("static gen should provide the encoder we need, but failed to create the encoder", e); + } + } + } + CodegenResult source = genSource(cacheKey, classInfo); + try { + generatedSources.put(cacheKey, source); + if (isDoingStaticCodegen == null) { + encoder = DynamicCodegen.gen(classInfo.clazz, cacheKey, source); + } else { + staticGen(classInfo.clazz, cacheKey, source); + } + return encoder; + } catch (Exception e) { + String msg = "failed to generate encoder for: " + type + " with " + Arrays.toString(classInfo.typeArgs) + ", exception: " + e; + msg = msg + "\n" + source; + throw new JsonException(msg, e); + } + } finally { + JsoniterSpi.addNewEncoder(cacheKey, encoder); + } + } + + private static void addPlaceholderEncoderToSupportRecursiveStructure(final String cacheKey) { + JsoniterSpi.addNewEncoder(cacheKey, new Encoder() { + @Override + public void encode(Object obj, JsonStream stream) throws IOException { + Encoder encoder = JsoniterSpi.getEncoder(cacheKey); + if (this == encoder) { + for(int i = 0; i < 30; i++) { + encoder = JsoniterSpi.getEncoder(cacheKey); + if (this == encoder) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new JsonException(e); + } + } else { + break; + } + } + if (this == encoder) { + throw new JsonException("internal error: placeholder is not replaced with real encoder"); + } + } + encoder.encode(obj, stream); + } + }); + } + + private static Type chooseAccessibleSuper(Type type) { Type[] typeArgs = new Type[0]; Class clazz; if (type instanceof ParameterizedType) { @@ -101,45 +151,22 @@ private static synchronized Encoder gen(String cacheKey, Type type) { } else { clazz = (Class) type; } - if (mode == EncodingMode.REFLECTION_MODE) { - encoder = ReflectionEncoderFactory.create(clazz, typeArgs); - JsoniterSpi.addNewEncoder(cacheKey, encoder); - return encoder; - } - if (!isDoingStaticCodegen) { - try { - encoder = (Encoder) Class.forName(cacheKey).newInstance(); - JsoniterSpi.addNewEncoder(cacheKey, encoder); - return encoder; - } catch (Exception e) { - if (mode == EncodingMode.STATIC_MODE) { - throw new JsonException("static gen should provide the encoder we need, but failed to create the encoder", e); - } - } + if (Modifier.isPublic(clazz.getModifiers())) { + return type; } - clazz = chooseAccessibleSuper(clazz); - CodegenResult source = genSource(cacheKey, clazz, typeArgs); - try { - generatedSources.put(cacheKey, source); - if (isDoingStaticCodegen) { - staticGen(clazz, cacheKey, source); - } else { - encoder = DynamicCodegen.gen(clazz, cacheKey, source); - } - JsoniterSpi.addNewEncoder(cacheKey, encoder); - return encoder; - } catch (Exception e) { - String msg = "failed to generate encoder for: " + type + " with " + Arrays.toString(typeArgs) + ", exception: " + e; - msg = msg + "\n" + source; - throw new JsonException(msg, e); + clazz = walkSuperUntilPublic(clazz.getSuperclass()); + if (typeArgs.length == 0) { + return clazz; + } else { + return GenericsHelper.createParameterizedType(typeArgs, null, clazz); } } - private static Class chooseAccessibleSuper(Class clazz) { + private static Class walkSuperUntilPublic(Class clazz) { if (Modifier.isPublic(clazz.getModifiers())) { return clazz; } - return chooseAccessibleSuper(clazz.getSuperclass()); + return walkSuperUntilPublic(clazz.getSuperclass()); } public static CodegenResult getGeneratedSource(String cacheKey) { @@ -149,7 +176,7 @@ public static CodegenResult getGeneratedSource(String cacheKey) { private static void staticGen(Class clazz, String cacheKey, CodegenResult source) throws IOException { createDir(cacheKey); String fileName = cacheKey.replace('.', '/') + ".java"; - FileOutputStream fileOutputStream = new FileOutputStream(fileName); + FileOutputStream fileOutputStream = new FileOutputStream(new File(isDoingStaticCodegen.outputDir, fileName)); try { OutputStreamWriter writer = new OutputStreamWriter(fileOutputStream); try { @@ -166,7 +193,7 @@ private static void staticGen(Class clazz, String cacheKey, OutputStreamWriter w String className = cacheKey.substring(cacheKey.lastIndexOf('.') + 1); String packageName = cacheKey.substring(0, cacheKey.lastIndexOf('.')); writer.write("package " + packageName + ";\n"); - writer.write("public class " + className + " extends com.jsoniter.spi.EmptyEncoder {\n"); + writer.write("public class " + className + " implements com.jsoniter.spi.Encoder {\n"); writer.write(source.generateWrapperCode(clazz)); writer.write(source.toString()); writer.write("}\n"); @@ -174,7 +201,7 @@ private static void staticGen(Class clazz, String cacheKey, OutputStreamWriter w private static void createDir(String cacheKey) { String[] parts = cacheKey.split("\\."); - File parent = new File("."); + File parent = new File(isDoingStaticCodegen.outputDir); for (int i = 0; i < parts.length - 1; i++) { String part = parts[i]; File current = new File(parent, part); @@ -183,24 +210,25 @@ private static void createDir(String cacheKey) { } } - private static CodegenResult genSource(String cacheKey, Class clazz, Type[] typeArgs) { + private static CodegenResult genSource(String cacheKey, ClassInfo classInfo) { + Class clazz = classInfo.clazz; if (clazz.isArray()) { - return CodegenImplArray.genArray(cacheKey, clazz); + return CodegenImplArray.genArray(cacheKey, classInfo); } if (Map.class.isAssignableFrom(clazz)) { - return CodegenImplMap.genMap(cacheKey, clazz, typeArgs); + return CodegenImplMap.genMap(cacheKey, classInfo); } if (Collection.class.isAssignableFrom(clazz)) { - return CodegenImplArray.genCollection(cacheKey, clazz, typeArgs); + return CodegenImplArray.genCollection(cacheKey, classInfo); } if (clazz.isEnum()) { return CodegenImplNative.genEnum(clazz); } - return CodegenImplObject.genObject(clazz); + return CodegenImplObject.genObject(classInfo); } - public static void staticGenEncoders(TypeLiteral[] typeLiterals) { - isDoingStaticCodegen = true; + public static void staticGenEncoders(TypeLiteral[] typeLiterals, CodegenAccess.StaticCodegenTarget staticCodegenTarget) { + isDoingStaticCodegen = staticCodegenTarget; for (TypeLiteral typeLiteral : typeLiterals) { gen(typeLiteral.getEncoderCacheKey(), typeLiteral.getType()); } diff --git a/src/main/java/com/jsoniter/output/CodegenAccess.java b/src/main/java/com/jsoniter/output/CodegenAccess.java index 15db4cc0..913df649 100644 --- a/src/main/java/com/jsoniter/output/CodegenAccess.java +++ b/src/main/java/com/jsoniter/output/CodegenAccess.java @@ -1,6 +1,5 @@ package com.jsoniter.output; -import com.jsoniter.*; import com.jsoniter.any.Any; import com.jsoniter.spi.Encoder; import com.jsoniter.spi.JsoniterSpi; @@ -53,12 +52,17 @@ public static void writeVal(String cacheKey, double obj, JsonStream stream) thro encoder.encodeDouble(obj, stream); } + public static void writeMapKey(String cacheKey, Object mapKey, JsonStream stream) throws IOException { + Encoder mapKeyEncoder = JsoniterSpi.getMapKeyEncoder(cacheKey); + mapKeyEncoder.encode(mapKey, stream); + } + public static void writeStringWithoutQuote(String obj, JsonStream stream) throws IOException { StreamImplString.writeStringWithoutQuote(stream, obj); } - public static void staticGenEncoders(TypeLiteral[] typeLiterals) { - Codegen.staticGenEncoders(typeLiterals); + public static void staticGenEncoders(TypeLiteral[] typeLiterals, StaticCodegenTarget staticCodegenTarget) { + Codegen.staticGenEncoders(typeLiterals, staticCodegenTarget); } public static Any wrap(Object val) { @@ -69,4 +73,13 @@ public static Any wrap(Object val) { String cacheKey = TypeLiteral.create(clazz).getEncoderCacheKey(); return Codegen.getReflectionEncoder(cacheKey, clazz).wrap(val); } + + public static class StaticCodegenTarget { + + public final String outputDir; + + public StaticCodegenTarget(String outputDir) { + this.outputDir = outputDir; + } + } } diff --git a/src/main/java/com/jsoniter/output/CodegenImplArray.java b/src/main/java/com/jsoniter/output/CodegenImplArray.java index 76108906..19af5531 100644 --- a/src/main/java/com/jsoniter/output/CodegenImplArray.java +++ b/src/main/java/com/jsoniter/output/CodegenImplArray.java @@ -1,11 +1,41 @@ package com.jsoniter.output; +import com.jsoniter.spi.ClassInfo; +import com.jsoniter.spi.JsoniterSpi; + import java.lang.reflect.Type; import java.util.*; class CodegenImplArray { - public static CodegenResult genArray(String cacheKey, Class clazz) { + public static CodegenResult genCollection(String cacheKey, ClassInfo classInfo) { + Type[] typeArgs = classInfo.typeArgs; + Class clazz = classInfo.clazz; + Type compType = Object.class; + if (typeArgs.length == 0) { + // default to List + } else if (typeArgs.length == 1) { + compType = typeArgs[0]; + } else { + throw new IllegalArgumentException( + "can not bind to generic collection without argument types, " + + "try syntax like TypeLiteral>{}"); + } + if (clazz == List.class) { + clazz = ArrayList.class; + } else if (clazz == Set.class) { + clazz = HashSet.class; + } + if (List.class.isAssignableFrom(clazz)) { + return genList(cacheKey, clazz, compType); + } else { + return genCollection(cacheKey, clazz, compType); + } + } + + public static CodegenResult genArray(String cacheKey, ClassInfo classInfo) { + boolean noIndention = JsoniterSpi.getCurrentConfig().indentionStep() == 0; + Class clazz = classInfo.clazz; Class compType = clazz.getComponentType(); if (compType.isArray()) { throw new IllegalArgumentException("nested array not supported: " + clazz.getCanonicalName()); @@ -20,57 +50,48 @@ public static CodegenResult genArray(String cacheKey, Class clazz) { 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(String.format("%s[] arr = (%s[])obj;", compType.getCanonicalName(), compType.getCanonicalName())); - ctx.append("if (arr.length == 0) { return; }"); - ctx.buffer('['); + if (noIndention) { + ctx.append("if (arr.length == 0) { return; }"); + ctx.buffer('['); + } else { + ctx.append("if (arr.length == 0) { stream.write((byte)'[', (byte)']'); return; }"); + ctx.append("stream.writeArrayStart(); stream.writeIndention();"); + } ctx.append("int i = 0;"); ctx.append(String.format("%s e = arr[i++];", compType.getCanonicalName())); if (isCollectionValueNullable) { ctx.append("if (e == null) { stream.writeNull(); } else {"); CodegenImplNative.genWriteOp(ctx, "e", compType, true); - ctx.append("}"); + ctx.append("}"); // if } else { CodegenImplNative.genWriteOp(ctx, "e", compType, false); } ctx.append("while (i < arr.length) {"); - ctx.append("stream.write(',');"); + if (noIndention) { + ctx.append("stream.write(',');"); + } else { + ctx.append("stream.writeMore();"); + } ctx.append("e = arr[i++];"); if (isCollectionValueNullable) { ctx.append("if (e == null) { stream.writeNull(); } else {"); CodegenImplNative.genWriteOp(ctx, "e", compType, true); - ctx.append("}"); + ctx.append("}"); // if } else { CodegenImplNative.genWriteOp(ctx, "e", compType, false); } - ctx.append("}"); - ctx.buffer(']'); - ctx.append("}"); - return ctx; - } - - public static CodegenResult genCollection(String cacheKey, Class clazz, Type[] typeArgs) { - Type compType = Object.class; - if (typeArgs.length == 0) { - // default to List - } else if (typeArgs.length == 1) { - compType = typeArgs[0]; - } else { - throw new IllegalArgumentException( - "can not bind to generic collection without argument types, " + - "try syntax like TypeLiteral>{}"); - } - if (clazz == List.class) { - clazz = ArrayList.class; - } else if (clazz == Set.class) { - clazz = HashSet.class; - } - if (List.class.isAssignableFrom(clazz)) { - return genList(cacheKey, clazz, compType); + ctx.append("}"); // while + if (noIndention) { + ctx.buffer(']'); } else { - return genCollection(cacheKey, clazz, compType); + ctx.append("stream.writeArrayEnd();"); } + ctx.append("}"); // public static void encode_ + return ctx; } private static CodegenResult genList(String cacheKey, Class clazz, Type compType) { + boolean noIndention = JsoniterSpi.getCurrentConfig().indentionStep() == 0; boolean isCollectionValueNullable = true; if (cacheKey.endsWith("__value_not_nullable")) { isCollectionValueNullable = false; @@ -79,8 +100,13 @@ private static CodegenResult genList(String cacheKey, Class clazz, Type compType ctx.append("public static void encode_(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException {"); ctx.append("java.util.List list = (java.util.List)obj;"); ctx.append("int size = list.size();"); - ctx.append("if (size == 0) { return; }"); - ctx.buffer('['); + if (noIndention) { + ctx.append("if (size == 0) { return; }"); + ctx.buffer('['); + } else { + ctx.append("if (size == 0) { stream.write((byte)'[', (byte)']'); return; }"); + ctx.append("stream.writeArrayStart(); stream.writeIndention();"); + } ctx.append("java.lang.Object e = list.get(0);"); if (isCollectionValueNullable) { ctx.append("if (e == null) { stream.writeNull(); } else {"); @@ -90,22 +116,31 @@ private static CodegenResult genList(String cacheKey, Class clazz, Type compType CodegenImplNative.genWriteOp(ctx, "e", compType, false); } ctx.append("for (int i = 1; i < size; i++) {"); - ctx.append("stream.write(',');"); + if (noIndention) { + ctx.append("stream.write(',');"); + } else { + ctx.append("stream.writeMore();"); + } ctx.append("e = list.get(i);"); if (isCollectionValueNullable) { ctx.append("if (e == null) { stream.writeNull(); } else {"); CodegenImplNative.genWriteOp(ctx, "e", compType, true); - ctx.append("}"); + ctx.append("}"); // if } else { CodegenImplNative.genWriteOp(ctx, "e", compType, false); } - ctx.append("}"); - ctx.buffer(']'); - ctx.append("}"); + ctx.append("}"); // for + if (noIndention) { + ctx.buffer(']'); + } else { + ctx.append("stream.writeArrayEnd();"); + } + ctx.append("}"); // public static void encode_ return ctx; } private static CodegenResult genCollection(String cacheKey, Class clazz, Type compType) { + boolean noIndention = JsoniterSpi.getCurrentConfig().indentionStep() == 0; boolean isCollectionValueNullable = true; if (cacheKey.endsWith("__value_not_nullable")) { isCollectionValueNullable = false; @@ -113,29 +148,42 @@ private static CodegenResult genCollection(String cacheKey, Class clazz, Type co 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("java.util.Iterator iter = ((java.util.Collection)obj).iterator();"); - ctx.append("if (!iter.hasNext()) { return; }"); - ctx.buffer('['); + if (noIndention) { + ctx.append("if (!iter.hasNext()) { return; }"); + ctx.buffer('['); + } else { + ctx.append("if (!iter.hasNext()) { stream.write((byte)'[', (byte)']'); return; }"); + ctx.append("stream.writeArrayStart(); stream.writeIndention();"); + } ctx.append("java.lang.Object e = iter.next();"); if (isCollectionValueNullable) { ctx.append("if (e == null) { stream.writeNull(); } else {"); CodegenImplNative.genWriteOp(ctx, "e", compType, true); - ctx.append("}"); + ctx.append("}"); // if } else { CodegenImplNative.genWriteOp(ctx, "e", compType, false); } ctx.append("while (iter.hasNext()) {"); - ctx.append("stream.write(',');"); + if (noIndention) { + ctx.append("stream.write(',');"); + } else { + ctx.append("stream.writeMore();"); + } ctx.append("e = iter.next();"); if (isCollectionValueNullable) { ctx.append("if (e == null) { stream.writeNull(); } else {"); CodegenImplNative.genWriteOp(ctx, "e", compType, true); - ctx.append("}"); + ctx.append("}"); // if } else { CodegenImplNative.genWriteOp(ctx, "e", compType, false); } - ctx.append("}"); - ctx.buffer(']'); - ctx.append("}"); + ctx.append("}"); // while + if (noIndention) { + ctx.buffer(']'); + } else { + ctx.append("stream.writeArrayEnd();"); + } + ctx.append("}"); // public static void encode_ return ctx; } diff --git a/src/main/java/com/jsoniter/output/CodegenImplMap.java b/src/main/java/com/jsoniter/output/CodegenImplMap.java index 432f9ab9..006817d3 100644 --- a/src/main/java/com/jsoniter/output/CodegenImplMap.java +++ b/src/main/java/com/jsoniter/output/CodegenImplMap.java @@ -1,39 +1,41 @@ package com.jsoniter.output; +import com.jsoniter.spi.ClassInfo; +import com.jsoniter.spi.JsoniterSpi; + import java.lang.reflect.Type; -import java.util.Collection; class CodegenImplMap { - public static CodegenResult genMap(String cacheKey, Class clazz, Type[] typeArgs) { + public static CodegenResult genMap(String cacheKey, ClassInfo classInfo) { + boolean noIndention = JsoniterSpi.getCurrentConfig().indentionStep() == 0; + Type[] typeArgs = classInfo.typeArgs; boolean isCollectionValueNullable = true; if (cacheKey.endsWith("__value_not_nullable")) { isCollectionValueNullable = false; } - Type keyType = String.class; + Type keyType = Object.class; Type valueType = Object.class; - if (typeArgs.length == 0) { - // default to Map - } else if (typeArgs.length == 2) { + if (typeArgs.length == 2) { keyType = typeArgs[0]; valueType = typeArgs[1]; - } else { - throw new IllegalArgumentException( - "can not bind to generic collection without argument types, " + - "try syntax like TypeLiteral>{}"); - } - if (keyType != String.class) { - throw new IllegalArgumentException("map key must be String"); } 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; }"); ctx.append("java.util.Map map = (java.util.Map)obj;"); ctx.append("java.util.Iterator iter = map.entrySet().iterator();"); - ctx.append("if(!iter.hasNext()) { return; }"); + if (noIndention) { + ctx.append("if(!iter.hasNext()) { return; }"); + } else { + ctx.append("if(!iter.hasNext()) { stream.write((byte)'{', (byte)'}'); return; }"); + } ctx.append("java.util.Map.Entry entry = (java.util.Map.Entry)iter.next();"); - ctx.buffer('{'); - ctx.append("stream.writeVal((String)entry.getKey());"); - ctx.buffer(':'); + if (noIndention) { + ctx.buffer('{'); + } else { + ctx.append("stream.writeObjectStart(); stream.writeIndention();"); + } + genWriteMapKey(ctx, keyType, noIndention); if (isCollectionValueNullable) { ctx.append("if (entry.getValue() == null) { stream.writeNull(); } else {"); CodegenImplNative.genWriteOp(ctx, "entry.getValue()", valueType, true); @@ -43,8 +45,12 @@ public static CodegenResult genMap(String cacheKey, Class clazz, Type[] typeArgs } ctx.append("while(iter.hasNext()) {"); ctx.append("entry = (java.util.Map.Entry)iter.next();"); - ctx.buffer(','); - ctx.append("stream.writeObjectField((String)entry.getKey());"); + if (noIndention) { + ctx.append("stream.write(',');"); + } else { + ctx.append("stream.writeMore();"); + } + genWriteMapKey(ctx, keyType, noIndention); if (isCollectionValueNullable) { ctx.append("if (entry.getValue() == null) { stream.writeNull(); } else {"); CodegenImplNative.genWriteOp(ctx, "entry.getValue()", valueType, true); @@ -53,8 +59,34 @@ public static CodegenResult genMap(String cacheKey, Class clazz, Type[] typeArgs CodegenImplNative.genWriteOp(ctx, "entry.getValue()", valueType, false); } ctx.append("}"); - ctx.buffer('}'); + if (noIndention) { + ctx.buffer('}'); + } else { + ctx.append("stream.writeObjectEnd();"); + } 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/CodegenImplNative.java b/src/main/java/com/jsoniter/output/CodegenImplNative.java index 5347a016..4237a5d2 100644 --- a/src/main/java/com/jsoniter/output/CodegenImplNative.java +++ b/src/main/java/com/jsoniter/output/CodegenImplNative.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; import java.math.BigDecimal; import java.math.BigInteger; import java.util.HashMap; @@ -14,8 +15,8 @@ import java.util.Map; class CodegenImplNative { - public static final Map NATIVE_ENCODERS = new IdentityHashMap() {{ - put(boolean.class, new Encoder() { + public static final Map NATIVE_ENCODERS = new IdentityHashMap() {{ + put(boolean.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal((Boolean) obj); @@ -27,7 +28,7 @@ public Any wrap(Object obj) { return Any.wrap((boolean) val); } }); - put(Boolean.class, new Encoder() { + put(Boolean.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal((Boolean) obj); @@ -39,7 +40,7 @@ public Any wrap(Object obj) { return Any.wrap((boolean) val); } }); - put(byte.class, new Encoder() { + put(byte.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal(((Byte) obj).shortValue()); @@ -51,7 +52,7 @@ public Any wrap(Object obj) { return Any.wrap((int) val); } }); - put(Byte.class, new Encoder() { + put(Byte.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal(((Byte) obj).shortValue()); @@ -63,7 +64,7 @@ public Any wrap(Object obj) { return Any.wrap((int) val); } }); - put(short.class, new Encoder() { + put(short.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal((Short) obj); @@ -75,7 +76,7 @@ public Any wrap(Object obj) { return Any.wrap((int) val); } }); - put(Short.class, new Encoder() { + put(Short.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal((Short) obj); @@ -87,7 +88,7 @@ public Any wrap(Object obj) { return Any.wrap((int) val); } }); - put(int.class, new Encoder() { + put(int.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal((Integer) obj); @@ -99,7 +100,7 @@ public Any wrap(Object obj) { return Any.wrap((int) val); } }); - put(Integer.class, new Encoder() { + put(Integer.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal((Integer) obj); @@ -111,7 +112,7 @@ public Any wrap(Object obj) { return Any.wrap((int) val); } }); - put(char.class, new Encoder() { + put(char.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal(((Character) obj).charValue()); @@ -123,7 +124,7 @@ public Any wrap(Object obj) { return Any.wrap((int) val); } }); - put(Character.class, new Encoder() { + put(Character.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal(((Character) obj).charValue()); @@ -135,7 +136,7 @@ public Any wrap(Object obj) { return Any.wrap((int) val); } }); - put(long.class, new Encoder() { + put(long.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal((Long) obj); @@ -147,7 +148,7 @@ public Any wrap(Object obj) { return Any.wrap((long) val); } }); - put(Long.class, new Encoder() { + put(Long.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal((Long) obj); @@ -159,7 +160,7 @@ public Any wrap(Object obj) { return Any.wrap((long) val); } }); - put(float.class, new Encoder() { + put(float.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal((Float) obj); @@ -171,7 +172,7 @@ public Any wrap(Object obj) { return Any.wrap((float) val); } }); - put(Float.class, new Encoder() { + put(Float.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal((Float) obj); @@ -183,7 +184,7 @@ public Any wrap(Object obj) { return Any.wrap((float) val); } }); - put(double.class, new Encoder() { + put(double.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal((Double) obj); @@ -195,7 +196,7 @@ public Any wrap(Object obj) { return Any.wrap((double) val); } }); - put(Double.class, new Encoder() { + put(Double.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal((Double) obj); @@ -207,7 +208,7 @@ public Any wrap(Object obj) { return Any.wrap((double) val); } }); - put(String.class, new Encoder() { + put(String.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { stream.writeVal((String) obj); @@ -219,7 +220,7 @@ public Any wrap(Object obj) { return Any.wrap(val); } }); - put(Object.class, new Encoder() { + put(Object.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { if (obj != null && obj.getClass() == Object.class) { @@ -238,7 +239,7 @@ public Any wrap(Object obj) { } }); - put(BigDecimal.class, new Encoder() { + put(BigDecimal.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { BigDecimal val = (BigDecimal) obj; @@ -250,7 +251,7 @@ public Any wrap(Object obj) { return Any.wrap(obj.toString()); } }); - put(BigInteger.class, new Encoder() { + put(BigInteger.class, new Encoder.ReflectionEncoder() { @Override public void encode(Object obj, JsonStream stream) throws IOException { BigInteger val = (BigInteger) obj; @@ -269,9 +270,10 @@ public static void genWriteOp(CodegenResult ctx, String code, Type valueType, bo } public static void genWriteOp(CodegenResult ctx, String code, Type valueType, boolean isNullable, boolean isCollectionValueNullable) { + boolean noIndention = JsoniterSpi.getCurrentConfig().indentionStep() == 0; String cacheKey = TypeLiteral.create(valueType).getEncoderCacheKey(); if (JsoniterSpi.getEncoder(cacheKey) == null) { - if (!isNullable && String.class == valueType) { + if (noIndention && !isNullable && String.class == valueType) { ctx.buffer('"'); ctx.append(String.format("com.jsoniter.output.CodegenAccess.writeStringWithoutQuote((java.lang.String)%s, stream);", code)); ctx.buffer('"'); @@ -281,6 +283,10 @@ public static void genWriteOp(CodegenResult ctx, String code, Type valueType, bo ctx.append(String.format("stream.writeVal((%s)%s);", getTypeName(valueType), code)); return; } + if (valueType instanceof WildcardType) { + ctx.append(String.format("stream.writeVal((%s)%s);", getTypeName(Object.class), code)); + return; + } } if (!isCollectionValueNullable) { @@ -312,17 +318,28 @@ public static String getTypeName(Type fieldType) { ParameterizedType pType = (ParameterizedType) fieldType; Class clazz = (Class) pType.getRawType(); return clazz.getCanonicalName(); + } else if (fieldType instanceof WildcardType) { + return Object.class.getCanonicalName(); } else { throw new JsonException("unsupported type: " + fieldType); } } public static CodegenResult genEnum(Class clazz) { + boolean noIndention = JsoniterSpi.getCurrentConfig().indentionStep() == 0; CodegenResult ctx = new CodegenResult(); ctx.append(String.format("public static void encode_(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException {", clazz.getCanonicalName())); ctx.append("if (obj == null) { stream.writeNull(); return; }"); - ctx.buffer('"'); + if (noIndention) { + ctx.buffer('"'); + } else { + ctx.append("stream.write('\"');"); + } ctx.append("stream.writeRaw(obj.toString());"); - ctx.buffer('"'); + if (noIndention) { + ctx.buffer('"'); + } else { + ctx.append("stream.write('\"');"); + } ctx.append("}"); return ctx; } diff --git a/src/main/java/com/jsoniter/output/CodegenImplObject.java b/src/main/java/com/jsoniter/output/CodegenImplObject.java index f3e35e19..2e07f651 100644 --- a/src/main/java/com/jsoniter/output/CodegenImplObject.java +++ b/src/main/java/com/jsoniter/output/CodegenImplObject.java @@ -1,44 +1,54 @@ package com.jsoniter.output; -import com.jsoniter.*; -import com.jsoniter.CodegenAccess; import com.jsoniter.spi.*; -import java.lang.reflect.Method; import java.util.*; class CodegenImplObject { - public static CodegenResult genObject(Class clazz) { - + public static CodegenResult genObject(ClassInfo classInfo) { + boolean noIndention = JsoniterSpi.getCurrentConfig().indentionStep() == 0; CodegenResult ctx = new CodegenResult(); - ClassDescriptor desc = JsoniterSpi.getEncodingClassDescriptor(clazz, false); - HashMap bindings = new HashMap(); - for (Binding binding : desc.allEncoderBindings()) { - for (String toName : binding.toNames) { - bindings.put(toName, binding); - } - } - ArrayList toNames = new ArrayList(bindings.keySet()); - Collections.sort(toNames, new Comparator() { - @Override - public int compare(String o1, String o2) { - int x = CodegenAccess.calcHash(o1); - int y = CodegenAccess.calcHash(o2); - return (x < y) ? -1 : ((x == y) ? 0 : 1); - } - }); - ctx.append(String.format("public static void encode_(%s obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException {", clazz.getCanonicalName())); + ClassDescriptor desc = ClassDescriptor.getEncodingClassDescriptor(classInfo, false); + List encodeTos = desc.encodeTos(); + ctx.append(String.format("public static void encode_(%s obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException {", classInfo.clazz.getCanonicalName())); if (hasFieldOutput(desc)) { int notFirst = 0; - ctx.buffer('{'); - for (String toName : toNames) { - notFirst = genField(ctx, bindings.get(toName), toName, notFirst); + if (noIndention) { + ctx.buffer('{'); + } else { + ctx.append("stream.writeObjectStart();"); + } + for (EncodeTo encodeTo : encodeTos) { + notFirst = genField(ctx, encodeTo.binding, encodeTo.toName, notFirst); } - for (Method unwrapper : desc.unWrappers) { - notFirst = appendComma(ctx, notFirst); - ctx.append(String.format("obj.%s(stream);", unwrapper.getName())); + for (UnwrapperDescriptor unwrapper : desc.unwrappers) { + if (unwrapper.isMap) { + ctx.append(String.format("java.util.Map map = (java.util.Map)obj.%s();", unwrapper.method.getName())); + ctx.append("java.util.Iterator iter = map.entrySet().iterator();"); + ctx.append("while(iter.hasNext()) {"); + ctx.append("java.util.Map.Entry entry = (java.util.Map.Entry)iter.next();"); + notFirst = appendComma(ctx, notFirst); + ctx.append("stream.writeObjectField(entry.getKey().toString());"); + ctx.append("if (entry.getValue() == null) { stream.writeNull(); } else {"); + CodegenImplNative.genWriteOp(ctx, "entry.getValue()", unwrapper.mapValueTypeLiteral.getType(), true); + ctx.append("}"); + ctx.append("}"); + } else { + notFirst = appendComma(ctx, notFirst); + ctx.append(String.format("obj.%s(stream);", unwrapper.method.getName())); + } + } + if (noIndention) { + ctx.buffer('}'); + } else { + if (notFirst == 1) { // definitely not first + ctx.append("stream.writeObjectEnd();"); + } else if (notFirst == 2) { // // maybe not first, previous field is omitNull + ctx.append("if (notFirst) { stream.writeObjectEnd(); } else { stream.write('}'); }"); + } else { // this is the first + ctx.append("stream.write('}');"); + } } - ctx.buffer('}'); } else { ctx.buffer("{}"); } @@ -48,18 +58,14 @@ public int compare(String o1, String o2) { private static boolean hasFieldOutput(ClassDescriptor desc) { - if (!desc.unWrappers.isEmpty()) { + if (!desc.unwrappers.isEmpty()) { return true; } - for (Binding binding : desc.allEncoderBindings()) { - if (binding.toNames.length > 0) { - return true; - } - } - return false; + return !desc.encodeTos().isEmpty(); } private static int genField(CodegenResult ctx, Binding binding, String toName, int notFirst) { + boolean noIndention = JsoniterSpi.getCurrentConfig().indentionStep() == 0; String fieldCacheKey = binding.encoderCacheKey(); Encoder encoder = JsoniterSpi.getEncoder(fieldCacheKey); boolean isCollectionValueNullable = binding.isCollectionValueNullable; @@ -76,32 +82,36 @@ private static int genField(CodegenResult ctx, Binding binding, String toName, i isCollectionValueNullable = true; } boolean nullable = !valueClazz.isPrimitive(); + boolean omitZero = JsoniterSpi.getCurrentConfig().omitDefaultValue(); if (!binding.isNullable) { nullable = false; } - if (nullable) { - if (binding.shouldOmitNull) { - if (notFirst == 0) { // no previous field - notFirst = 2; // maybe - ctx.append("boolean notFirst = false;"); - } - ctx.append(String.format("if (%s != null) {", valueAccessor)); - notFirst = appendComma(ctx, notFirst); + if (binding.defaultValueToOmit != null) { + if (notFirst == 0) { // no previous field + notFirst = 2; // maybe + ctx.append("boolean notFirst = false;"); + } + + ctx.append("if (!(" + String.format(binding.defaultValueToOmit.code(), valueAccessor)+ ")) {"); + notFirst = appendComma(ctx, notFirst); + if (noIndention) { ctx.append(CodegenResult.bufferToWriteOp("\"" + toName + "\":")); } else { - notFirst = appendComma(ctx, notFirst); + ctx.append(String.format("stream.writeObjectField(\"%s\");", toName)); + } + } else { + notFirst = appendComma(ctx, notFirst); + if (noIndention) { ctx.buffer('"'); ctx.buffer(toName); ctx.buffer('"'); ctx.buffer(':'); + } else { + ctx.append(String.format("stream.writeObjectField(\"%s\");", toName)); + } + if (nullable) { ctx.append(String.format("if (%s == null) { stream.writeNull(); } else {", valueAccessor)); } - } else { - notFirst = appendComma(ctx, notFirst); - ctx.buffer('"'); - ctx.buffer(toName); - ctx.buffer('"'); - ctx.buffer(':'); } if (encoder == null) { CodegenImplNative.genWriteOp(ctx, valueAccessor, binding.valueType, nullable, isCollectionValueNullable); @@ -109,19 +119,31 @@ private static int genField(CodegenResult ctx, Binding binding, String toName, i ctx.append(String.format("com.jsoniter.output.CodegenAccess.writeVal(\"%s\", %s, stream);", fieldCacheKey, valueAccessor)); } - if (nullable) { + if (nullable || omitZero) { ctx.append("}"); } return notFirst; } private static int appendComma(CodegenResult ctx, int notFirst) { + boolean noIndention = JsoniterSpi.getCurrentConfig().indentionStep() == 0; if (notFirst == 1) { // definitely not first - ctx.buffer(','); + if (noIndention) { + ctx.buffer(','); + } else { + ctx.append("stream.writeMore();"); + } } else if (notFirst == 2) { // maybe not first, previous field is omitNull - ctx.append("if (notFirst) { stream.write(','); } else { notFirst = true; }"); + if (noIndention) { + ctx.append("if (notFirst) { stream.write(','); } else { notFirst = true; }"); + } else { + ctx.append("if (notFirst) { stream.writeMore(); } else { stream.writeIndention(); notFirst = true; }"); + } } else { // this is the first, do not write comma notFirst = 1; + if (!noIndention) { + ctx.append("stream.writeIndention();"); + } } return notFirst; } diff --git a/src/main/java/com/jsoniter/output/CodegenResult.java b/src/main/java/com/jsoniter/output/CodegenResult.java index a998fa8f..0271ba2b 100644 --- a/src/main/java/com/jsoniter/output/CodegenResult.java +++ b/src/main/java/com/jsoniter/output/CodegenResult.java @@ -1,48 +1,17 @@ package com.jsoniter.output; +import com.jsoniter.spi.JsoniterSpi; + class CodegenResult { + private final boolean supportBuffer; String prelude = null; // first String epilogue = null; // last private StringBuilder lines = new StringBuilder(); private StringBuilder buffered = new StringBuilder(); - public static String bufferToWriteOp(String buffered) { - if (buffered == null) { - return ""; - } - if (buffered.length() == 1) { - return String.format("stream.write((byte)'%s');", escape(buffered.charAt(0))); - } else if (buffered.length() == 2) { - return String.format("stream.write((byte)'%s', (byte)'%s');", - escape(buffered.charAt(0)), escape(buffered.charAt(1))); - } else if (buffered.length() == 3) { - return String.format("stream.write((byte)'%s', (byte)'%s', (byte)'%s');", - escape(buffered.charAt(0)), escape(buffered.charAt(1)), escape(buffered.charAt(2))); - } else if (buffered.length() == 4) { - return String.format("stream.write((byte)'%s', (byte)'%s', (byte)'%s', (byte)'%s');", - escape(buffered.charAt(0)), escape(buffered.charAt(1)), escape(buffered.charAt(2)), escape(buffered.charAt(3))); - } else { - StringBuilder escaped = new StringBuilder(); - for (int i = 0; i < buffered.length(); i++) { - char c = buffered.charAt(i); - if (c == '"') { - escaped.append('\\'); - } - escaped.append(c); - } - return String.format("stream.writeRaw(\"%s\", %s);", escaped.toString(), buffered.length()); - } - } - - private static String escape(char c) { - if (c == '"') { - return "\\\""; - } - if (c == '\\') { - return "\\\\"; - } - return String.valueOf(c); + public CodegenResult() { + supportBuffer = JsoniterSpi.getCurrentConfig().indentionStep() == 0; } public void append(String str) { @@ -56,14 +25,23 @@ public void append(String str) { } public void buffer(char c) { - buffered.append(c); + if (supportBuffer) { + buffered.append(c); + } else { + throw new UnsupportedOperationException("internal error: should not call buffer when indention step > 0"); + } } public void buffer(String s) { if (s == null) { return; } - buffered.append(s); + if (supportBuffer) { + buffered.append(s); + } else { + throw new UnsupportedOperationException("internal error: should not call buffer when indention step > 0"); + + } } public void flushBuffer() { @@ -94,7 +72,7 @@ public void appendBuffer() { public String generateWrapperCode(Class clazz) { flushBuffer(); StringBuilder lines = new StringBuilder(); - append(lines, "public void encode(Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException {"); + append(lines, "public void encode(java.lang.Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException {"); append(lines, "if (obj == null) { stream.writeNull(); return; }"); if (prelude != null) { append(lines, CodegenResult.bufferToWriteOp(prelude)); @@ -111,4 +89,42 @@ private static void append(StringBuilder lines, String line) { lines.append(line); lines.append('\n'); } + + public static String bufferToWriteOp(String buffered) { + if (buffered == null) { + return ""; + } + if (buffered.length() == 1) { + return String.format("stream.write((byte)'%s');", escape(buffered.charAt(0))); + } else if (buffered.length() == 2) { + return String.format("stream.write((byte)'%s', (byte)'%s');", + escape(buffered.charAt(0)), escape(buffered.charAt(1))); + } else if (buffered.length() == 3) { + return String.format("stream.write((byte)'%s', (byte)'%s', (byte)'%s');", + escape(buffered.charAt(0)), escape(buffered.charAt(1)), escape(buffered.charAt(2))); + } else if (buffered.length() == 4) { + return String.format("stream.write((byte)'%s', (byte)'%s', (byte)'%s', (byte)'%s');", + escape(buffered.charAt(0)), escape(buffered.charAt(1)), escape(buffered.charAt(2)), escape(buffered.charAt(3))); + } else { + StringBuilder escaped = new StringBuilder(); + for (int i = 0; i < buffered.length(); i++) { + char c = buffered.charAt(i); + if (c == '"') { + escaped.append('\\'); + } + escaped.append(c); + } + return String.format("stream.writeRaw(\"%s\", %s);", escaped.toString(), buffered.length()); + } + } + + private static String escape(char c) { + if (c == '"') { + return "\\\""; + } + if (c == '\\') { + return "\\\\"; + } + return String.valueOf(c); + } } diff --git a/src/main/java/com/jsoniter/output/DynamicCodegen.java b/src/main/java/com/jsoniter/output/DynamicCodegen.java index 4789a5b9..00f07197 100644 --- a/src/main/java/com/jsoniter/output/DynamicCodegen.java +++ b/src/main/java/com/jsoniter/output/DynamicCodegen.java @@ -1,9 +1,6 @@ package com.jsoniter.output; -import com.jsoniter.spi.Decoder; -import com.jsoniter.spi.EmptyEncoder; import com.jsoniter.spi.Encoder; -import com.sun.org.apache.xml.internal.utils.StringBufferPool; import javassist.*; class DynamicCodegen { @@ -18,7 +15,6 @@ public static Encoder gen(Class clazz, String cacheKey, CodegenResult source) th source.flushBuffer(); CtClass ctClass = pool.makeClass(cacheKey); ctClass.setInterfaces(new CtClass[]{pool.get(Encoder.class.getName())}); - ctClass.setSuperclass(pool.get(EmptyEncoder.class.getName())); String staticCode = source.toString(); CtMethod staticMethod = CtNewMethod.make(staticCode, ctClass); ctClass.addMethod(staticMethod); diff --git a/src/main/java/com/jsoniter/output/JsonStream.java b/src/main/java/com/jsoniter/output/JsonStream.java index f7a84fc9..7886bc05 100644 --- a/src/main/java/com/jsoniter/output/JsonStream.java +++ b/src/main/java/com/jsoniter/output/JsonStream.java @@ -1,18 +1,16 @@ package com.jsoniter.output; -import com.jsoniter.spi.JsonException; import com.jsoniter.any.Any; -import com.jsoniter.spi.Encoder; -import com.jsoniter.spi.TypeLiteral; +import com.jsoniter.spi.*; import java.io.IOException; import java.io.OutputStream; +import java.lang.reflect.Type; public class JsonStream extends OutputStream { - public static int defaultIndentionStep = 0; - public int indentionStep = defaultIndentionStep; - private int indention = 0; + public Config configCache; + int indention = 0; private OutputStream out; byte buf[]; int count; @@ -30,34 +28,46 @@ public void reset(OutputStream out) { this.count = 0; } - public final void write(int b) throws IOException { - if (count == buf.length) { - flushBuffer(); + final void ensure(int minimal) throws IOException { + int available = buf.length - count; + if (available < minimal) { + if (count > 1024) { + flushBuffer(); + } + growAtLeast(minimal); + } + } + + private final void growAtLeast(int minimal) { + int toGrow = buf.length; + if (toGrow < minimal) { + toGrow = minimal; } + byte[] newBuf = new byte[buf.length + toGrow]; + System.arraycopy(buf, 0, newBuf, 0, buf.length); + buf = newBuf; + } + + public final void write(int b) throws IOException { + ensure(1); buf[count++] = (byte) b; } public final void write(byte b1, byte b2) throws IOException { - if (count >= buf.length - 1) { - flushBuffer(); - } + ensure(2); buf[count++] = b1; buf[count++] = b2; } public final void write(byte b1, byte b2, byte b3) throws IOException { - if (count >= buf.length - 2) { - flushBuffer(); - } + ensure(3); buf[count++] = b1; buf[count++] = b2; buf[count++] = b3; } public final void write(byte b1, byte b2, byte b3, byte b4) throws IOException { - if (count >= buf.length - 3) { - flushBuffer(); - } + ensure(4); buf[count++] = b1; buf[count++] = b2; buf[count++] = b3; @@ -65,9 +75,7 @@ public final void write(byte b1, byte b2, byte b3, byte b4) throws IOException { } public final void write(byte b1, byte b2, byte b3, byte b4, byte b5) throws IOException { - if (count >= buf.length - 4) { - flushBuffer(); - } + ensure(5); buf[count++] = b1; buf[count++] = b2; buf[count++] = b3; @@ -76,9 +84,7 @@ public final void write(byte b1, byte b2, byte b3, byte b4, byte b5) throws IOEx } public final void write(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6) throws IOException { - if (count >= buf.length - 5) { - flushBuffer(); - } + ensure(6); buf[count++] = b1; buf[count++] = b2; buf[count++] = b3; @@ -88,16 +94,20 @@ public final void write(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6) th } public final void write(byte b[], int off, int len) throws IOException { - if (len >= buf.length - count) { - if (len >= buf.length) { + if (out == null) { + ensure(len); + } else { + if (len >= buf.length - count) { + if (len >= buf.length) { /* If the request length exceeds the size of the output buffer, flush the output buffer and then write the data directly. In this way buffered streams will cascade harmlessly. */ + flushBuffer(); + out.write(b, off, len); + return; + } flushBuffer(); - out.write(b, off, len); - return; } - flushBuffer(); } System.arraycopy(b, off, buf, count, len); count += len; @@ -110,6 +120,9 @@ public void flush() throws IOException { @Override public void close() throws IOException { + if (out == null) { + return; + } if (count > 0) { flushBuffer(); } @@ -119,6 +132,9 @@ public void close() throws IOException { } final void flushBuffer() throws IOException { + if (out == null) { + return; + } out.write(buf, 0, count); count = 0; } @@ -136,6 +152,12 @@ public final void writeRaw(String val) throws IOException { } public final void writeRaw(String val, int remaining) throws IOException { + if (out == null) { + ensure(remaining); + val.getBytes(0, remaining, buf, count); + count += remaining; + return; + } int i = 0; for (; ; ) { int available = buf.length - count; @@ -262,9 +284,8 @@ public final void writeEmptyArray() throws IOException { } public final void writeArrayStart() throws IOException { - indention += indentionStep; + indention += currentConfig().indentionStep(); write('['); - writeIndention(); } public final void writeMore() throws IOException { @@ -272,7 +293,7 @@ public final void writeMore() throws IOException { writeIndention(); } - private void writeIndention() throws IOException { + public void writeIndention() throws IOException { writeIndention(0); } @@ -282,37 +303,50 @@ private void writeIndention(int delta) throws IOException { } write('\n'); int toWrite = indention - delta; - int i = 0; - for (; ; ) { - for (; i < toWrite && count < buf.length; i++) { - buf[count++] = ' '; - } - if (i == toWrite) { - break; - } else { - flushBuffer(); - } + ensure(toWrite); + for (int i = 0; i < toWrite && count < buf.length; i++) { + buf[count++] = ' '; } } public final void writeArrayEnd() throws IOException { + int indentionStep = currentConfig().indentionStep(); writeIndention(indentionStep); indention -= indentionStep; write(']'); } public final void writeObjectStart() throws IOException { + int indentionStep = currentConfig().indentionStep(); indention += indentionStep; write('{'); - writeIndention(); } public final void writeObjectField(String field) throws IOException { writeVal(field); - write(':'); + if (indention > 0) { + write((byte) ':', (byte) ' '); + } else { + write(':'); + } + } + + 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); indention -= indentionStep; write('}'); @@ -324,7 +358,7 @@ public final void writeVal(Object obj) throws IOException { return; } Class clazz = obj.getClass(); - String cacheKey = TypeLiteral.create(clazz).getEncoderCacheKey(); + String cacheKey = currentConfig().getEncoderCacheKey(clazz); Codegen.getEncoder(cacheKey, clazz).encode(obj, this); } @@ -332,19 +366,42 @@ public final void writeVal(TypeLiteral typeLiteral, T obj) throws IOExcep if (null == obj) { writeNull(); } else { - Codegen.getEncoder(typeLiteral.getEncoderCacheKey(), typeLiteral.getType()).encode(obj, this); + Config config = currentConfig(); + String cacheKey = config.getEncoderCacheKey(typeLiteral.getType()); + Codegen.getEncoder(cacheKey, typeLiteral.getType()).encode(obj, this); + } + } + + public final void writeVal(Type type, T obj) throws IOException { + if (null == obj) { + writeNull(); + } else { + Config config = currentConfig(); + String cacheKey = config.getEncoderCacheKey(type); + Codegen.getEncoder(cacheKey, type).encode(obj, this); + } + } + + public Config currentConfig() { + if (configCache != null) { + return configCache; } + configCache = JsoniterSpi.getCurrentConfig(); + return configCache; } - private final static ThreadLocal tlsStream = new ThreadLocal() { - @Override - protected JsonStream initialValue() { - return new JsonStream(null, 4096); + public static void serialize(Config config, Object obj, OutputStream out) { + JsoniterSpi.setCurrentConfig(config); + try { + serialize(obj, out); + } finally { + JsoniterSpi.clearCurrentConfig(); } - }; + + } public static void serialize(Object obj, OutputStream out) { - JsonStream stream = tlsStream.get(); + JsonStream stream = JsonStreamPool.borrowJsonStream(); try { try { stream.reset(out); @@ -354,28 +411,120 @@ public static void serialize(Object obj, OutputStream out) { } } catch (IOException e) { throw new JsonException(e); + } finally { + JsonStreamPool.returnJsonStream(stream); } } - private final static ThreadLocal tlsAsciiOutputStream = new ThreadLocal() { - @Override - protected AsciiOutputStream initialValue() { - return new AsciiOutputStream(); + public static void serialize(Config config, TypeLiteral typeLiteral, Object obj, OutputStream out) { + JsoniterSpi.setCurrentConfig(config); + try { + serialize(typeLiteral, obj, out); + } finally { + JsoniterSpi.clearCurrentConfig(); } - }; + } + + public static void serialize(TypeLiteral typeLiteral, Object obj, OutputStream out) { + JsonStream stream = JsonStreamPool.borrowJsonStream(); + try { + try { + stream.reset(out); + stream.writeVal(typeLiteral, obj); + } finally { + stream.close(); + } + } catch (IOException e) { + throw new JsonException(e); + } finally { + JsonStreamPool.returnJsonStream(stream); + } + } + + public static void serialize(Type type, Object obj, OutputStream out) { + serialize(type, obj, out, false); + } + + public static String serialize(Config config, Object obj) { + return serialize(config, obj.getClass(), obj); + } public static String serialize(Object obj) { - AsciiOutputStream asciiOutputStream = tlsAsciiOutputStream.get(); - asciiOutputStream.reset(); - serialize(obj, asciiOutputStream); - return asciiOutputStream.toString(); + 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(type, obj); + } finally { + // Revert old config + JsoniterSpi.setCurrentConfig(configBackup); + } + } + + public static String serialize(TypeLiteral typeLiteral, Object obj) { + return serialize(typeLiteral.getType(), obj); + } + + public static String serialize(boolean escapeUnicode, Type type, Object obj) { + 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 { + 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 { + return new String(stream.buf, 0, stream.count, "UTF8"); + } + } catch (IOException e) { + throw new JsonException(e); + } finally { + JsonStreamPool.returnJsonStream(stream); + } } public static void setMode(EncodingMode mode) { - Codegen.setMode(mode); + Config newConfig = JsoniterSpi.getDefaultConfig().copyBuilder().encodingMode(mode).build(); + JsoniterSpi.setDefaultConfig(newConfig); + JsoniterSpi.setCurrentConfig(newConfig); + + } + + public static void setIndentionStep(int indentionStep) { + Config newConfig = JsoniterSpi.getDefaultConfig().copyBuilder().indentionStep(indentionStep).build(); + JsoniterSpi.setDefaultConfig(newConfig); + JsoniterSpi.setCurrentConfig(newConfig); } - public static void registerNativeEncoder(Class clazz, Encoder encoder) { + public static void registerNativeEncoder(Class clazz, Encoder.ReflectionEncoder encoder) { CodegenImplNative.NATIVE_ENCODERS.put(clazz, encoder); } + + public Slice buffer() { + return new Slice(buf, 0, count); + } } diff --git a/src/main/java/com/jsoniter/output/JsonStreamPool.java b/src/main/java/com/jsoniter/output/JsonStreamPool.java new file mode 100644 index 00000000..5a2acb87 --- /dev/null +++ b/src/main/java/com/jsoniter/output/JsonStreamPool.java @@ -0,0 +1,35 @@ +package com.jsoniter.output; + +public class JsonStreamPool { + + private final static ThreadLocal slot1 = new ThreadLocal(); + private final static ThreadLocal slot2 = new ThreadLocal(); + + public static JsonStream borrowJsonStream() { + JsonStream stream = slot1.get(); + if (stream != null) { + slot1.set(null); + return stream; + } + stream = slot2.get(); + if (stream != null) { + slot2.set(null); + return stream; + } + return new JsonStream(null, 512); + } + + public static void returnJsonStream(JsonStream jsonStream) { + jsonStream.configCache = null; + jsonStream.indention = 0; + if (slot1.get() == null) { + slot1.set(jsonStream); + return; + } + if (slot2.get() == null) { + slot2.set(jsonStream); + return; + } + } + +} 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/ReflectionArrayEncoder.java b/src/main/java/com/jsoniter/output/ReflectionArrayEncoder.java index 7d316dce..bac1dbae 100644 --- a/src/main/java/com/jsoniter/output/ReflectionArrayEncoder.java +++ b/src/main/java/com/jsoniter/output/ReflectionArrayEncoder.java @@ -8,7 +8,7 @@ import java.lang.reflect.Array; import java.lang.reflect.Type; -class ReflectionArrayEncoder implements Encoder { +class ReflectionArrayEncoder implements Encoder.ReflectionEncoder { private final TypeLiteral compTypeLiteral; @@ -28,6 +28,7 @@ public void encode(Object obj, JsonStream stream) throws IOException { return; } stream.writeArrayStart(); + stream.writeIndention(); stream.writeVal(compTypeLiteral, Array.get(obj, 0)); for (int i = 1; i < len; i++) { stream.writeMore(); diff --git a/src/main/java/com/jsoniter/output/ReflectionCollectionEncoder.java b/src/main/java/com/jsoniter/output/ReflectionCollectionEncoder.java index 0d7d5a38..9479d0a7 100644 --- a/src/main/java/com/jsoniter/output/ReflectionCollectionEncoder.java +++ b/src/main/java/com/jsoniter/output/ReflectionCollectionEncoder.java @@ -9,7 +9,7 @@ import java.util.Collection; import java.util.Iterator; -class ReflectionCollectionEncoder implements Encoder { +class ReflectionCollectionEncoder implements Encoder.ReflectionEncoder { private final TypeLiteral compTypeLiteral; @@ -34,6 +34,7 @@ public void encode(Object obj, JsonStream stream) throws IOException { return; } stream.writeArrayStart(); + stream.writeIndention(); stream.writeVal(compTypeLiteral, iter.next()); while (iter.hasNext()) { stream.writeMore(); diff --git a/src/main/java/com/jsoniter/output/ReflectionEncoderFactory.java b/src/main/java/com/jsoniter/output/ReflectionEncoderFactory.java index e5e1e844..3825225d 100644 --- a/src/main/java/com/jsoniter/output/ReflectionEncoderFactory.java +++ b/src/main/java/com/jsoniter/output/ReflectionEncoderFactory.java @@ -1,5 +1,6 @@ package com.jsoniter.output; +import com.jsoniter.spi.ClassInfo; import com.jsoniter.spi.Encoder; import java.lang.reflect.Type; @@ -9,7 +10,9 @@ public class ReflectionEncoderFactory { - public static Encoder create(Class clazz, Type... typeArgs) { + public static Encoder.ReflectionEncoder create(ClassInfo classInfo) { + Class clazz = classInfo.clazz; + Type[] typeArgs = classInfo.typeArgs; if (clazz.isArray()) { return new ReflectionArrayEncoder(clazz, typeArgs); } @@ -25,6 +28,6 @@ public static Encoder create(Class clazz, Type... typeArgs) { if (clazz.isEnum()) { return new ReflectionEnumEncoder(clazz); } - return new ReflectionObjectEncoder(clazz); + return new ReflectionObjectEncoder(classInfo); } } diff --git a/src/main/java/com/jsoniter/output/ReflectionEnumEncoder.java b/src/main/java/com/jsoniter/output/ReflectionEnumEncoder.java index 3c1c547f..4324e6c9 100644 --- a/src/main/java/com/jsoniter/output/ReflectionEnumEncoder.java +++ b/src/main/java/com/jsoniter/output/ReflectionEnumEncoder.java @@ -5,7 +5,7 @@ import java.io.IOException; -class ReflectionEnumEncoder implements Encoder { +class ReflectionEnumEncoder implements Encoder.ReflectionEncoder { public ReflectionEnumEncoder(Class clazz) { } diff --git a/src/main/java/com/jsoniter/output/ReflectionListEncoder.java b/src/main/java/com/jsoniter/output/ReflectionListEncoder.java index 8d276edf..bfeccafa 100644 --- a/src/main/java/com/jsoniter/output/ReflectionListEncoder.java +++ b/src/main/java/com/jsoniter/output/ReflectionListEncoder.java @@ -8,7 +8,7 @@ import java.lang.reflect.Type; import java.util.List; -class ReflectionListEncoder implements Encoder { +class ReflectionListEncoder implements Encoder.ReflectionEncoder { private final TypeLiteral compTypeLiteral; @@ -32,6 +32,7 @@ public void encode(Object obj, JsonStream stream) throws IOException { return; } stream.writeArrayStart(); + stream.writeIndention(); stream.writeVal(compTypeLiteral, list.get(0)); for (int i = 1; i < list.size(); i++) { stream.writeMore(); diff --git a/src/main/java/com/jsoniter/output/ReflectionMapEncoder.java b/src/main/java/com/jsoniter/output/ReflectionMapEncoder.java index 08274387..ddf27d65 100644 --- a/src/main/java/com/jsoniter/output/ReflectionMapEncoder.java +++ b/src/main/java/com/jsoniter/output/ReflectionMapEncoder.java @@ -1,24 +1,27 @@ package com.jsoniter.output; import com.jsoniter.any.Any; -import com.jsoniter.spi.Encoder; -import com.jsoniter.spi.TypeLiteral; +import com.jsoniter.spi.*; import java.io.IOException; import java.lang.reflect.Type; -import java.util.HashMap; +import java.util.Iterator; import java.util.Map; -class ReflectionMapEncoder implements Encoder { +class ReflectionMapEncoder implements Encoder.ReflectionEncoder { private final TypeLiteral valueTypeLiteral; + private final Encoder mapKeyEncoder; public ReflectionMapEncoder(Class clazz, Type[] typeArgs) { - if (typeArgs.length > 1) { - valueTypeLiteral = TypeLiteral.create(typeArgs[1]); - } else { - valueTypeLiteral = TypeLiteral.create(Object.class); + Type keyType = Object.class; + Type valueType = Object.class; + if (typeArgs.length == 2) { + keyType = typeArgs[0]; + valueType = typeArgs[1]; } + mapKeyEncoder = MapKeyEncoders.registerOrGetExisting(keyType); + valueTypeLiteral = TypeLiteral.create(valueType); } @Override @@ -27,21 +30,35 @@ public void encode(Object obj, JsonStream stream) throws IOException { stream.writeNull(); return; } - Map map = (Map) obj; + Map map = (Map) obj; + Iterator> iter = map.entrySet().iterator(); + if (!iter.hasNext()) { + stream.write((byte) '{', (byte) '}'); + return; + } stream.writeObjectStart(); boolean notFirst = false; - for (Map.Entry entry : map.entrySet()) { - if (notFirst) { - stream.writeMore(); - } else { - notFirst = true; - } - stream.writeObjectField(entry.getKey()); - stream.writeVal(valueTypeLiteral, entry.getValue()); + Map.Entry entry = iter.next(); + notFirst = writeEntry(stream, notFirst, entry); + while (iter.hasNext()) { + entry = iter.next(); + notFirst = writeEntry(stream, notFirst, entry); } stream.writeObjectEnd(); } + private boolean writeEntry(JsonStream stream, boolean notFirst, Map.Entry entry) throws IOException { + if (notFirst) { + stream.writeMore(); + } else { + stream.writeIndention(); + notFirst = true; + } + stream.writeObjectField(entry.getKey(), mapKeyEncoder); + stream.writeVal(valueTypeLiteral, entry.getValue()); + return notFirst; + } + @Override public Any wrap(Object obj) { Map map = (Map) obj; diff --git a/src/main/java/com/jsoniter/output/ReflectionObjectEncoder.java b/src/main/java/com/jsoniter/output/ReflectionObjectEncoder.java index 6c875db8..99c256c3 100644 --- a/src/main/java/com/jsoniter/output/ReflectionObjectEncoder.java +++ b/src/main/java/com/jsoniter/output/ReflectionObjectEncoder.java @@ -1,27 +1,33 @@ package com.jsoniter.output; -import com.jsoniter.spi.JsonException; +import com.jsoniter.spi.*; import com.jsoniter.any.Any; -import com.jsoniter.spi.Binding; -import com.jsoniter.spi.ClassDescriptor; -import com.jsoniter.spi.Encoder; -import com.jsoniter.spi.JsoniterSpi; import java.io.IOException; -import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; +import java.util.Map; -class ReflectionObjectEncoder implements Encoder { +class ReflectionObjectEncoder implements Encoder.ReflectionEncoder { private final ClassDescriptor desc; + private final List fields = new ArrayList(); + private final List getters = new ArrayList(); - public ReflectionObjectEncoder(Class clazz) { - desc = JsoniterSpi.getEncodingClassDescriptor(clazz, true); - for (Binding binding : desc.allEncoderBindings()) { + public ReflectionObjectEncoder(ClassInfo classInfo) { + desc = ClassDescriptor.getEncodingClassDescriptor(classInfo, true); + for (EncodeTo encodeTo : desc.encodeTos()) { + Binding binding = encodeTo.binding; if (binding.encoder == null) { // the field encoder might be registered directly binding.encoder = JsoniterSpi.getEncoder(binding.encoderCacheKey()); } + if (binding.field != null) { + fields.add(encodeTo); + } else { + getters.add(encodeTo); + } } } @@ -29,6 +35,8 @@ public ReflectionObjectEncoder(Class clazz) { public void encode(Object obj, JsonStream stream) throws IOException { try { enocde_(obj, stream); + } catch (JsonException e) { + throw e; } catch (Exception e) { throw new JsonException(e); } @@ -38,18 +46,16 @@ public void encode(Object obj, JsonStream stream) throws IOException { public Any wrap(Object obj) { HashMap copied = new HashMap(); try { - for (Binding field : desc.fields) { - Object val = field.field.get(obj); - for (String toName : field.toNames) { - copied.put(toName, val); - } + for (EncodeTo encodeTo : fields) { + Object val = encodeTo.binding.field.get(obj); + copied.put(encodeTo.toName, val); } - for (Binding getter : desc.getters) { - Object val = getter.method.invoke(obj); - for (String toName : getter.toNames) { - copied.put(toName, val); - } + for (EncodeTo getter : getters) { + Object val = getter.binding.method.invoke(obj); + copied.put(getter.toName, val); } + } catch (JsonException e) { + throw e; } catch (Exception e) { throw new JsonException(e); } @@ -63,50 +69,58 @@ private void enocde_(Object obj, JsonStream stream) throws Exception { } stream.writeObjectStart(); boolean notFirst = false; - for (Binding field : desc.fields) { - Object val = field.field.get(obj); - for (String toName : field.toNames) { - if (!(field.shouldOmitNull && val == null)) { - if (notFirst) { - stream.writeMore(); - } else { - notFirst = true; - } - stream.writeObjectField(toName); - if (field.encoder != null) { - field.encoder.encode(val, stream); - } else { - stream.writeVal(val); - } - } - } + for (EncodeTo encodeTo : fields) { + Object val = encodeTo.binding.field.get(obj); + notFirst = writeEncodeTo(stream, notFirst, encodeTo, val); + } + for (EncodeTo encodeTo : getters) { + Object val = encodeTo.binding.method.invoke(obj); + notFirst = writeEncodeTo(stream, notFirst, encodeTo, val); } - for (Binding getter : desc.getters) { - Object val = getter.method.invoke(obj); - for (String toName : getter.toNames) { - if (!(getter.shouldOmitNull && val == null)) { + for (UnwrapperDescriptor unwrapper : desc.unwrappers) { + if (unwrapper.isMap) { + Map map = (Map) unwrapper.method.invoke(obj); + for (Map.Entry entry : map.entrySet()) { if (notFirst) { stream.writeMore(); } else { notFirst = true; } - stream.writeObjectField(toName); - if (getter.encoder != null) { - getter.encoder.encode(val, stream); - } else { - stream.writeVal(val); - } + stream.writeObjectField(entry.getKey().toString()); + stream.writeVal(unwrapper.mapValueTypeLiteral, entry.getValue()); + } + } else { + if (notFirst) { + stream.writeMore(); + } else { + notFirst = true; } + unwrapper.method.invoke(obj, stream); } } - for (Method unwrapper : desc.unWrappers) { + if (notFirst) { + stream.writeObjectEnd(); + } else { + stream.write('}'); + } + } + + private boolean writeEncodeTo(JsonStream stream, boolean notFirst, EncodeTo encodeTo, Object val) throws IOException { + OmitValue defaultValueToOmit = encodeTo.binding.defaultValueToOmit; + if (!(defaultValueToOmit != null && defaultValueToOmit.shouldOmit(val))) { if (notFirst) { stream.writeMore(); } else { + stream.writeIndention(); notFirst = true; } - unwrapper.invoke(obj, stream); + stream.writeObjectField(encodeTo.toName); + if (encodeTo.binding.encoder != null) { + encodeTo.binding.encoder.encode(val, stream); + } else { + stream.writeVal(val); + } } - stream.writeObjectEnd(); + return notFirst; } } diff --git a/src/main/java/com/jsoniter/output/StreamImplNumber.java b/src/main/java/com/jsoniter/output/StreamImplNumber.java index c225c5d9..54e039c6 100644 --- a/src/main/java/com/jsoniter/output/StreamImplNumber.java +++ b/src/main/java/com/jsoniter/output/StreamImplNumber.java @@ -49,9 +49,7 @@ class StreamImplNumber { private static final byte[] MIN_INT = "-2147483648".getBytes(); public static final void writeInt(final JsonStream stream, int value) throws IOException { - if (stream.buf.length - stream.count < 11) { - stream.flushBuffer(); - } + stream.ensure(12); byte[] buf = stream.buf; int pos = stream.count; if (value < 0) { @@ -117,9 +115,7 @@ private static void writeBuf(final byte[] buf, final int v, int pos) { private static final byte[] MIN_LONG = "-9223372036854775808".getBytes(); public static final void writeLong(final JsonStream stream, long value) throws IOException { - if (stream.buf.length - stream.count < 21) { - stream.flushBuffer(); - } + stream.ensure(22); byte[] buf = stream.buf; int pos = stream.count; if (value < 0) { @@ -216,10 +212,18 @@ public static final void writeLong(final JsonStream stream, long value) throws I public static final void writeFloat(JsonStream stream, float val) throws IOException { if (val < 0) { + if (val == Float.NEGATIVE_INFINITY) { + stream.writeVal("-Infinity"); + return; + } stream.write('-'); val = -val; } if (val > 0x4ffffff) { + if (val == Float.POSITIVE_INFINITY) { + stream.writeVal("Infinity"); + return; + } stream.writeRaw(Float.toString(val)); return; } @@ -232,9 +236,7 @@ public static final void writeFloat(JsonStream stream, float val) throws IOExcep return; } stream.write('.'); - if (stream.buf.length - stream.count < 10) { - stream.flushBuffer(); - } + stream.ensure(11); for (int p = precision - 1; p > 0 && fval < POW10[p]; p--) { stream.buf[stream.count++] = '0'; } @@ -246,10 +248,18 @@ public static final void writeFloat(JsonStream stream, float val) throws IOExcep public static final void writeDouble(JsonStream stream, double val) throws IOException { if (val < 0) { + if (val == Double.NEGATIVE_INFINITY) { + stream.writeVal("-Infinity"); + return; + } val = -val; stream.write('-'); } if (val > 0x4ffffff) { + if (val == Double.POSITIVE_INFINITY) { + stream.writeVal("Infinity"); + return; + } stream.writeRaw(Double.toString(val)); return; } @@ -262,9 +272,7 @@ public static final void writeDouble(JsonStream stream, double val) throws IOExc return; } stream.write('.'); - if (stream.buf.length - stream.count < 10) { - stream.flushBuffer(); - } + stream.ensure(11); for (int p = precision - 1; p > 0 && fval < POW10[p]; p--) { stream.buf[stream.count++] = '0'; } diff --git a/src/main/java/com/jsoniter/output/StreamImplString.java b/src/main/java/com/jsoniter/output/StreamImplString.java index b7bb25d2..7c4a27a7 100644 --- a/src/main/java/com/jsoniter/output/StreamImplString.java +++ b/src/main/java/com/jsoniter/output/StreamImplString.java @@ -31,6 +31,8 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ package com.jsoniter.output; +import com.jsoniter.spi.JsonException; + import java.io.IOException; class StreamImplString { @@ -39,10 +41,14 @@ class StreamImplString { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; private static final boolean[] CAN_DIRECT_WRITE = new boolean[128]; + private final static int SURR1_FIRST = 0xD800; + private final static int SURR1_LAST = 0xDBFF; + private final static int SURR2_FIRST = 0xDC00; + private final static int SURR2_LAST = 0xDFFF; static { for (int i = 0; i < CAN_DIRECT_WRITE.length; i++) { - if (i > 31 && i < 126 && i != '"' && i != '\\') { + if (i > 31 && i <= 126 && i != '"' && i != '\\') { CAN_DIRECT_WRITE[i] = true; } } @@ -57,7 +63,7 @@ public static final void writeString(final JsonStream stream, final String val) toWriteLen = bufLengthMinusTwo - stream.count; } if (toWriteLen < 0) { - stream.flushBuffer(); + stream.ensure(32); if (stream.count + toWriteLen > bufLengthMinusTwo) { toWriteLen = bufLengthMinusTwo - stream.count; } @@ -97,7 +103,7 @@ public static final void writeStringWithoutQuote(final JsonStream stream, final toWriteLen = bufLen - stream.count; } if (toWriteLen < 0) { - stream.flushBuffer(); + stream.ensure(32); if (stream.count + toWriteLen > bufLen) { toWriteLen = bufLen - stream.count; } @@ -122,41 +128,110 @@ public static final void writeStringWithoutQuote(final JsonStream stream, final } private static void writeStringSlowPath(JsonStream stream, String val, int i, int valLen) throws IOException { + boolean escapeUnicode = stream.currentConfig().escapeUnicode(); + if (escapeUnicode) { + for (; i < valLen; i++) { + int c = val.charAt(i); + if (c > 127) { + writeAsSlashU(stream, c); + } else { + writeAsciiChar(stream, c); + } + } + } else { + writeStringSlowPathWithoutEscapeUnicode(stream, val, i, valLen); + } + } + + private static void writeStringSlowPathWithoutEscapeUnicode(JsonStream stream, String val, int i, int valLen) throws IOException { + int _surrogate; for (; i < valLen; i++) { int c = val.charAt(i); - if (c > 125) { - byte b4 = (byte) (c & 0xf); - byte b3 = (byte) (c >> 4 & 0xf); - byte b2 = (byte) (c >> 8 & 0xf); - byte b1 = (byte) (c >> 12 & 0xf); - stream.write((byte) '\\', (byte) 'u', ITOA[b1], ITOA[b2], ITOA[b3], ITOA[b4]); - } else { - switch (c) { - case '"': - stream.write((byte) '\\', (byte) '"'); + if (c > 127) { + if (c < 0x800) { // 2-byte + stream.write( + (byte) (0xc0 | (c >> 6)), + (byte) (0x80 | (c & 0x3f)) + ); + } else { // 3 or 4 bytes + // Surrogates? + if (c < SURR1_FIRST || c > SURR2_LAST) { + stream.write( + (byte) (0xe0 | (c >> 12)), + (byte) (0x80 | ((c >> 6) & 0x3f)), + (byte) (0x80 | (c & 0x3f)) + ); + continue; + } + // Yup, a surrogate: + if (c > SURR1_LAST) { // must be from first range + throw new JsonException("illegalSurrogate"); + } + _surrogate = c; + // and if so, followed by another from next range + if (i >= valLen) { // unless we hit the end? break; - case '\\': - stream.write((byte) '\\', (byte) '\\'); - break; - case '\b': - stream.write((byte) '\\', (byte) 'b'); - break; - case '\f': - stream.write((byte) '\\', (byte) 'f'); - break; - case '\n': - stream.write((byte) '\\', (byte) 'n'); - break; - case '\r': - stream.write((byte) '\\', (byte) 'r'); - break; - case '\t': - stream.write((byte) '\\', (byte) 't'); - break; - default: - stream.write(c); + } + int firstPart = _surrogate; + _surrogate = 0; + // Ok, then, is the second part valid? + if (c < SURR2_FIRST || c > SURR2_LAST) { + throw new JsonException("Broken surrogate pair: first char 0x" + Integer.toHexString(firstPart) + ", second 0x" + Integer.toHexString(c) + "; illegal combination"); + } + c = 0x10000 + ((firstPart - SURR1_FIRST) << 10) + (c - SURR2_FIRST); + if (c > 0x10FFFF) { // illegal in JSON as well as in XML + throw new JsonException("illegalSurrogate"); + } + stream.write( + (byte) (0xf0 | (c >> 18)), + (byte) (0x80 | ((c >> 12) & 0x3f)), + (byte) (0x80 | ((c >> 6) & 0x3f)), + (byte) (0x80 | (c & 0x3f)) + ); } + } else { + writeAsciiChar(stream, c); } } } + + private static void writeAsciiChar(JsonStream stream, int c) throws IOException { + switch (c) { + case '"': + stream.write((byte) '\\', (byte) '"'); + break; + case '\\': + stream.write((byte) '\\', (byte) '\\'); + break; + case '\b': + stream.write((byte) '\\', (byte) 'b'); + break; + case '\f': + stream.write((byte) '\\', (byte) 'f'); + break; + case '\n': + stream.write((byte) '\\', (byte) 'n'); + break; + case '\r': + stream.write((byte) '\\', (byte) 'r'); + break; + case '\t': + stream.write((byte) '\\', (byte) 't'); + break; + default: + if (c < 32) { + writeAsSlashU(stream, c); + } else { + stream.write(c); + } + } + } + + private static void writeAsSlashU(JsonStream stream, int c) throws IOException { + byte b4 = (byte) (c & 0xf); + byte b3 = (byte) (c >> 4 & 0xf); + byte b2 = (byte) (c >> 8 & 0xf); + byte b1 = (byte) (c >> 12 & 0xf); + stream.write((byte) '\\', (byte) 'u', ITOA[b1], ITOA[b2], ITOA[b3], ITOA[b4]); + } } diff --git a/src/main/java/com/jsoniter/spi/Binding.java b/src/main/java/com/jsoniter/spi/Binding.java index eec04647..72afc79d 100644 --- a/src/main/java/com/jsoniter/spi/Binding.java +++ b/src/main/java/com/jsoniter/spi/Binding.java @@ -25,7 +25,7 @@ public class Binding { public boolean asExtraWhenPresent; public boolean isNullable = true; public boolean isCollectionValueNullable = true; - public boolean shouldOmitNull = true; + public OmitValue defaultValueToOmit; // then this property will not be unknown // but we do not want to bind it anywhere public boolean shouldSkip; @@ -33,9 +33,9 @@ public class Binding { public int idx; public long mask; - public Binding(Class clazz, Map lookup, Type valueType) { - this.clazz = clazz; - this.clazzTypeLiteral = TypeLiteral.create(clazz); + public Binding(ClassInfo classInfo, Map lookup, Type valueType) { + this.clazz = classInfo.clazz; + this.clazzTypeLiteral = TypeLiteral.create(classInfo.type); this.valueType = substituteTypeVariables(lookup, valueType); this.valueTypeLiteral = TypeLiteral.create(this.valueType); } @@ -58,11 +58,12 @@ private static Type substituteTypeVariables(Map lookup, Type type) for (int i = 0; i < args.length; i++) { args[i] = substituteTypeVariables(lookup, args[i]); } - return new ParameterizedTypeImpl(args, pType.getOwnerType(), pType.getRawType()); + return GenericsHelper.createParameterizedType(args, pType.getOwnerType(), pType.getRawType()); } if (type instanceof GenericArrayType) { GenericArrayType gaType = (GenericArrayType) type; - return new GenericArrayTypeImpl(substituteTypeVariables(lookup, gaType.getGenericComponentType())); + Type componentType = substituteTypeVariables(lookup, gaType.getGenericComponentType()); + return GenericsHelper.createGenericArrayType(componentType); } return type; } @@ -106,12 +107,14 @@ public boolean equals(Object o) { Binding binding = (Binding) o; if (clazz != null ? !clazz.equals(binding.clazz) : binding.clazz != null) return false; + if (method != null ? !method.equals(binding.method) : binding.method != null) return false; return name != null ? name.equals(binding.name) : binding.name == null; } @Override public int hashCode() { int result = clazz != null ? clazz.hashCode() : 0; + result = 31 * result + (method != null ? method.hashCode() : 0); result = 31 * result + (name != null ? name.hashCode() : 0); return result; } diff --git a/src/main/java/com/jsoniter/spi/ClassDescriptor.java b/src/main/java/com/jsoniter/spi/ClassDescriptor.java index d0735bfa..a47dbe5d 100644 --- a/src/main/java/com/jsoniter/spi/ClassDescriptor.java +++ b/src/main/java/com/jsoniter/spi/ClassDescriptor.java @@ -1,25 +1,404 @@ package com.jsoniter.spi; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.lang.reflect.*; +import java.util.*; + +import static java.lang.reflect.Modifier.isTransient; public class ClassDescriptor { + public ClassInfo classInfo; public Class clazz; public Map lookup; public ConstructorDescriptor ctor; public List fields; public List setters; public List getters; - public List wrappers; - public List unWrappers; + public List bindingTypeWrappers; + public List keyValueTypeWrappers; + public List unwrappers; public boolean asExtraForUnknownProperties; public Binding onMissingProperties; public Binding onExtraProperties; + private ClassDescriptor() { + } + + public static ClassDescriptor getDecodingClassDescriptor(ClassInfo classInfo, boolean includingPrivate) { + Class clazz = classInfo.clazz; + Map lookup = collectTypeVariableLookup(classInfo.type); + ClassDescriptor desc = new ClassDescriptor(); + desc.classInfo = classInfo; + desc.clazz = clazz; + desc.lookup = lookup; + desc.ctor = getCtor(clazz); + desc.setters = getSetters(lookup, classInfo, includingPrivate); + desc.getters = new ArrayList(); + desc.fields = getFields(lookup, classInfo, includingPrivate); + desc.bindingTypeWrappers = new ArrayList(); + desc.keyValueTypeWrappers = new ArrayList(); + desc.unwrappers = new ArrayList(); + for (Extension extension : JsoniterSpi.getExtensions()) { + extension.updateClassDescriptor(desc); + } + for (Binding field : desc.fields) { + if (field.valueType instanceof Class) { + Class valueClazz = (Class) field.valueType; + if (valueClazz.isArray()) { + field.valueCanReuse = false; + continue; + } + } + field.valueCanReuse = field.valueTypeLiteral.nativeType == null; + } + decodingDeduplicate(desc); + if (includingPrivate) { + if (desc.ctor.ctor != null) { + desc.ctor.ctor.setAccessible(true); + } + if (desc.ctor.staticFactory != null) { + desc.ctor.staticFactory.setAccessible(true); + } + for (WrapperDescriptor setter : desc.bindingTypeWrappers) { + setter.method.setAccessible(true); + } + } + for (Binding binding : desc.allDecoderBindings()) { + if (binding.fromNames == null) { + binding.fromNames = new String[]{binding.name}; + } + if (binding.field != null && includingPrivate) { + binding.field.setAccessible(true); + } + if (binding.method != null && includingPrivate) { + binding.method.setAccessible(true); + } + if (binding.decoder != null) { + JsoniterSpi.addNewDecoder(binding.decoderCacheKey(), binding.decoder); + } + } + return desc; + } + + public static ClassDescriptor getEncodingClassDescriptor(ClassInfo classInfo, boolean includingPrivate) { + Class clazz = classInfo.clazz; + Map lookup = collectTypeVariableLookup(classInfo.type); + ClassDescriptor desc = new ClassDescriptor(); + desc.classInfo = classInfo; + desc.clazz = clazz; + desc.lookup = lookup; + desc.fields = getFields(lookup, classInfo, includingPrivate); + desc.getters = getGetters(lookup, classInfo, includingPrivate); + desc.bindingTypeWrappers = new ArrayList(); + desc.keyValueTypeWrappers = new ArrayList(); + desc.unwrappers = new ArrayList(); + for (Extension extension : JsoniterSpi.getExtensions()) { + extension.updateClassDescriptor(desc); + } + encodingDeduplicate(desc); + for (Binding binding : desc.allEncoderBindings()) { + if (binding.toNames == null) { + binding.toNames = new String[]{binding.name}; + } + if (binding.field != null && includingPrivate) { + binding.field.setAccessible(true); + } + if (binding.method != null && includingPrivate) { + binding.method.setAccessible(true); + } + if (binding.encoder != null) { + JsoniterSpi.addNewEncoder(binding.encoderCacheKey(), binding.encoder); + } + } + return desc; + } + + private static void decodingDeduplicate(ClassDescriptor desc) { + HashMap byFromName = new HashMap(); + HashMap byFieldName = new HashMap(); + for (Binding field : desc.fields) { + for (String fromName : field.fromNames) { + if (byFromName.containsKey(fromName)) { + throw new JsonException("field decode from same name: " + fromName); + } + byFromName.put(fromName, field); + } + byFieldName.put(field.name, field); + } + ArrayList iteratingSetters = new ArrayList(desc.setters); + Collections.reverse(iteratingSetters); + for (Binding setter : iteratingSetters) { + if (setter.fromNames.length == 0) { + continue; + } + Binding existing = byFieldName.get(setter.name); + if (existing != null) { + existing.fromNames = new String[0]; + } + deduplicateByFromName(byFromName, setter); + } + for (WrapperDescriptor wrapper : desc.bindingTypeWrappers) { + for (Binding param : wrapper.parameters) { + deduplicateByFromName(byFromName, param); + } + } + for (Binding param : desc.ctor.parameters) { + deduplicateByFromName(byFromName, param); + } + } + + private static void deduplicateByFromName(Map byFromName, Binding setter) { + for (String fromName : setter.fromNames) { + Binding existing = byFromName.get(fromName); + if (existing == null) { + byFromName.put(fromName, setter); + continue; + } + existing.fromNames = new String[0]; + } + } + + private static void encodingDeduplicate(ClassDescriptor desc) { + HashMap byToName = new HashMap(); + HashMap byFieldName = new HashMap(); + for (Binding field : desc.fields) { + for (String toName : field.toNames) { + if (byToName.containsKey(toName)) { + throw new JsonException("field encode to same name: " + toName); + } + byToName.put(toName, field); + } + byFieldName.put(field.name, field); + } + for (Binding getter : new ArrayList(desc.getters)) { + if (getter.toNames.length == 0) { + continue; + } + Binding existing = byFieldName.get(getter.name); + if (existing != null) { + existing.toNames = new String[0]; + } + for (String toName : getter.toNames) { + existing = byToName.get(toName); + if (existing == null) { + byToName.put(toName, getter); + continue; + } + existing.toNames = new String[0]; + } + } + } + + private static ConstructorDescriptor getCtor(Class clazz) { + ConstructorDescriptor cctor = new ConstructorDescriptor(); + if (JsoniterSpi.canCreate(clazz)) { + cctor.objectFactory = JsoniterSpi.getObjectFactory(clazz); + return cctor; + } + try { + cctor.ctor = clazz.getDeclaredConstructor(); + } catch (Exception e) { + cctor.ctor = null; + } + return cctor; + } + + private static List getFields(Map lookup, ClassInfo classInfo, boolean includingPrivate) { + ArrayList bindings = new ArrayList(); + for (Field field : getAllFields(classInfo.clazz)) { + if (Modifier.isStatic(field.getModifiers())) { + continue; + } + if (includingPrivate) { + field.setAccessible(true); + } + if (isTransient(field.getModifiers())) { + continue; + } + Binding binding = createBindingFromField(lookup, classInfo, field); + if (!includingPrivate && !Modifier.isPublic(field.getModifiers())) { + binding.toNames = new String[0]; + binding.fromNames = new String[0]; + } + if (!includingPrivate && !Modifier.isPublic(field.getType().getModifiers())) { + binding.toNames = new String[0]; + binding.fromNames = new String[0]; + } + bindings.add(binding); + } + return bindings; + } + + private static Binding createBindingFromField(Map lookup, ClassInfo classInfo, Field field) { + try { + Binding binding = new Binding(classInfo, lookup, field.getGenericType()); + binding.fromNames = new String[]{field.getName()}; + binding.toNames = new String[]{field.getName()}; + binding.name = field.getName(); + binding.annotations = field.getAnnotations(); + binding.field = field; + return binding; + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new JsonException("failed to create binding for field: " + field, e); + } + } + + private static List getAllFields(Class clazz) { + ArrayList allFields = new ArrayList(); + Class current = clazz; + while (current != null) { + allFields.addAll(Arrays.asList(current.getDeclaredFields())); + current = current.getSuperclass(); + } + return allFields; + } + + private static List getSetters(Map lookup, ClassInfo classInfo, boolean includingPrivate) { + ArrayList setters = new ArrayList(); + for (Method method : getAllMethods(classInfo.clazz, includingPrivate)) { + if (Modifier.isStatic(method.getModifiers())) { + continue; + } + String methodName = method.getName(); + if (methodName.length() < 4) { + continue; + } + if (!methodName.startsWith("set")) { + continue; + } + Type[] paramTypes = method.getGenericParameterTypes(); + if (paramTypes.length != 1) { + continue; + } + if (!includingPrivate && !Modifier.isPublic(method.getParameterTypes()[0].getModifiers())) { + continue; + } + if (includingPrivate) { + method.setAccessible(true); + } + try { + String fromName = translateSetterName(methodName); + Field field = null; + try { + field = method.getDeclaringClass().getDeclaredField(fromName); + } catch (NoSuchFieldException e) { + // ignore + } + Binding setter = new Binding(classInfo, lookup, paramTypes[0]); + if (field != null && isTransient(field.getModifiers())) { + setter.fromNames = new String[0]; + } else { + setter.fromNames = new String[]{fromName}; + } + setter.name = fromName; + setter.method = method; + setter.annotations = method.getAnnotations(); + setters.add(setter); + } catch (JsonException e) { + throw e; + } catch (Exception e) { + throw new JsonException("failed to create binding from setter: " + method, e); + } + } + return setters; + } + + private static List getAllMethods(Class clazz, boolean includingPrivate) { + List allMethods = Arrays.asList(clazz.getMethods()); + if (includingPrivate) { + allMethods = new ArrayList(); + Class current = clazz; + while (current != null) { + allMethods.addAll(Arrays.asList(current.getDeclaredMethods())); + current = current.getSuperclass(); + } + } + return allMethods; + } + + private static String translateSetterName(String methodName) { + if (!methodName.startsWith("set")) { + return null; + } + String fromName = methodName.substring("set".length()); + char[] fromNameChars = fromName.toCharArray(); + fromNameChars[0] = Character.toLowerCase(fromNameChars[0]); + fromName = new String(fromNameChars); + return fromName; + } + + private static List getGetters(Map lookup, ClassInfo classInfo, boolean includingPrivate) { + ArrayList getters = new ArrayList(); + for (Method method : getAllMethods(classInfo.clazz, includingPrivate)) { + if (Modifier.isStatic(method.getModifiers())) { + continue; + } + String methodName = method.getName(); + if ("getClass".equals(methodName)) { + continue; + } + if (methodName.length() < 4) { + continue; + } + if (!methodName.startsWith("get")) { + continue; + } + if (method.getGenericParameterTypes().length != 0) { + continue; + } + String toName = methodName.substring("get".length()); + char[] toNameChars = toName.toCharArray(); + toNameChars[0] = Character.toLowerCase(toNameChars[0]); + toName = new String(toNameChars); + Binding getter = new Binding(classInfo, lookup, method.getGenericReturnType()); + Field field = null; + try { + field = method.getDeclaringClass().getDeclaredField(toName); + } catch (NoSuchFieldException e) { + // ignore + } + if (field != null && isTransient(field.getModifiers())) { + getter.toNames = new String[0]; + } else { + getter.toNames = new String[]{toName}; + } + getter.name = toName; + getter.method = method; + getter.annotations = method.getAnnotations(); + getters.add(getter); + } + return getters; + } + + private static Map collectTypeVariableLookup(Type type) { + HashMap vars = new HashMap(); + if (null == type) { + return vars; + } + if (type instanceof ParameterizedType) { + ParameterizedType pType = (ParameterizedType) type; + Type[] actualTypeArguments = pType.getActualTypeArguments(); + Class clazz = (Class) pType.getRawType(); + for (int i = 0; i < clazz.getTypeParameters().length; i++) { + TypeVariable variable = clazz.getTypeParameters()[i]; + vars.put(variable.getName() + "@" + clazz.getCanonicalName(), actualTypeArguments[i]); + } + vars.putAll(collectTypeVariableLookup(clazz.getGenericSuperclass())); + return vars; + } + if (type instanceof Class) { + Class clazz = (Class) type; + vars.putAll(collectTypeVariableLookup(clazz.getGenericSuperclass())); + return vars; + } + if (type instanceof WildcardType) { + return vars; + } + throw new JsonException("unexpected type: " + type); + } + public List allBindings() { ArrayList bindings = new ArrayList(8); bindings.addAll(fields); @@ -32,8 +411,8 @@ public List allBindings() { if (ctor != null) { bindings.addAll(ctor.parameters); } - if (wrappers != null) { - for (WrapperDescriptor setter : wrappers) { + if (bindingTypeWrappers != null) { + for (WrapperDescriptor setter : bindingTypeWrappers) { bindings.addAll(setter.parameters); } } @@ -47,16 +426,46 @@ public List allDecoderBindings() { if (ctor != null) { bindings.addAll(ctor.parameters); } - for (WrapperDescriptor setter : wrappers) { + for (WrapperDescriptor setter : bindingTypeWrappers) { bindings.addAll(setter.parameters); } return bindings; } + public List allEncoderBindings() { ArrayList bindings = new ArrayList(8); bindings.addAll(fields); bindings.addAll(getters); return bindings; } + + public List encodeTos() { + HashMap previousAppearance = new HashMap(); + ArrayList encodeTos = new ArrayList(8); + collectEncodeTo(encodeTos, fields, previousAppearance); + collectEncodeTo(encodeTos, getters, previousAppearance); + ArrayList removedNulls = new ArrayList(encodeTos.size()); + for (EncodeTo encodeTo : encodeTos) { + if (encodeTo != null) { + removedNulls.add(encodeTo); + } + } + return removedNulls; + } + + private void collectEncodeTo(ArrayList encodeTos, List fields, HashMap previousAppearance) { + for (Binding field : fields) { + for (String toName : field.toNames) { + if (previousAppearance.containsKey(toName)) { + encodeTos.set(previousAppearance.get(toName), null); + } + previousAppearance.put(toName, encodeTos.size()); + EncodeTo encodeTo = new EncodeTo(); + encodeTo.binding = field; + encodeTo.toName = toName; + encodeTos.add(encodeTo); + } + } + } } diff --git a/src/main/java/com/jsoniter/spi/ClassInfo.java b/src/main/java/com/jsoniter/spi/ClassInfo.java new file mode 100644 index 00000000..e85f7e38 --- /dev/null +++ b/src/main/java/com/jsoniter/spi/ClassInfo.java @@ -0,0 +1,27 @@ +package com.jsoniter.spi; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; + +public class ClassInfo { + + public final Type type; + public final Class clazz; + public final Type[] typeArgs; + + public ClassInfo(Type type) { + this.type = type; + if (type instanceof ParameterizedType) { + ParameterizedType pType = (ParameterizedType) type; + clazz = (Class) pType.getRawType(); + typeArgs = pType.getActualTypeArguments(); + } else if (type instanceof WildcardType) { + clazz = Object.class; + typeArgs = new Type[0]; + } else { + clazz = (Class) type; + typeArgs = new Type[0]; + } + } +} diff --git a/src/main/java/com/jsoniter/spi/Config.java b/src/main/java/com/jsoniter/spi/Config.java new file mode 100644 index 00000000..5b0d6c98 --- /dev/null +++ b/src/main/java/com/jsoniter/spi/Config.java @@ -0,0 +1,555 @@ +package com.jsoniter.spi; + +import com.jsoniter.annotation.*; +import com.jsoniter.output.EncodingMode; + +import java.lang.annotation.Annotation; +import java.lang.reflect.*; +import java.util.*; + +public class Config extends EmptyExtension { + + private final String configName; + private final Builder builder; + private static volatile Map configs = new HashMap(); + private volatile Map decoderCacheKeys = new HashMap(); + private volatile Map encoderCacheKeys = new HashMap(); + private final static Map primitiveOmitValues = new HashMap() {{ + put(boolean.class, new OmitValue.False()); + put(char.class, new OmitValue.ZeroChar()); + put(byte.class, new OmitValue.ZeroByte()); + put(short.class, new OmitValue.ZeroShort()); + put(int.class, new OmitValue.ZeroInt()); + put(long.class, new OmitValue.ZeroLong()); + put(float.class, new OmitValue.ZeroFloat()); + put(double.class, new OmitValue.ZeroDouble()); + }}; + + protected Config(String configName, Builder builder) { + this.configName = configName; + this.builder = builder; + } + + public String configName() { + return configName; + } + + public String getDecoderCacheKey(Type type) { + String cacheKey = decoderCacheKeys.get(type); + if (cacheKey != null) { + return cacheKey; + } + synchronized (this) { + cacheKey = decoderCacheKeys.get(type); + if (cacheKey != null) { + return cacheKey; + } + cacheKey = TypeLiteral.create(type).getDecoderCacheKey(configName); + HashMap newCache = new HashMap(decoderCacheKeys); + newCache.put(type, cacheKey); + decoderCacheKeys = newCache; + return cacheKey; + } + } + + public String getEncoderCacheKey(Type type) { + String cacheKey = encoderCacheKeys.get(type); + if (cacheKey != null) { + return cacheKey; + } + synchronized (this) { + cacheKey = encoderCacheKeys.get(type); + if (cacheKey != null) { + return cacheKey; + } + cacheKey = TypeLiteral.create(type).getEncoderCacheKey(configName); + HashMap newCache = new HashMap(encoderCacheKeys); + newCache.put(type, cacheKey); + encoderCacheKeys = newCache; + return cacheKey; + } + } + + public DecodingMode decodingMode() { + return builder.decodingMode; + } + + protected Builder builder() { + return builder; + } + + public Builder copyBuilder() { + return builder.copy(); + } + + public int indentionStep() { + return builder.indentionStep; + } + + public boolean omitDefaultValue() { + return builder.omitDefaultValue; + } + + public boolean escapeUnicode() { + return builder.escapeUnicode; + } + + public EncodingMode encodingMode() { + return builder.encodingMode; + } + + public static class Builder { + + private DecodingMode decodingMode; + private EncodingMode encodingMode; + private int indentionStep; + private boolean escapeUnicode = true; + private boolean omitDefaultValue = false; + + public Builder() { + String envMode = System.getenv("JSONITER_DECODING_MODE"); + if (envMode != null) { + decodingMode = DecodingMode.valueOf(envMode); + } else { + decodingMode = DecodingMode.REFLECTION_MODE; + } + envMode = System.getenv("JSONITER_ENCODING_MODE"); + if (envMode != null) { + encodingMode = EncodingMode.valueOf(envMode); + } else { + encodingMode = EncodingMode.REFLECTION_MODE; + } + } + + public Builder decodingMode(DecodingMode decodingMode) { + this.decodingMode = decodingMode; + return this; + } + + public Builder encodingMode(EncodingMode encodingMode) { + this.encodingMode = encodingMode; + return this; + } + + public Builder indentionStep(int indentionStep) { + this.indentionStep = indentionStep; + return this; + } + + public Builder omitDefaultValue(boolean omitDefaultValue) { + this.omitDefaultValue = omitDefaultValue; + return this; + } + + public Builder escapeUnicode(boolean escapeUnicode) { + this.escapeUnicode = escapeUnicode; + return this; + } + + public Config build() { + String configName = JsoniterSpi.assignConfigName(this); + Config config = configs.get(configName); + if (config != null) { + return config; + } + synchronized (Config.class) { + config = configs.get(configName); + if (config != null) { + return config; + } + config = doBuild(configName); + HashMap newCache = new HashMap(configs); + newCache.put(configName, config); + configs = newCache; + return config; + } + } + + protected Config doBuild(String configName) { + return new Config(configName, this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Builder builder = (Builder) o; + + if (indentionStep != builder.indentionStep) return false; + if (escapeUnicode != builder.escapeUnicode) return false; + if (decodingMode != builder.decodingMode) return false; + if (omitDefaultValue != builder.omitDefaultValue) return false; + return encodingMode == builder.encodingMode; + } + + @Override + public int hashCode() { + int result = decodingMode != null ? decodingMode.hashCode() : 0; + result = 31 * result + (encodingMode != null ? encodingMode.hashCode() : 0); + result = 31 * result + indentionStep; + result = 31 * result + (escapeUnicode ? 1 : 0); + result = 31 * result + (omitDefaultValue ? 1 : 0); + return result; + } + + public Builder copy() { + Builder builder = new Builder(); + builder.encodingMode = encodingMode; + builder.decodingMode = decodingMode; + builder.indentionStep = indentionStep; + builder.escapeUnicode = escapeUnicode; + builder.omitDefaultValue = omitDefaultValue; + return builder; + } + + @Override + public String toString() { + return "Config{" + + "decodingMode=" + decodingMode + + ", encodingMode=" + encodingMode + + ", indentionStep=" + indentionStep + + ", escapeUnicode=" + escapeUnicode + + ", omitDefaultValue=" + omitDefaultValue + + '}'; + } + } + + public static final Config INSTANCE = new Builder().build(); + + @Override + public void updateClassDescriptor(ClassDescriptor desc) { + JsonObject jsonObject = (JsonObject) desc.clazz.getAnnotation(JsonObject.class); + if (jsonObject != null) { + if (jsonObject.asExtraForUnknownProperties()) { + desc.asExtraForUnknownProperties = true; + } + for (String fieldName : jsonObject.unknownPropertiesWhitelist()) { + Binding binding = new Binding(desc.classInfo, desc.lookup, Object.class); + binding.name = fieldName; + binding.fromNames = new String[]{binding.name}; + binding.toNames = new String[0]; + binding.shouldSkip = true; + desc.fields.add(binding); + } + for (String fieldName : jsonObject.unknownPropertiesBlacklist()) { + Binding binding = new Binding(desc.classInfo, desc.lookup, Object.class); + binding.name = fieldName; + binding.fromNames = new String[]{binding.name}; + binding.toNames = new String[0]; + binding.asExtraWhenPresent = true; + desc.fields.add(binding); + } + } + List allMethods = new ArrayList(); + Class current = desc.clazz; + while (current != null) { + allMethods.addAll(Arrays.asList(current.getDeclaredMethods())); + current = current.getSuperclass(); + } + updateBindings(desc); + detectCtor(desc); + detectStaticFactory(desc, allMethods); + detectWrappers(desc, allMethods); + detectUnwrappers(desc, allMethods); + } + + private void detectUnwrappers(ClassDescriptor desc, List allMethods) { + for (Method method : allMethods) { + if (Modifier.isStatic(method.getModifiers())) { + continue; + } + if (getJsonUnwrapper(method.getAnnotations()) == null) { + continue; + } + desc.unwrappers.add(new UnwrapperDescriptor(method)); + } + } + + private void detectWrappers(ClassDescriptor desc, List allMethods) { + for (Method method : allMethods) { + if (Modifier.isStatic(method.getModifiers())) { + continue; + } + JsonWrapper jsonWrapper = getJsonWrapper(method.getAnnotations()); + if (jsonWrapper == null) { + continue; + } + Annotation[][] annotations = method.getParameterAnnotations(); + String[] paramNames = getParamNames(method, annotations.length); + Iterator iter = desc.setters.iterator(); + while(iter.hasNext()) { + if (method.equals(iter.next().method)) { + iter.remove(); + } + } + if (JsonWrapperType.BINDING.equals(jsonWrapper.value())) { + WrapperDescriptor wrapper = new WrapperDescriptor(); + wrapper.method = method; + for (int i = 0; i < annotations.length; i++) { + Annotation[] paramAnnotations = annotations[i]; + Binding binding = new Binding(desc.classInfo, desc.lookup, method.getGenericParameterTypes()[i]); + JsonProperty jsonProperty = getJsonProperty(paramAnnotations); + if (jsonProperty != null) { + updateBindingWithJsonProperty(binding, jsonProperty); + } + if (binding.name == null || binding.name.length() == 0) { + binding.name = paramNames[i]; + } + binding.fromNames = new String[]{binding.name}; + binding.toNames = new String[]{binding.name}; + binding.annotations = paramAnnotations; + wrapper.parameters.add(binding); + } + desc.bindingTypeWrappers.add(wrapper); + } else if (JsonWrapperType.KEY_VALUE.equals(jsonWrapper.value())) { + desc.keyValueTypeWrappers.add(method); + } else { + throw new JsonException("unknown json wrapper type: " + jsonWrapper.value()); + } + } + } + + private String[] getParamNames(Object obj, int paramCount) { + String[] paramNames = new String[paramCount]; + try { + Object params = reflectCall(obj, "getParameters"); + for (int i = 0; i < paramNames.length; i++) { + paramNames[i] = (String) reflectCall(Array.get(params, i), "getName"); + } + } catch (Exception e) { + } + return paramNames; + } + + private Object reflectCall(Object obj, String methodName, Object... args) throws Exception { + Method method = obj.getClass().getMethod(methodName); + return method.invoke(obj, args); + } + + private void detectStaticFactory(ClassDescriptor desc, List allMethods) { + for (Method method : allMethods) { + if (!Modifier.isStatic(method.getModifiers())) { + continue; + } + JsonCreator jsonCreator = getJsonCreator(method.getAnnotations()); + if (jsonCreator == null) { + continue; + } + desc.ctor.staticMethodName = method.getName(); + desc.ctor.staticFactory = method; + desc.ctor.ctor = null; + Annotation[][] annotations = method.getParameterAnnotations(); + String[] paramNames = getParamNames(method, annotations.length); + for (int i = 0; i < annotations.length; i++) { + Annotation[] paramAnnotations = annotations[i]; + JsonProperty jsonProperty = getJsonProperty(paramAnnotations); + Binding binding = new Binding(desc.classInfo, desc.lookup, method.getGenericParameterTypes()[i]); + if (jsonProperty != null) { + updateBindingWithJsonProperty(binding, jsonProperty); + } + if (binding.name == null || binding.name.length() == 0) { + binding.name = paramNames[i]; + } + binding.fromNames = new String[]{binding.name}; + binding.toNames = new String[]{binding.name}; + binding.annotations = paramAnnotations; + desc.ctor.parameters.add(binding); + } + } + } + + private void detectCtor(ClassDescriptor desc) { + if (desc.ctor == null) { + return; + } + for (Constructor ctor : desc.clazz.getDeclaredConstructors()) { + JsonCreator jsonCreator = getJsonCreator(ctor.getAnnotations()); + if (jsonCreator == null) { + continue; + } + desc.ctor.staticMethodName = null; + desc.ctor.ctor = ctor; + desc.ctor.staticFactory = null; + Annotation[][] annotations = ctor.getParameterAnnotations(); + String[] paramNames = getParamNames(ctor, annotations.length); + for (int i = 0; i < annotations.length; i++) { + Annotation[] paramAnnotations = annotations[i]; + JsonProperty jsonProperty = getJsonProperty(paramAnnotations); + Binding binding = new Binding(desc.classInfo, desc.lookup, ctor.getGenericParameterTypes()[i]); + if (jsonProperty != null) { + updateBindingWithJsonProperty(binding, jsonProperty); + } + if (binding.name == null || binding.name.length() == 0) { + binding.name = paramNames[i]; + } + binding.fromNames = new String[]{binding.name}; + binding.toNames = new String[]{binding.name}; + binding.annotations = paramAnnotations; + desc.ctor.parameters.add(binding); + } + } + } + + private void updateBindings(ClassDescriptor desc) { + boolean globalOmitDefault = JsoniterSpi.getCurrentConfig().omitDefaultValue(); + for (Binding binding : desc.allBindings()) { + boolean annotated = false; + JsonIgnore jsonIgnore = getJsonIgnore(binding.annotations); + if (jsonIgnore != null) { + annotated = true; + if (jsonIgnore.ignoreDecoding()) { + binding.fromNames = new String[0]; + } + if (jsonIgnore.ignoreEncoding()) { + binding.toNames = new String[0]; + } + } + // map JsonUnwrapper is not getter + JsonUnwrapper jsonUnwrapper = getJsonUnwrapper(binding.annotations); + if (jsonUnwrapper != null) { + annotated = true; + binding.fromNames = new String[0]; + binding.toNames = new String[0]; + } + if (globalOmitDefault) { + binding.defaultValueToOmit = createOmitValue(binding.valueType); + } + JsonProperty jsonProperty = getJsonProperty(binding.annotations); + if (jsonProperty != null) { + annotated = true; + updateBindingWithJsonProperty(binding, jsonProperty); + } + if (getAnnotation(binding.annotations, JsonMissingProperties.class) != null) { + annotated = true; + // this binding will not bind from json + // instead it will be set by jsoniter with missing property names + binding.fromNames = new String[0]; + desc.onMissingProperties = binding; + } + if (getAnnotation(binding.annotations, JsonExtraProperties.class) != null) { + annotated = true; + // this binding will not bind from json + // instead it will be set by jsoniter with extra properties + binding.fromNames = new String[0]; + desc.onExtraProperties = binding; + } + if (annotated && binding.field != null) { + if (desc.setters != null) { + for (Binding setter : desc.setters) { + if (binding.field.getName().equals(setter.name)) { + setter.fromNames = new String[0]; + setter.toNames = new String[0]; + } + } + } + if (desc.getters != null) { + for (Binding getter : desc.getters) { + if (binding.field.getName().equals(getter.name)) { + getter.fromNames = new String[0]; + getter.toNames = new String[0]; + } + } + } + } + } + } + + private void updateBindingWithJsonProperty(Binding binding, JsonProperty jsonProperty) { + binding.asMissingWhenNotPresent = jsonProperty.required(); + binding.isNullable = jsonProperty.nullable(); + binding.isCollectionValueNullable = jsonProperty.collectionValueNullable(); + String defaultValueToOmit = jsonProperty.defaultValueToOmit(); + if (!defaultValueToOmit.isEmpty()) { + binding.defaultValueToOmit = OmitValue.Parsed.parse(binding.valueType, defaultValueToOmit); + } + String altName = jsonProperty.value(); + if (!altName.isEmpty()) { + if (binding.name == null) { + binding.name = altName; + } + binding.fromNames = new String[]{altName}; + binding.toNames = new String[]{altName}; + } + if (jsonProperty.from().length > 0) { + binding.fromNames = jsonProperty.from(); + } + if (jsonProperty.to().length > 0) { + binding.toNames = jsonProperty.to(); + } + Class decoderClass = jsonProperty.decoder(); + if (decoderClass != Decoder.class) { + try { + try { + Constructor decoderCtor = decoderClass.getConstructor(Binding.class); + binding.decoder = (Decoder) decoderCtor.newInstance(binding); + } catch (NoSuchMethodException e) { + binding.decoder = (Decoder) decoderClass.newInstance(); + } + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new JsonException(e); + } + } + Class encoderClass = jsonProperty.encoder(); + if (encoderClass != Encoder.class) { + try { + try { + Constructor encoderCtor = encoderClass.getConstructor(Binding.class); + binding.encoder = (Encoder) encoderCtor.newInstance(binding); + } catch (NoSuchMethodException e) { + binding.encoder = (Encoder) encoderClass.newInstance(); + } + } catch (JsonException e) { + throw e; + } catch (Exception e) { + throw new JsonException(e); + } + } + if (jsonProperty.implementation() != Object.class) { + binding.valueType = GenericsHelper.useImpl(binding.valueType, jsonProperty.implementation()); + binding.valueTypeLiteral = TypeLiteral.create(binding.valueType); + } + } + + protected OmitValue createOmitValue(Type valueType) { + OmitValue omitValue = primitiveOmitValues.get(valueType); + if (omitValue != null) { + return omitValue; + } + return new OmitValue.Null(); + } + + protected JsonWrapper getJsonWrapper(Annotation[] annotations) { + return getAnnotation(annotations, JsonWrapper.class); + } + + protected JsonUnwrapper getJsonUnwrapper(Annotation[] annotations) { + return getAnnotation(annotations, JsonUnwrapper.class); + } + + protected JsonCreator getJsonCreator(Annotation[] annotations) { + return getAnnotation(annotations, JsonCreator.class); + } + + protected JsonProperty getJsonProperty(Annotation[] annotations) { + return getAnnotation(annotations, JsonProperty.class); + } + + protected JsonIgnore getJsonIgnore(Annotation[] annotations) { + return getAnnotation(annotations, JsonIgnore.class); + } + + protected static T getAnnotation(Annotation[] annotations, Class annotationClass) { + if (annotations == null) { + return null; + } + for (Annotation annotation : annotations) { + if (annotationClass.isAssignableFrom(annotation.getClass())) { + return (T) annotation; + } + } + return null; + } +} diff --git a/src/main/java/com/jsoniter/DecodingMode.java b/src/main/java/com/jsoniter/spi/DecodingMode.java similarity index 94% rename from src/main/java/com/jsoniter/DecodingMode.java rename to src/main/java/com/jsoniter/spi/DecodingMode.java index e8873f3a..8e42f774 100644 --- a/src/main/java/com/jsoniter/DecodingMode.java +++ b/src/main/java/com/jsoniter/spi/DecodingMode.java @@ -1,4 +1,4 @@ -package com.jsoniter; +package com.jsoniter.spi; public enum DecodingMode { /** diff --git a/src/main/java/com/jsoniter/spi/EmptyEncoder.java b/src/main/java/com/jsoniter/spi/EmptyEncoder.java deleted file mode 100644 index a4744b08..00000000 --- a/src/main/java/com/jsoniter/spi/EmptyEncoder.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.jsoniter.spi; - -import com.jsoniter.any.Any; -import com.jsoniter.output.JsonStream; - -import java.io.IOException; - -public class EmptyEncoder implements Encoder { - - @Override - public void encode(Object obj, JsonStream stream) throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public Any wrap(Object obj) { - throw new UnsupportedOperationException(); - } -} diff --git a/src/main/java/com/jsoniter/spi/EncodeTo.java b/src/main/java/com/jsoniter/spi/EncodeTo.java new file mode 100644 index 00000000..7c03a34e --- /dev/null +++ b/src/main/java/com/jsoniter/spi/EncodeTo.java @@ -0,0 +1,6 @@ +package com.jsoniter.spi; + +public class EncodeTo { + public Binding binding; + public String toName; +} diff --git a/src/main/java/com/jsoniter/spi/Encoder.java b/src/main/java/com/jsoniter/spi/Encoder.java index d9d249e4..f359061c 100644 --- a/src/main/java/com/jsoniter/spi/Encoder.java +++ b/src/main/java/com/jsoniter/spi/Encoder.java @@ -9,7 +9,11 @@ public interface Encoder { void encode(Object obj, JsonStream stream) throws IOException; - Any wrap(Object obj); + + interface ReflectionEncoder extends Encoder { + + Any wrap(Object obj); + } abstract class BooleanEncoder implements Encoder { @Override @@ -20,7 +24,7 @@ public void encode(Object obj, JsonStream stream) throws IOException { public abstract void encodeBoolean(boolean obj, JsonStream stream) throws IOException; } - abstract class ShortEncoder implements Encoder { + abstract class ShortEncoder implements ReflectionEncoder { @Override public void encode(Object obj, JsonStream stream) throws IOException { @@ -46,7 +50,7 @@ public void encodeShort(short obj, JsonStream stream) throws IOException { } } - abstract class IntEncoder implements Encoder { + abstract class IntEncoder implements ReflectionEncoder { @Override public void encode(Object obj, JsonStream stream) throws IOException { encodeInt((Integer) obj, stream); @@ -71,7 +75,7 @@ public void encodeInt(int obj, JsonStream stream) throws IOException { } } - abstract class LongEncoder implements Encoder { + abstract class LongEncoder implements ReflectionEncoder { @Override public void encode(Object obj, JsonStream stream) throws IOException { encodeLong((Long) obj, stream); @@ -96,7 +100,7 @@ public void encodeLong(long obj, JsonStream stream) throws IOException { } } - abstract class FloatEncoder implements Encoder { + abstract class FloatEncoder implements ReflectionEncoder { @Override public void encode(Object obj, JsonStream stream) throws IOException { encodeFloat((Float) obj, stream); @@ -121,7 +125,7 @@ public void encodeFloat(float obj, JsonStream stream) throws IOException { } } - abstract class DoubleEncoder implements Encoder { + abstract class DoubleEncoder implements ReflectionEncoder { @Override public void encode(Object obj, JsonStream stream) throws IOException { encodeDouble((Double) obj, stream); diff --git a/src/main/java/com/jsoniter/spi/GenericArrayTypeImpl.java b/src/main/java/com/jsoniter/spi/GenericArrayTypeImpl.java deleted file mode 100644 index 18a9e8fb..00000000 --- a/src/main/java/com/jsoniter/spi/GenericArrayTypeImpl.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.jsoniter.spi; - -import java.lang.reflect.GenericArrayType; -import java.lang.reflect.Type; - -public class GenericArrayTypeImpl implements GenericArrayType { - - private final Type componentType; - - GenericArrayTypeImpl(Type componentType) { - this.componentType = componentType; - } - - @Override - public Type getGenericComponentType() { - return componentType; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - GenericArrayTypeImpl that = (GenericArrayTypeImpl) o; - - return componentType != null ? componentType.equals(that.componentType) : that.componentType == null; - - } - - @Override - public int hashCode() { - return componentType != null ? componentType.hashCode() : 0; - } - - @Override - public String toString() { - return "GenericArrayTypeImpl{" + - "componentType=" + componentType + - '}'; - } -} diff --git a/src/main/java/com/jsoniter/spi/GenericsHelper.java b/src/main/java/com/jsoniter/spi/GenericsHelper.java new file mode 100644 index 00000000..bebd5b4c --- /dev/null +++ b/src/main/java/com/jsoniter/spi/GenericsHelper.java @@ -0,0 +1,136 @@ +package com.jsoniter.spi; + +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; + +public class GenericsHelper { + + public static GenericArrayType createGenericArrayType(Type componentType) { + return new GenericArrayTypeImpl(componentType); + } + + public static ParameterizedType createParameterizedType(Type[] actualTypeArguments, Type ownerType, Type rawType) { + return new ParameterizedTypeImpl(actualTypeArguments, ownerType, rawType); + } + + public static boolean isSameClass(Type type, Class clazz) { + if (type == clazz) { + return true; + } + if (type instanceof ParameterizedType) { + ParameterizedType pType = (ParameterizedType) type; + return pType.getRawType() == clazz; + } + return false; + } + + public static Type useImpl(Type type, Class clazz) { + if (type instanceof Class) { + return clazz; + } + if (type instanceof ParameterizedType) { + ParameterizedType pType = (ParameterizedType) type; + return createParameterizedType(pType.getActualTypeArguments(), pType.getOwnerType(), clazz); + } + throw new JsonException("can not change impl for: " + type); + } + + private static class GenericArrayTypeImpl implements GenericArrayType { + + private final Type componentType; + + GenericArrayTypeImpl(Type componentType) { + this.componentType = componentType; + } + + @Override + public Type getGenericComponentType() { + return componentType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + GenericArrayTypeImpl that = (GenericArrayTypeImpl) o; + + return componentType != null ? componentType.equals(that.componentType) : that.componentType == null; + + } + + @Override + public int hashCode() { + return componentType != null ? componentType.hashCode() : 0; + } + + @Override + public String toString() { + return "GenericArrayTypeImpl{" + + "componentType=" + componentType + + '}'; + } + } + + private static class ParameterizedTypeImpl implements ParameterizedType { + private final Type[] actualTypeArguments; + private final Type ownerType; + private final Type rawType; + + public ParameterizedTypeImpl(Type[] actualTypeArguments, Type ownerType, Type rawType){ + this.actualTypeArguments = actualTypeArguments; + this.ownerType = ownerType; + this.rawType = rawType; + } + + public Type[] getActualTypeArguments() { + return actualTypeArguments; + } + + public Type getOwnerType() { + return ownerType; + } + + public Type getRawType() { + return rawType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ParameterizedTypeImpl that = (ParameterizedTypeImpl) o; + + // Probably incorrect - comparing Object[] arrays with Arrays.equals + if (!Arrays.equals(actualTypeArguments, that.actualTypeArguments)) return false; + if (ownerType != null ? !ownerType.equals(that.ownerType) : that.ownerType != null) return false; + return rawType != null ? rawType.equals(that.rawType) : that.rawType == null; + + } + + @Override + public int hashCode() { + int result = Arrays.hashCode(actualTypeArguments); + result = 31 * result + (ownerType != null ? ownerType.hashCode() : 0); + result = 31 * result + (rawType != null ? rawType.hashCode() : 0); + return result; + } + + @Override + public String toString() { + String rawTypeName = rawType.toString(); + if (rawType instanceof Class) { + Class clazz = (Class) rawType; + rawTypeName = clazz.getName(); + } + return "ParameterizedTypeImpl{" + + "actualTypeArguments=" + Arrays.toString(actualTypeArguments) + + ", ownerType=" + ownerType + + ", rawType=" + rawTypeName + + '}'; + } + } +} diff --git a/src/main/java/com/jsoniter/spi/JsoniterSpi.java b/src/main/java/com/jsoniter/spi/JsoniterSpi.java index 1e519d55..4b40e77e 100644 --- a/src/main/java/com/jsoniter/spi/JsoniterSpi.java +++ b/src/main/java/com/jsoniter/spi/JsoniterSpi.java @@ -1,22 +1,112 @@ package com.jsoniter.spi; -import java.lang.reflect.*; -import java.util.*; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class JsoniterSpi { - static List extensions = new ArrayList(); - static Map typeImpls = new HashMap(); - static volatile Map encoders = new HashMap(); - static volatile Map decoders = new HashMap(); - static volatile Map objectFactories = new HashMap(); + // registered at startup, global state + private static Config defaultConfig; + private static List extensions = new ArrayList(); + private static Map typeImpls = new HashMap(); + private static Map globalMapKeyDecoders = new HashMap(); + private static Map globalMapKeyEncoders = new HashMap(); + private static Map globalTypeDecoders = new HashMap(); + private static Map globalTypeEncoders = new HashMap(); + private static Map globalPropertyDecoders = new HashMap(); + private static Map globalPropertyEncoders = new HashMap(); + + // current state + private static ThreadLocal currentConfig = new ThreadLocal() { + @Override + protected Config initialValue() { + return defaultConfig; + } + }; + private static volatile Map configNames = new HashMap(); + private static volatile Map mapKeyEncoders = new HashMap(); + private static volatile Map mapKeyDecoders = new HashMap(); + private static volatile Map encoders = new HashMap(); + private static volatile Map decoders = new HashMap(); + private static volatile Map objectFactories = new HashMap(); + + static { + defaultConfig = Config.INSTANCE; + } + + // === global === + + public static void setCurrentConfig(Config val) { + currentConfig.set(val); + } + + // TODO usage of this method leads to potentially unexpected side effects. All usage should be checked. + public static void clearCurrentConfig() { + currentConfig.set(defaultConfig); + } + + public static Config getCurrentConfig() { + return currentConfig.get(); + } + + public static void setDefaultConfig(Config val) { + defaultConfig = val; + } + + public static Config getDefaultConfig() { + return defaultConfig; + } + + public static String assignConfigName(Object obj) { + String configName = configNames.get(obj); + if (configName != null) { + return configName; + } + return assignNewConfigName(obj); + } + + private synchronized static String assignNewConfigName(Object obj) { + String configName = configNames.get(obj); + if (configName != null) { + return configName; + } + + long hash = obj.toString().hashCode(); + if (hash < 0) { + hash = Long.MAX_VALUE + hash; + } + configName = "jsoniter_codegen.cfg" + hash + "."; + copyGlobalSettings(configName); + HashMap newCache = new HashMap(configNames); + newCache.put(obj, configName); + configNames = newCache; + return configName; + } public static void registerExtension(Extension extension) { - extensions.add(extension); + if (!extensions.contains(extension)) { + extensions.add(extension); + } } + // TODO: use composite pattern public static List getExtensions() { - return Collections.unmodifiableList(extensions); + ArrayList combined = new ArrayList(extensions); + combined.add(currentConfig.get()); + return combined; + } + + public static void registerMapKeyDecoder(Type mapKeyType, Decoder mapKeyDecoder) { + globalMapKeyDecoders.put(mapKeyType, mapKeyDecoder); + copyGlobalMapKeyDecoder(getCurrentConfig().configName(), mapKeyType, mapKeyDecoder); + } + + public static void registerMapKeyEncoder(Type mapKeyType, Encoder mapKeyEncoder) { + globalMapKeyEncoders.put(mapKeyType, mapKeyEncoder); + copyGlobalMapKeyEncoder(getCurrentConfig().configName(), mapKeyType, mapKeyEncoder); } public static void registerTypeImplementation(Class superClazz, Class implClazz) { @@ -28,35 +118,123 @@ public static Class getTypeImplementation(Class superClazz) { } public static void registerTypeDecoder(Class clazz, Decoder decoder) { - addNewDecoder(TypeLiteral.create(clazz).getDecoderCacheKey(), decoder); + globalTypeDecoders.put(clazz, decoder); + copyGlobalTypeDecoder(getCurrentConfig().configName(), clazz, decoder); } public static void registerTypeDecoder(TypeLiteral typeLiteral, Decoder decoder) { - addNewDecoder(typeLiteral.getDecoderCacheKey(), decoder); + globalTypeDecoders.put(typeLiteral.getType(), decoder); + copyGlobalTypeDecoder(getCurrentConfig().configName(), typeLiteral.getType(), decoder); + } + + public static void registerTypeEncoder(Class clazz, Encoder encoder) { + globalTypeEncoders.put(clazz, encoder); + copyGlobalTypeEncoder(getCurrentConfig().configName(), clazz, encoder); } - public static void registerPropertyDecoder(Class clazz, String field, Decoder decoder) { - addNewDecoder(field + "@" + TypeLiteral.create(clazz).getDecoderCacheKey(), decoder); + public static void registerTypeEncoder(TypeLiteral typeLiteral, Encoder encoder) { + globalTypeEncoders.put(typeLiteral.getType(), encoder); + copyGlobalTypeEncoder(getCurrentConfig().configName(), typeLiteral.getType(), encoder); } - public static void registerPropertyDecoder(TypeLiteral typeLiteral, String field, Decoder decoder) { - addNewDecoder(field + "@" + typeLiteral.getDecoderCacheKey(), decoder); + public static void registerPropertyDecoder(Class clazz, String property, Decoder decoder) { + globalPropertyDecoders.put(new TypeProperty(clazz, property), decoder); + copyGlobalPropertyDecoder(getCurrentConfig().configName(), clazz, property, decoder); } - public static void registerTypeEncoder(Class clazz, Encoder encoder) { - addNewEncoder(TypeLiteral.create(clazz).getEncoderCacheKey(), encoder); + public static void registerPropertyDecoder(TypeLiteral typeLiteral, String property, Decoder decoder) { + globalPropertyDecoders.put(new TypeProperty(typeLiteral.getType(), property), decoder); + copyGlobalPropertyDecoder(getCurrentConfig().configName(), typeLiteral.getType(), property, decoder); } - public static void registerTypeEncoder(TypeLiteral typeLiteral, Encoder encoder) { - addNewEncoder(typeLiteral.getDecoderCacheKey(), encoder); + public static void registerPropertyEncoder(Class clazz, String property, Encoder encoder) { + globalPropertyEncoders.put(new TypeProperty(clazz, property), encoder); + copyGlobalPropertyEncoder(getCurrentConfig().configName(), clazz, property, encoder); + } + + public static void registerPropertyEncoder(TypeLiteral typeLiteral, String property, Encoder encoder) { + globalPropertyEncoders.put(new TypeProperty(typeLiteral.getType(), property), encoder); + copyGlobalPropertyEncoder(getCurrentConfig().configName(), typeLiteral.getType(), property, encoder); + } + + // === copy from global to current === + + private static void copyGlobalSettings(String configName) { + for (Map.Entry entry : globalMapKeyDecoders.entrySet()) { + copyGlobalMapKeyDecoder(configName, entry.getKey(), entry.getValue()); + } + for (Map.Entry entry : globalMapKeyEncoders.entrySet()) { + copyGlobalMapKeyEncoder(configName, entry.getKey(), entry.getValue()); + } + for (Map.Entry entry : globalTypeDecoders.entrySet()) { + copyGlobalTypeDecoder(configName, entry.getKey(), entry.getValue()); + } + for (Map.Entry entry : globalTypeEncoders.entrySet()) { + copyGlobalTypeEncoder(configName, entry.getKey(), entry.getValue()); + } + for (Map.Entry entry : globalPropertyDecoders.entrySet()) { + copyGlobalPropertyDecoder(configName, entry.getKey().type, entry.getKey().property, entry.getValue()); + } + for (Map.Entry entry : globalPropertyEncoders.entrySet()) { + copyGlobalPropertyEncoder(configName, entry.getKey().type, entry.getKey().property, entry.getValue()); + + } + } + + private static void copyGlobalPropertyEncoder(String configName, Type type, String property, Encoder propertyEncoder) { + addNewEncoder(property + "@" + TypeLiteral.create(type).getEncoderCacheKey(), propertyEncoder); } - public static void registerPropertyEncoder(Class clazz, String field, Encoder encoder) { - addNewEncoder(field + "@" + TypeLiteral.create(clazz).getEncoderCacheKey(), encoder); + private static void copyGlobalPropertyDecoder(String configName, Type type, String property, Decoder propertyDecoder) { + addNewDecoder(property + "@" + TypeLiteral.create(type).getDecoderCacheKey(), propertyDecoder); } - public static void registerPropertyEncoder(TypeLiteral typeLiteral, String field, Encoder encoder) { - addNewEncoder(field + "@" + typeLiteral.getDecoderCacheKey(), encoder); + private static void copyGlobalTypeEncoder(String configName, Type type, Encoder typeEncoder) { + addNewEncoder(TypeLiteral.create(type).getEncoderCacheKey(configName), typeEncoder); + } + + private static void copyGlobalTypeDecoder(String configName, Type type, Decoder typeDecoder) { + addNewDecoder(TypeLiteral.create(type).getDecoderCacheKey(configName), typeDecoder); + } + + private static void copyGlobalMapKeyDecoder(String configName, Type mapKeyType, Decoder mapKeyDecoder) { + addNewMapDecoder(TypeLiteral.create(mapKeyType).getDecoderCacheKey(configName), mapKeyDecoder); + } + + private static void copyGlobalMapKeyEncoder(String configName, Type mapKeyType, Encoder mapKeyEncoder) { + addNewMapEncoder(TypeLiteral.create(mapKeyType).getEncoderCacheKey(configName), mapKeyEncoder); + } + + public static String getMapKeyEncoderCacheKey(Type mapKeyType) { + TypeLiteral typeLiteral = TypeLiteral.create(mapKeyType); + return typeLiteral.getEncoderCacheKey(); + } + + public static String getMapKeyDecoderCacheKey(Type mapKeyType) { + TypeLiteral typeLiteral = TypeLiteral.create(mapKeyType); + return typeLiteral.getDecoderCacheKey(); + } + + // === current === + + public synchronized static void addNewMapDecoder(String cacheKey, Decoder mapKeyDecoder) { + HashMap newCache = new HashMap(mapKeyDecoders); + newCache.put(cacheKey, mapKeyDecoder); + mapKeyDecoders = newCache; + } + + public static Decoder getMapKeyDecoder(String cacheKey) { + return mapKeyDecoders.get(cacheKey); + } + + public synchronized static void addNewMapEncoder(String cacheKey, Encoder mapKeyEncoder) { + HashMap newCache = new HashMap(mapKeyEncoders); + newCache.put(cacheKey, mapKeyEncoder); + mapKeyEncoders = newCache; + } + + public static Encoder getMapKeyEncoder(String cacheKey) { + return mapKeyEncoders.get(cacheKey); } public static Decoder getDecoder(String cacheKey) { @@ -65,7 +243,11 @@ public static Decoder getDecoder(String cacheKey) { public synchronized static void addNewDecoder(String cacheKey, Decoder decoder) { HashMap newCache = new HashMap(decoders); - newCache.put(cacheKey, decoder); + if (decoder == null) { + newCache.remove(cacheKey); + } else { + newCache.put(cacheKey, decoder); + } decoders = newCache; } @@ -75,7 +257,11 @@ public static Encoder getEncoder(String cacheKey) { public synchronized static void addNewEncoder(String cacheKey, Encoder encoder) { HashMap newCache = new HashMap(encoders); - newCache.put(cacheKey, encoder); + if (encoder == null) { + newCache.remove(cacheKey); + } else { + newCache.put(cacheKey, encoder); + } encoders = newCache; } @@ -83,7 +269,7 @@ public static boolean canCreate(Class clazz) { if (objectFactories.containsKey(clazz)) { return true; } - for (Extension extension : extensions) { + for (Extension extension : getExtensions()) { if (extension.canCreate(clazz)) { addObjectFactory(clazz, extension); return true; @@ -93,7 +279,11 @@ public static boolean canCreate(Class clazz) { } public static Object create(Class clazz) { - return objectFactories.get(clazz).create(clazz); + return getObjectFactory(clazz).create(clazz); + } + + public static Extension getObjectFactory(Class clazz) { + return objectFactories.get(clazz); } private synchronized static void addObjectFactory(Class clazz, Extension extension) { @@ -102,346 +292,32 @@ private synchronized static void addObjectFactory(Class clazz, Extension extensi objectFactories = copy; } - public static ClassDescriptor getDecodingClassDescriptor(Class clazz, boolean includingPrivate) { - Map lookup = collectTypeVariableLookup(clazz); - ClassDescriptor desc = new ClassDescriptor(); - desc.clazz = clazz; - desc.lookup = lookup; - desc.ctor = getCtor(clazz); - desc.fields = getFields(lookup, clazz, includingPrivate); - desc.setters = getSetters(lookup, clazz, includingPrivate); - desc.wrappers = new ArrayList(); - desc.unWrappers = new ArrayList(); - for (Extension extension : extensions) { - extension.updateClassDescriptor(desc); - } - for (Binding field : desc.fields) { - if (field.valueType instanceof Class) { - Class valueClazz = (Class) field.valueType; - if (valueClazz.isArray()) { - field.valueCanReuse = false; - continue; - } - } - field.valueCanReuse = field.valueTypeLiteral.nativeType == null; - } - decodingDeduplicate(desc); - if (includingPrivate) { - if (desc.ctor.ctor != null) { - desc.ctor.ctor.setAccessible(true); - } - if (desc.ctor.staticFactory != null) { - desc.ctor.staticFactory.setAccessible(true); - } - for (WrapperDescriptor setter : desc.wrappers) { - setter.method.setAccessible(true); - } - } - for (Binding binding : desc.allDecoderBindings()) { - if (binding.fromNames == null) { - binding.fromNames = new String[]{binding.name}; - } - if (binding.field != null && includingPrivate) { - binding.field.setAccessible(true); - } - if (binding.method != null && includingPrivate) { - binding.method.setAccessible(true); - } - if (binding.decoder != null) { - JsoniterSpi.addNewDecoder(binding.decoderCacheKey(), binding.decoder); - } - } - return desc; - } - - public static ClassDescriptor getEncodingClassDescriptor(Class clazz, boolean includingPrivate) { - Map lookup = collectTypeVariableLookup(clazz); - ClassDescriptor desc = new ClassDescriptor(); - desc.clazz = clazz; - desc.lookup = lookup; - desc.fields = getFields(lookup, clazz, includingPrivate); - desc.getters = getGetters(lookup, clazz, includingPrivate); - desc.wrappers = new ArrayList(); - desc.unWrappers = new ArrayList(); - for (Extension extension : extensions) { - extension.updateClassDescriptor(desc); - } - encodingDeduplicate(desc); - for (Binding binding : desc.allEncoderBindings()) { - if (binding.toNames == null) { - binding.toNames = new String[]{binding.name}; - } - if (binding.field != null && includingPrivate) { - binding.field.setAccessible(true); - } - if (binding.method != null && includingPrivate) { - binding.method.setAccessible(true); - } - if (binding.encoder != null) { - JsoniterSpi.addNewEncoder(binding.encoderCacheKey(), binding.encoder); - } - } - return desc; - } - - private static void decodingDeduplicate(ClassDescriptor desc) { - HashMap byName = new HashMap(); - for (Binding field : desc.fields) { - if (byName.containsKey(field.name)) { - throw new JsonException("field name conflict: " + field.name); - } - byName.put(field.name, field); - } - for (Binding setter : desc.setters) { - Binding existing = byName.get(setter.name); - if (existing == null) { - byName.put(setter.name, setter); - continue; - } - if (desc.fields.remove(existing)) { - continue; - } - throw new JsonException("setter name conflict: " + setter.name); - } - for (WrapperDescriptor wrapper : desc.wrappers) { - for (Binding param : wrapper.parameters) { - Binding existing = byName.get(param.name); - if (existing == null) { - byName.put(param.name, param); - continue; - } - if (desc.fields.remove(existing)) { - continue; - } - if (desc.setters.remove(existing)) { - continue; - } - throw new JsonException("wrapper parameter name conflict: " + param.name); - } - } - for (Binding param : desc.ctor.parameters) { - Binding existing = byName.get(param.name); - if (existing == null) { - byName.put(param.name, param); - continue; - } - if (desc.fields.remove(existing)) { - continue; - } - if (desc.setters.remove(existing)) { - continue; - } - throw new JsonException("ctor parameter name conflict: " + param.name); - } - } - - private static void encodingDeduplicate(ClassDescriptor desc) { - HashMap byName = new HashMap(); - for (Binding field : desc.fields) { - if (byName.containsKey(field.name)) { - throw new JsonException("field name conflict: " + field.name); - } - byName.put(field.name, field); - } - for (Binding getter : desc.getters) { - Binding existing = byName.get(getter.name); - if (existing == null) { - byName.put(getter.name, getter); - continue; - } - if (desc.fields.remove(existing)) { - continue; - } - throw new JsonException("getter name conflict: " + getter.name); - } - } - - private static ConstructorDescriptor getCtor(Class clazz) { - ConstructorDescriptor cctor = new ConstructorDescriptor(); - if (canCreate(clazz)) { - cctor.objectFactory = objectFactories.get(clazz); - return cctor; - } - try { - cctor.ctor = clazz.getDeclaredConstructor(); - } catch (Exception e) { - cctor.ctor = null; - } - return cctor; - } - - private static List getFields(Map lookup, Class clazz, boolean includingPrivate) { - ArrayList bindings = new ArrayList(); - for (Field field : getAllFields(clazz, includingPrivate)) { - if (Modifier.isStatic(field.getModifiers())) { - continue; - } - if (Modifier.isTransient(field.getModifiers())) { - continue; - } - if (!includingPrivate && !Modifier.isPublic(field.getType().getModifiers())) { - continue; - } - if (includingPrivate) { - field.setAccessible(true); - } - Binding binding = createBindingFromField(lookup, clazz, field); - bindings.add(binding); - } - return bindings; - } - - private static Binding createBindingFromField(Map lookup, Class clazz, Field field) { - try { - Binding binding = new Binding(clazz, lookup, field.getGenericType()); - binding.fromNames = new String[]{field.getName()}; - binding.name = field.getName(); - binding.annotations = field.getAnnotations(); - binding.field = field; - return binding; - } catch (Exception e) { - throw new JsonException("failed to create binding for field: " + field, e); - } - } + private static class TypeProperty { - private static List getAllFields(Class clazz, boolean includingPrivate) { - List allFields = Arrays.asList(clazz.getFields()); - if (includingPrivate) { - allFields = new ArrayList(); - Class current = clazz; - while (current != null) { - allFields.addAll(Arrays.asList(current.getDeclaredFields())); - current = current.getSuperclass(); - } - } - return allFields; - } + public final Type type; + public final String property; - private static List getSetters(Map lookup, Class clazz, boolean includingPrivate) { - ArrayList setters = new ArrayList(); - for (Method method : getAllMethods(clazz, includingPrivate)) { - if (Modifier.isStatic(method.getModifiers())) { - continue; - } - String methodName = method.getName(); - if (methodName.length() < 4) { - continue; - } - if (!methodName.startsWith("set")) { - continue; - } - Type[] paramTypes = method.getGenericParameterTypes(); - if (paramTypes.length != 1) { - continue; - } - if (!includingPrivate && !Modifier.isPublic(method.getParameterTypes()[0].getModifiers())) { - continue; - } - if (includingPrivate) { - method.setAccessible(true); - } - try { - String fromName = translateSetterName(methodName); - Binding binding = new Binding(clazz, lookup, paramTypes[0]); - binding.fromNames = new String[]{fromName}; - binding.name = fromName; - binding.method = method; - binding.annotations = method.getAnnotations(); - setters.add(binding); - } catch (Exception e) { - throw new JsonException("failed to create binding from setter: " + method, e); - } + private TypeProperty(Type type, String property) { + this.type = type; + this.property = property; } - return setters; - } - private static List getAllMethods(Class clazz, boolean includingPrivate) { - List allMethods = Arrays.asList(clazz.getMethods()); - if (includingPrivate) { - allMethods = new ArrayList(); - Class current = clazz; - while (current != null) { - allMethods.addAll(Arrays.asList(current.getDeclaredMethods())); - current = current.getSuperclass(); - } - } - return allMethods; - } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; - private static String translateSetterName(String methodName) { - if (!methodName.startsWith("set")) { - return null; - } - String fromName = methodName.substring("set".length()); - char[] fromNameChars = fromName.toCharArray(); - fromNameChars[0] = Character.toLowerCase(fromNameChars[0]); - fromName = new String(fromNameChars); - return fromName; - } - - private static List getGetters(Map lookup, Class clazz, boolean includingPrivate) { - ArrayList getters = new ArrayList(); - for (Method method : getAllMethods(clazz, includingPrivate)) { - if (Modifier.isStatic(method.getModifiers())) { - continue; - } - String methodName = method.getName(); - if ("getClass".equals(methodName)) { - continue; - } - if (methodName.length() < 4) { - continue; - } - if (!methodName.startsWith("get")) { - continue; - } - if (method.getGenericParameterTypes().length != 0) { - continue; - } - String toName = methodName.substring("get".length()); - char[] fromNameChars = toName.toCharArray(); - fromNameChars[0] = Character.toLowerCase(fromNameChars[0]); - toName = new String(fromNameChars); - Binding getter = new Binding(clazz, lookup, method.getGenericReturnType()); - getter.toNames = new String[]{toName}; - getter.name = toName; - getter.method = method; - getter.annotations = method.getAnnotations(); - getters.add(getter); - } - return getters; - } + TypeProperty that = (TypeProperty) o; - public static void dump() { - for (String cacheKey : decoders.keySet()) { - System.err.println(cacheKey); - } - for (String cacheKey : encoders.keySet()) { - System.err.println(cacheKey); + if (type != null ? !type.equals(that.type) : that.type != null) return false; + return property != null ? property.equals(that.property) : that.property == null; } - } - private static Map collectTypeVariableLookup(Type type) { - HashMap vars = new HashMap(); - if (null == type) { - return vars; - } - if (type instanceof ParameterizedType) { - ParameterizedType pType = (ParameterizedType) type; - Type[] actualTypeArguments = pType.getActualTypeArguments(); - Class clazz = (Class) pType.getRawType(); - for (int i = 0; i < clazz.getTypeParameters().length; i++) { - TypeVariable variable = clazz.getTypeParameters()[i]; - vars.put(variable.getName() + "@" + clazz.getCanonicalName(), actualTypeArguments[i]); - } - vars.putAll(collectTypeVariableLookup(clazz.getGenericSuperclass())); - return vars; - } - if (type instanceof Class) { - Class clazz = (Class) type; - vars.putAll(collectTypeVariableLookup(clazz.getGenericSuperclass())); - return vars; + @Override + public int hashCode() { + int result = type != null ? type.hashCode() : 0; + result = 31 * result + (property != null ? property.hashCode() : 0); + return result; } - throw new JsonException("unexpected type: " + type); } } diff --git a/src/main/java/com/jsoniter/spi/OmitValue.java b/src/main/java/com/jsoniter/spi/OmitValue.java new file mode 100644 index 00000000..c56ae591 --- /dev/null +++ b/src/main/java/com/jsoniter/spi/OmitValue.java @@ -0,0 +1,206 @@ +package com.jsoniter.spi; + +import java.lang.reflect.Type; + +public interface OmitValue { + + boolean shouldOmit(Object val); + + String code(); + + class Null implements OmitValue { + + @Override + public boolean shouldOmit(Object val) { + return val == null; + } + + @Override + public String code() { + return "null == %s"; + } + } + + class ZeroByte implements OmitValue { + + @Override + public boolean shouldOmit(Object val) { + return (Byte) val == 0; + } + + @Override + public String code() { + return "0 == %s"; + } + } + + class ZeroShort implements OmitValue { + + @Override + public boolean shouldOmit(Object val) { + return (Short) val == 0; + } + + @Override + public String code() { + return "0 == %s"; + } + } + + class ZeroInt implements OmitValue { + + @Override + public boolean shouldOmit(Object val) { + return ((Integer) val) == 0; + } + + @Override + public String code() { + return "0 == %s"; + } + } + + class ZeroLong implements OmitValue { + + @Override + public boolean shouldOmit(Object val) { + return ((Long) val) == 0; + } + + @Override + public String code() { + return "0 == %s"; + } + } + + class ZeroFloat implements OmitValue { + + @Override + public boolean shouldOmit(Object val) { + return ((Float) val) == 0; + } + + @Override + public String code() { + return "0 == %s"; + } + } + + class ZeroDouble implements OmitValue { + + @Override + public boolean shouldOmit(Object val) { + return ((Double) val) == 0; + } + + @Override + public String code() { + return "0 == %s"; + } + } + + class ZeroChar implements OmitValue { + + @Override + public boolean shouldOmit(Object val) { + return (Character) val == 0; + } + + @Override + public String code() { + return "0 == %s"; + } + } + + class False implements OmitValue { + + @Override + public boolean shouldOmit(Object val) { + return !((Boolean) val); + } + + @Override + public String code() { + return "false == %s"; + } + } + + class Parsed implements OmitValue { + + private final Object defaultValue; + private final String code; + + public Parsed(Object defaultValue, String code) { + this.defaultValue = defaultValue; + this.code = code; + } + + public static OmitValue parse(Type valueType, String defaultValueToOmit) { + if ("void".equals(defaultValueToOmit)) { + return null; + } else if ("null".equals(defaultValueToOmit)) { + return new OmitValue.Null(); + } else if (boolean.class.equals(valueType)) { + Boolean defaultValue = Boolean.valueOf(defaultValueToOmit); + return new OmitValue.Parsed(defaultValue, defaultValueToOmit + " == %s"); + } else if (Boolean.class.equals(valueType)) { + Boolean defaultValue = Boolean.valueOf(defaultValueToOmit); + return new OmitValue.Parsed(defaultValue, defaultValueToOmit + " == %s.booleanValue()"); + } else if (int.class.equals(valueType)) { + Integer defaultValue = Integer.valueOf(defaultValueToOmit); + return new OmitValue.Parsed(defaultValue, defaultValueToOmit + " == %s"); + } else if (Integer.class.equals(valueType)) { + Integer defaultValue = Integer.valueOf(defaultValueToOmit); + return new OmitValue.Parsed(defaultValue, defaultValueToOmit + " == %s.intValue()"); + } else if (byte.class.equals(valueType)) { + Byte defaultValue = Byte.valueOf(defaultValueToOmit); + return new OmitValue.Parsed(defaultValue, defaultValueToOmit + " == %s"); + } else if (Byte.class.equals(valueType)) { + Byte defaultValue = Byte.valueOf(defaultValueToOmit); + return new OmitValue.Parsed(defaultValue, defaultValueToOmit + " == %s.byteValue()"); + } else if (short.class.equals(valueType)) { + Short defaultValue = Short.valueOf(defaultValueToOmit); + return new OmitValue.Parsed(defaultValue, defaultValueToOmit + " == %s"); + } else if (Short.class.equals(valueType)) { + Short defaultValue = Short.valueOf(defaultValueToOmit); + return new OmitValue.Parsed(defaultValue, defaultValueToOmit + " == %s.shortValue()"); + } else if (long.class.equals(valueType)) { + Long defaultValue = Long.valueOf(defaultValueToOmit); + return new OmitValue.Parsed(defaultValue, defaultValueToOmit + "L == %s"); + } else if (Long.class.equals(valueType)) { + Long defaultValue = Long.valueOf(defaultValueToOmit); + return new OmitValue.Parsed(defaultValue, defaultValueToOmit + "L == %s.longValue()"); + } else if (float.class.equals(valueType)) { + Float defaultValue = Float.valueOf(defaultValueToOmit); + return new OmitValue.Parsed(defaultValue, defaultValueToOmit + "F == %s"); + } else if (Float.class.equals(valueType)) { + Float defaultValue = Float.valueOf(defaultValueToOmit); + return new OmitValue.Parsed(defaultValue, defaultValueToOmit + "F == %s.floatValue()"); + } else if (double.class.equals(valueType)) { + Double defaultValue = Double.valueOf(defaultValueToOmit); + return new OmitValue.Parsed(defaultValue, defaultValueToOmit + "D == %s"); + } else if (Double.class.equals(valueType)) { + Double defaultValue = Double.valueOf(defaultValueToOmit); + return new OmitValue.Parsed(defaultValue, defaultValueToOmit + "D == %s.doubleValue()"); + } else if (char.class.equals(valueType) && defaultValueToOmit.length() == 1) { + Character defaultValue = defaultValueToOmit.charAt(0); + return new OmitValue.Parsed(defaultValue, "'" + defaultValueToOmit + "' == %s"); + } else if (Character.class.equals(valueType) && defaultValueToOmit.length() == 1) { + Character defaultValue = defaultValueToOmit.charAt(0); + return new OmitValue.Parsed(defaultValue, "'" + defaultValueToOmit + "' == %s.charValue()"); + } else { + throw new UnsupportedOperationException("failed to parse defaultValueToOmit: " + defaultValueToOmit); + } + } + + @Override + public boolean shouldOmit(Object val) { + return defaultValue.equals(val); + } + + @Override + public String code() { + return code; + } + } +} diff --git a/src/main/java/com/jsoniter/spi/ParameterizedTypeImpl.java b/src/main/java/com/jsoniter/spi/ParameterizedTypeImpl.java deleted file mode 100644 index 436b5231..00000000 --- a/src/main/java/com/jsoniter/spi/ParameterizedTypeImpl.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.jsoniter.spi; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Arrays; - -public class ParameterizedTypeImpl implements ParameterizedType { - private final Type[] actualTypeArguments; - private final Type ownerType; - private final Type rawType; - - public ParameterizedTypeImpl(Type[] actualTypeArguments, Type ownerType, Type rawType){ - this.actualTypeArguments = actualTypeArguments; - this.ownerType = ownerType; - this.rawType = rawType; - } - - public Type[] getActualTypeArguments() { - return actualTypeArguments; - } - - public Type getOwnerType() { - return ownerType; - } - - public Type getRawType() { - return rawType; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - ParameterizedTypeImpl that = (ParameterizedTypeImpl) o; - - // Probably incorrect - comparing Object[] arrays with Arrays.equals - if (!Arrays.equals(actualTypeArguments, that.actualTypeArguments)) return false; - if (ownerType != null ? !ownerType.equals(that.ownerType) : that.ownerType != null) return false; - return rawType != null ? rawType.equals(that.rawType) : that.rawType == null; - - } - - @Override - public int hashCode() { - int result = Arrays.hashCode(actualTypeArguments); - result = 31 * result + (ownerType != null ? ownerType.hashCode() : 0); - result = 31 * result + (rawType != null ? rawType.hashCode() : 0); - return result; - } - - @Override - public String toString() { - String rawTypeName = rawType.toString(); - if (rawType instanceof Class) { - Class clazz = (Class) rawType; - rawTypeName = clazz.getName(); - } - return "ParameterizedTypeImpl{" + - "actualTypeArguments=" + Arrays.toString(actualTypeArguments) + - ", ownerType=" + ownerType + - ", rawType=" + rawTypeName + - '}'; - } - - public static boolean isSameClass(Type type, Class clazz) { - if (type == clazz) { - return true; - } - if (type instanceof ParameterizedType) { - ParameterizedType pType = (ParameterizedType) type; - return pType.getRawType() == clazz; - } - return false; - } - - public static Type useImpl(Type type, Class clazz) { - if (type instanceof Class) { - return clazz; - } - if (type instanceof ParameterizedType) { - ParameterizedType pType = (ParameterizedType) type; - return new ParameterizedTypeImpl(pType.getActualTypeArguments(), pType.getOwnerType(), clazz); - } - throw new JsonException("can not change impl for: " + type); - } -} diff --git a/src/main/java/com/jsoniter/Slice.java b/src/main/java/com/jsoniter/spi/Slice.java similarity index 98% rename from src/main/java/com/jsoniter/Slice.java rename to src/main/java/com/jsoniter/spi/Slice.java index 8c801e13..0eacf3b0 100644 --- a/src/main/java/com/jsoniter/Slice.java +++ b/src/main/java/com/jsoniter/spi/Slice.java @@ -1,4 +1,4 @@ -package com.jsoniter; +package com.jsoniter.spi; public class Slice { diff --git a/src/main/java/com/jsoniter/spi/TypeLiteral.java b/src/main/java/com/jsoniter/spi/TypeLiteral.java index 51dc5c5b..b4461390 100644 --- a/src/main/java/com/jsoniter/spi/TypeLiteral.java +++ b/src/main/java/com/jsoniter/spi/TypeLiteral.java @@ -5,6 +5,7 @@ import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; import java.math.BigDecimal; import java.math.BigInteger; import java.util.HashMap; @@ -56,6 +57,7 @@ public enum NativeType { final Type type; final String decoderCacheKey; final String encoderCacheKey; + // TODO: remove native type final NativeType nativeType; /** @@ -109,6 +111,8 @@ private static String generateCacheKey(Type type, String prefix) { decoderClassName.append('_'); decoderClassName.append(typeName); } + } catch (JsonException e) { + throw e; } catch (Exception e) { throw new JsonException("failed to generate cache key for: " + type, e); } @@ -117,6 +121,8 @@ private static String generateCacheKey(Type type, String prefix) { Type compType = gaType.getGenericComponentType(); decoderClassName.append(formatTypeWithoutSpecialCharacter(compType)); decoderClassName.append("_array"); + } else if (type instanceof WildcardType) { + decoderClassName.append(Object.class.getName()); } else { throw new UnsupportedOperationException("do not know how to handle: " + type); } @@ -141,6 +147,9 @@ private static String formatTypeWithoutSpecialCharacter(Type type) { GenericArrayType gaType = (GenericArrayType) type; return formatTypeWithoutSpecialCharacter(gaType.getGenericComponentType()) + "_array"; } + if (type instanceof WildcardType) { + return Object.class.getCanonicalName(); + } throw new JsonException("unsupported type: " + type + ", of class " + type.getClass()); } @@ -180,11 +189,19 @@ public Type getType() { } public String getDecoderCacheKey() { - return decoderCacheKey; + return getDecoderCacheKey(JsoniterSpi.getCurrentConfig().configName()); + } + + public String getDecoderCacheKey(String configName) { + return configName + decoderCacheKey; } public String getEncoderCacheKey() { - return encoderCacheKey; + return getEncoderCacheKey(JsoniterSpi.getCurrentConfig().configName()); + } + + public String getEncoderCacheKey(String configName) { + return configName + encoderCacheKey; } public NativeType getNativeType() { diff --git a/src/main/java/com/jsoniter/spi/UnwrapperDescriptor.java b/src/main/java/com/jsoniter/spi/UnwrapperDescriptor.java new file mode 100644 index 00000000..eb758054 --- /dev/null +++ b/src/main/java/com/jsoniter/spi/UnwrapperDescriptor.java @@ -0,0 +1,55 @@ +package com.jsoniter.spi; + +import com.jsoniter.output.JsonStream; + +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Map; + +public class UnwrapperDescriptor { + + public Method method; + + public boolean isMap; + + public TypeLiteral mapValueTypeLiteral; + + public UnwrapperDescriptor(Method method) { + this.method = method; + if (isMapUnwrapper(method)) { + this.isMap = true; + Type mapType = method.getGenericReturnType(); + mapValueTypeLiteral = TypeLiteral.create(Object.class); + if (mapType instanceof ParameterizedType) { + ParameterizedType pType = (ParameterizedType) mapType; + Type[] typeArgs = pType.getActualTypeArguments(); + if (typeArgs.length == 2) { + mapValueTypeLiteral = TypeLiteral.create(typeArgs[1]); + } + } + } else if (isStreamUnwrapper(method)) { + this.isMap = false; + } else { + throw new JsonException("invalid unwrapper method signature: " + method); + } + } + + private boolean isMapUnwrapper(Method method) { + if (method.getParameterTypes().length != 0) { + return false; + } + return Map.class.isAssignableFrom(method.getReturnType()); + } + + private boolean isStreamUnwrapper(Method method) { + if (method.getReturnType() != void.class) { + return false; + } + Class[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length != 1) { + return false; + } + return parameterTypes[0] == JsonStream.class; + } +} diff --git a/src/main/java/com/jsoniter/static_codegen/StaticCodegen.java b/src/main/java/com/jsoniter/static_codegen/StaticCodegen.java new file mode 100644 index 00000000..89002585 --- /dev/null +++ b/src/main/java/com/jsoniter/static_codegen/StaticCodegen.java @@ -0,0 +1,41 @@ +package com.jsoniter.static_codegen; + +import com.jsoniter.CodegenAccess; +import com.jsoniter.spi.DecodingMode; +import com.jsoniter.JsonIterator; +import com.jsoniter.output.EncodingMode; +import com.jsoniter.output.JsonStream; +import com.jsoniter.spi.JsonException; + +import java.io.File; + +public class StaticCodegen { + public static void main(String[] args) throws Exception { + if (args.length == 0) { + System.out.println("StaticCodegen configClassName [outputDir]"); + System.out.println("configClassName: like a.b.Config, a class defining what to codegen"); + System.out.println("outputDir: if not specified, will write to source directory of configClass"); + return; + } + String configClassName = args[0]; + String configJavaFile = configClassName.replace('.', '/') + ".java"; + String outputDir; + if (args.length > 1) { + outputDir = args[1]; + } else { + if (!new File(configJavaFile).exists()) { + throw new JsonException("must execute static code generator in the java source code directory which contains: " + configJavaFile); + } + outputDir = new File(".").getAbsolutePath(); + } + Class clazz = Class.forName(configClassName); + StaticCodegenConfig config = (StaticCodegenConfig) clazz.newInstance(); + JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); + JsonStream.setMode(EncodingMode.DYNAMIC_MODE); + config.setup(); + CodegenAccess.staticGenDecoders( + config.whatToCodegen(), new CodegenAccess.StaticCodegenTarget(outputDir)); + com.jsoniter.output.CodegenAccess.staticGenEncoders( + config.whatToCodegen(), new com.jsoniter.output.CodegenAccess.StaticCodegenTarget(outputDir)); + } +} diff --git a/src/main/java/com/jsoniter/spi/CodegenConfig.java b/src/main/java/com/jsoniter/static_codegen/StaticCodegenConfig.java similarity index 69% rename from src/main/java/com/jsoniter/spi/CodegenConfig.java rename to src/main/java/com/jsoniter/static_codegen/StaticCodegenConfig.java index 002c2d0b..05690524 100644 --- a/src/main/java/com/jsoniter/spi/CodegenConfig.java +++ b/src/main/java/com/jsoniter/static_codegen/StaticCodegenConfig.java @@ -1,6 +1,8 @@ -package com.jsoniter.spi; +package com.jsoniter.static_codegen; -public interface CodegenConfig { +import com.jsoniter.spi.TypeLiteral; + +public interface StaticCodegenConfig { /** * register decoder/encoder before codegen * register extension before codegen diff --git a/src/test/java/com/jsoniter/BenchGson.java b/src/test/java/com/jsoniter/BenchGson.java new file mode 100644 index 00000000..67747b6c --- /dev/null +++ b/src/test/java/com/jsoniter/BenchGson.java @@ -0,0 +1,295 @@ +package com.jsoniter; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.type.TypeReference; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.annotations.SerializedName; +import com.jsoniter.extra.GsonCompatibilityMode; +import com.jsoniter.spi.DecodingMode; +import com.jsoniter.spi.JsoniterSpi; +import org.openjdk.jmh.Main; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.BenchmarkParams; +import org.openjdk.jmh.infra.Blackhole; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Date; +import java.util.List; + +@State(Scope.Thread) +public class BenchGson { + private GsonCompatibilityMode gsonCompatibilityMode; + private Gson gson; + + @Setup(Level.Trial) + public void benchSetup(BenchmarkParams params) { + gson = new GsonBuilder() + .setDateFormat("EEE MMM dd HH:mm:ss Z yyyy") + .create(); + gsonCompatibilityMode = new GsonCompatibilityMode.Builder().setDateFormat("EEE MMM dd HH:mm:ss Z yyyy").build(); + JsoniterSpi.setCurrentConfig(gsonCompatibilityMode); + JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); + if (params != null) { + if (params.getBenchmark().contains("jsoniterDynamicCodegenDecoder")) { + JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); + } + } + } + + @Benchmark + public void gsonDecoder(Blackhole bh) throws IOException { + FileInputStream stream = new FileInputStream("./src/test/tweets.json"); + InputStreamReader reader = new InputStreamReader(stream); + try { + bh.consume(gson.fromJson(reader, new TypeReference>() { + }.getType())); + } finally { + reader.close(); + stream.close(); + } + } + + @Benchmark + public void jsoniterReflectionDecoder(Blackhole bh) throws IOException { + FileInputStream stream = new FileInputStream("./src/test/tweets.json"); + JsonIterator iter = JsonIteratorPool.borrowJsonIterator(); + try { + iter.reset(stream); + bh.consume(iter.read(new TypeReference>() { + }.getType())); + } finally { + JsonIteratorPool.returnJsonIterator(iter); + stream.close(); + } + } + +// +// @Benchmark +// public void jsoniterDynamicCodegenDecoder(Blackhole bh) throws IOException { +// bh.consume(JsonIterator.deserialize(gsonCompatibilityMode, json, BagOfPrimitives.class)); +// } + + public static void main(String[] args) throws Exception { + Main.main(new String[]{ + "BenchGson", + "-i", "5", + "-wi", "5", + "-f", "1", + }); + } + + public static class Tweet { + @JsonProperty + String coordinates; + @JsonProperty + boolean favorited; + @JsonProperty + Date created_at; + @JsonProperty + boolean truncated; + @JsonProperty + Tweet retweeted_status; + @JsonProperty + String id_str; + @JsonProperty + String in_reply_to_id_str; + @JsonProperty + String contributors; + @JsonProperty + String text; + @JsonProperty + long id; + @JsonProperty + String retweet_count; + @JsonProperty + String in_reply_to_status_id_str; + @JsonProperty + Object geo; + @JsonProperty + boolean retweeted; + @JsonProperty + String in_reply_to_user_id; + @JsonProperty + String in_reply_to_screen_name; + @JsonProperty + Object place; + @JsonProperty + User user; + @JsonProperty + String source; + @JsonProperty + String in_reply_to_user_id_str; + } + + static class User { + @JsonProperty + String name; + @JsonProperty + String profile_sidebar_border_color; + @JsonProperty + boolean profile_background_tile; + @JsonProperty + String profile_sidebar_fill_color; + @JsonProperty + Date created_at; + @JsonProperty + String location; + @JsonProperty + String profile_image_url; + @JsonProperty + boolean follow_request_sent; + @JsonProperty + String profile_link_color; + @JsonProperty + boolean is_translator; + @JsonProperty + String id_str; + @JsonProperty + int favourites_count; + @JsonProperty + boolean contributors_enabled; + @JsonProperty + String url; + @JsonProperty + boolean default_profile; + @JsonProperty + long utc_offset; + @JsonProperty + long id; + @JsonProperty + boolean profile_use_background_image; + @JsonProperty + int listed_count; + @JsonProperty + String lang; + @JsonProperty("protected") + @SerializedName("protected") + boolean isProtected; + @JsonProperty + int followers_count; + @JsonProperty + String profile_text_color; + @JsonProperty + String profile_background_color; + @JsonProperty + String time_zone; + @JsonProperty + String description; + @JsonProperty + boolean notifications; + @JsonProperty + boolean geo_enabled; + @JsonProperty + boolean verified; + @JsonProperty + String profile_background_image_url; + @JsonProperty + boolean defalut_profile_image; + @JsonProperty + int friends_count; + @JsonProperty + int statuses_count; + @JsonProperty + String screen_name; + @JsonProperty + boolean following; + @JsonProperty + boolean show_all_inline_media; + } + + static class Feed { + @JsonProperty + String id; + @JsonProperty + String title; + @JsonProperty + String description; + @JsonProperty("alternate") + @SerializedName("alternate") + List alternates; + @JsonProperty + long updated; + @JsonProperty + List items; + + @Override + public String toString() { + StringBuilder result = new StringBuilder() + .append(id) + .append("\n").append(title) + .append("\n").append(description) + .append("\n").append(alternates) + .append("\n").append(updated); + int i = 1; + for (Item item : items) { + result.append(i++).append(": ").append(item).append("\n\n"); + } + return result.toString(); + } + } + + static class Link { + @JsonProperty + String href; + + @Override + public String toString() { + return href; + } + } + + static class Item { + @JsonProperty + List categories; + @JsonProperty + String title; + @JsonProperty + long published; + @JsonProperty + long updated; + @JsonProperty("alternate") + @SerializedName("alternate") + List alternates; + @JsonProperty + Content content; + @JsonProperty + String author; + @JsonProperty + List likingUsers; + + @Override + public String toString() { + return title + + "\nauthor: " + author + + "\npublished: " + published + + "\nupdated: " + updated + + "\n" + content + + "\nliking users: " + likingUsers + + "\nalternates: " + alternates + + "\ncategories: " + categories; + } + } + + static class Content { + @JsonProperty + String content; + + @Override + public String toString() { + return content; + } + } + + static class ReaderUser { + @JsonProperty + String userId; + + @Override + public String toString() { + return userId; + } + } +} diff --git a/src/test/java/com/jsoniter/IterImplForStreamingTest.java b/src/test/java/com/jsoniter/IterImplForStreamingTest.java new file mode 100644 index 00000000..e0432d39 --- /dev/null +++ b/src/test/java/com/jsoniter/IterImplForStreamingTest.java @@ -0,0 +1,93 @@ +package com.jsoniter; + +import com.jsoniter.any.Any; +import com.jsoniter.spi.JsonException; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import junit.framework.TestCase; +import org.junit.experimental.categories.Category; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; + +public class IterImplForStreamingTest extends TestCase { + + public void testReadMaxDouble() throws Exception { + String maxDouble = "1.7976931348623157e+308"; + JsonIterator iter = JsonIterator.parse("1.7976931348623157e+308"); + IterImplForStreaming.numberChars numberChars = IterImplForStreaming.readNumber(iter); + String number = new String(numberChars.chars, 0, numberChars.charsLength); + assertEquals(maxDouble, number); + } + + @Category(StreamingCategory.class) + public void testLoadMore() throws IOException { + final String originalContent = "1234567890"; + final byte[] src = ("{\"a\":\"" + originalContent + "\"}").getBytes(); + + int initialBufferSize; + Any parsedString; + // Case #1: Data fits into initial buffer, autoresizing on + // Input must definitely fit into such large buffer + initialBufferSize = src.length * 2; + JsonIterator jsonIterator = JsonIterator.parse(getSluggishInputStream(src), initialBufferSize, 512); + jsonIterator.readObject(); + parsedString = jsonIterator.readAny(); + assertEquals(originalContent, parsedString.toString()); + // Check buffer was not expanded + assertEquals(initialBufferSize, jsonIterator.buf.length); + + // Case #2: Data does not fit into initial buffer, autoresizing off + initialBufferSize = originalContent.length() / 2; + jsonIterator = JsonIterator.parse(getSluggishInputStream(src), initialBufferSize, 0); + jsonIterator.readObject(); + try { + jsonIterator.readAny(); + fail("Expect to fail because buffer is too small."); + } catch (JsonException e) { + if (!e.getMessage().startsWith("loadMore")) { + throw e; + } + } + // Check buffer was not expanded + assertEquals(initialBufferSize, jsonIterator.buf.length); + + // Case #3: Data does fit into initial buffer, autoresizing on + initialBufferSize = originalContent.length() / 2; + int autoExpandBufferStep = initialBufferSize * 3; + jsonIterator = JsonIterator.parse(getSluggishInputStream(src), initialBufferSize, autoExpandBufferStep); + jsonIterator.readObject(); + parsedString = jsonIterator.readAny(); + assertEquals(originalContent, parsedString.toString()); + // Check buffer was expanded exactly once + assertEquals(initialBufferSize + autoExpandBufferStep, jsonIterator.buf.length); + + // Case #4: Data does not fit (but largest string does) into initial buffer, autoresizing on + initialBufferSize = originalContent.length() + 2; + jsonIterator = JsonIterator.parse(new ByteArrayInputStream(src), initialBufferSize, 0); + jsonIterator.readObject(); + parsedString = jsonIterator.readAny(); + assertEquals(originalContent, parsedString.toString()); + // Check buffer was expanded exactly once + assertEquals(initialBufferSize, jsonIterator.buf.length); + } + + private static InputStream getSluggishInputStream(final byte[] src) { + return new InputStream() { + int position = 0; + + @Override + public int read() throws IOException { + throw new NotImplementedException(); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (position < src.length) { + b[off] = src[position++]; + return 1; + } + return -1; + } + }; + } +} diff --git a/src/test/java/com/jsoniter/TestAnnotation.java b/src/test/java/com/jsoniter/TestAnnotation.java index 0da1317e..3992e3f7 100644 --- a/src/test/java/com/jsoniter/TestAnnotation.java +++ b/src/test/java/com/jsoniter/TestAnnotation.java @@ -1,76 +1,19 @@ package com.jsoniter; -import com.jsoniter.annotation.*; -import com.jsoniter.any.Any; -import com.jsoniter.fuzzy.StringIntDecoder; +import com.jsoniter.annotation.JsonCreator; +import com.jsoniter.annotation.JsonProperty; import com.jsoniter.spi.JsonException; -import com.jsoniter.spi.JsoniterSpi; import junit.framework.TestCase; import java.io.IOException; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; public class TestAnnotation extends TestCase { static { - JsoniterAnnotationSupport.enable(); -// JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); +// JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_STRICTLY); // JsonIterator.setMode(DecodingMode.REFLECTION_MODE); } - public static class TestObject1 { - @JsonProperty(from = {"field-1"}) - public int field1; - - @JsonIgnore - public int field2; - } - - public void test_rename() throws IOException { - JsonIterator iter = JsonIterator.parse("{'field-1': 100}".replace('\'', '"')); - TestObject1 obj = iter.read(TestObject1.class); - assertEquals(100, obj.field1); - } - - public void test_ignore() throws IOException { - JsonIterator iter = JsonIterator.parse("{'field2': 100}".replace('\'', '"')); - TestObject1 obj = iter.read(TestObject1.class); - assertEquals(0, obj.field2); - } - - public static class TestObject2 { - private int field1; - - @JsonCreator - public TestObject2(@JsonProperty("field1") int field1) { - this.field1 = field1; - } - } - - public void test_ctor() throws IOException { - JsonIterator iter = JsonIterator.parse("{'field1': 100}".replace('\'', '"')); - TestObject2 obj = iter.read(TestObject2.class); - assertEquals(100, obj.field1); - } - - public static class TestObject3 { - public int field1; - - @JsonCreator - private TestObject3() { - } - } - - public void test_private_ctor() throws IOException { - JsoniterSpi.registerTypeDecoder(TestObject3.class, ReflectionDecoderFactory.create(TestObject3.class)); - JsonIterator iter = JsonIterator.parse("{'field1': 100}".replace('\'', '"')); - TestObject3 obj = iter.read(TestObject3.class); - assertEquals(100, obj.field1); - } - public static class TestObject4 { private int field1; @@ -106,36 +49,6 @@ public void test_single_param_setter() throws IOException { assertEquals(100, obj.field1); } - public static class TestObject6 { - - private int field1; - - @JsonWrapper - public void initialize(@JsonProperty("field1") int field1) { - this.field1 = field1; - } - } - - public void test_multi_param_setter() throws IOException { - JsonIterator iter = JsonIterator.parse("{'field1': 100}".replace('\'', '"')); - TestObject6 obj = iter.read(TestObject6.class); - assertEquals(100, obj.field1); - } - - public static class TestObject7 { - @JsonProperty(required = true) - public int field1; - - @JsonMissingProperties - public List missingProperties; - } - - public void test_required_properties() throws IOException { - JsonIterator iter = JsonIterator.parse("{}"); - TestObject7 obj = iter.read(TestObject7.class); - assertEquals(Arrays.asList("field1"), obj.missingProperties); - } - public static class TestObject8 { @JsonCreator public TestObject8(@JsonProperty(required = true) int param1) { @@ -143,7 +56,7 @@ public TestObject8(@JsonProperty(required = true) int param1) { } } - public void test_missing_ctor_arg() throws IOException { + public void skip_missing_ctor_arg() throws IOException { JsonIterator iter = JsonIterator.parse("{}"); try { iter.read(TestObject8.class); @@ -153,121 +66,42 @@ public void test_missing_ctor_arg() throws IOException { } } - @JsonObject(asExtraForUnknownProperties = true) - public static class TestObject9 { - @JsonExtraProperties - public Map extraProperties; - } - - public void test_extra_properties() throws IOException { - JsonIterator iter = JsonIterator.parse("{\"field1\": 100}"); - TestObject9 obj = iter.read(TestObject9.class); - assertEquals(100, obj.extraProperties.get("field1").toInt()); - } - - public static class TestObject10 { - @JsonProperty(decoder = StringIntDecoder.class) - public int field1; - } - - public void test_property_decoder() throws IOException { - JsonIterator iter = JsonIterator.parse("{\"field1\": \"100\"}"); - TestObject10 obj = iter.read(TestObject10.class); - assertEquals(100, obj.field1); - } - - public static class TestObject11 { - @JsonProperty(decoder = StringIntDecoder.class) - public Integer field1; - } - - public void test_integer_property_decoder() throws IOException { - JsonIterator iter = JsonIterator.parse("{\"field1\": \"100\"}"); - TestObject11 obj = iter.read(TestObject11.class); - assertEquals(Integer.valueOf(100), obj.field1); - } - - public static class TestObject12 { - @JsonProperty(from = {"field_1", "field-1"}) + public static class TestObject17 { public int field1; - } - public void test_bind_from_multiple_names() throws IOException { - JsonIterator iter = JsonIterator.parse("{\"field-1\": 100, \"field-1\": 101}"); - TestObject12 obj = iter.read(TestObject12.class); - assertEquals(101, obj.field1); - } - - @JsonObject(asExtraForUnknownProperties = true) - public static class TestObject13 { - } - - public void test_unknown_properties() throws IOException { - JsonIterator iter = JsonIterator.parse("{\"field-1\": 100, \"field-1\": 101}"); - try { - iter.read(TestObject13.class); - fail(); - } catch (JsonException e) { - System.out.println(e); + public void setField1(int field1) { + this.field1 = field1; } - } - - public static class TestObject14 { - @JsonProperty(required = true) - public int field1; - @JsonMissingProperties - public List missingProperties; - } - - public void test_required_properties_not_missing() throws IOException { - JsonIterator iter = JsonIterator.parse("{\"field1\": 100}"); - TestObject14 obj = iter.read(TestObject14.class); - assertNull(obj.missingProperties); - assertEquals(100, obj.field1); - } - - @JsonObject(unknownPropertiesBlacklist = {"field1"}) - public static class TestObject15 { - } + @JsonCreator + public void initialize(@JsonProperty("field1") int field1) { - public void test_unknown_properties_blacklist() throws IOException { - JsonIterator iter = JsonIterator.parse("{\"field1\": 100}"); - try { - iter.read(TestObject15.class); - fail(); - } catch (JsonException e) { - System.out.println(e); } } - public static class TestObject16 { - @JsonProperty(implementation = LinkedList.class) - public List values; + public void test_name_conflict() throws IOException { + JsonIterator iter = JsonIterator.parse("{}"); + assertNotNull(iter.read(TestObject17.class)); } - public void test_specify_property() throws IOException { - JsonIterator iter = JsonIterator.parse("{\"values\": [100]}"); - TestObject16 obj = iter.read(TestObject16.class); - assertEquals(Arrays.asList(100), obj.values); - assertEquals(LinkedList.class, obj.values.getClass()); + public interface TestObject18Interface { + void setHello(A val); } - public static class TestObject17 { - public int field1; + public static class TestObject18 implements TestObject18Interface { - public void setField1(int field1) { - this.field1 = field1; - } - - @JsonCreator - public void initialize(@JsonProperty("field1") int field1) { + public int _val; + @Override + public void setHello(Integer val) { + _val = val; } } - public void test_name_conflict() throws IOException { - JsonIterator iter = JsonIterator.parse("{}"); - assertNotNull(iter.read(TestObject17.class)); + public void test_inherited_setter_is_not_duplicate() throws IOException { + JsonIterator iter = JsonIterator.parse("{\"hello\":1}"); + TestObject18 obj = iter.read(TestObject18.class); + assertNotNull(obj); + assertEquals(1, obj._val); } } diff --git a/src/test/java/com/jsoniter/TestAnnotationJsonCreator.java b/src/test/java/com/jsoniter/TestAnnotationJsonCreator.java new file mode 100644 index 00000000..767b5b04 --- /dev/null +++ b/src/test/java/com/jsoniter/TestAnnotationJsonCreator.java @@ -0,0 +1,54 @@ +package com.jsoniter; + +import com.jsoniter.annotation.JsonCreator; +import com.jsoniter.annotation.JsonIgnore; +import com.jsoniter.annotation.JsonProperty; +import com.jsoniter.annotation.JsonWrapper; +import com.jsoniter.any.Any; +import junit.framework.TestCase; + +import java.io.IOException; +import java.util.Properties; + +public class TestAnnotationJsonCreator extends TestCase { + + + public static class TestObject2 { + private int field1; + + @JsonCreator + public TestObject2(@JsonProperty("field1") int field1) { + this.field1 = field1; + } + } + + public void test_ctor() throws IOException { + JsonIterator iter = JsonIterator.parse("{'field1': 100}".replace('\'', '"')); + TestObject2 obj = iter.read(TestObject2.class); + assertEquals(100, obj.field1); + } + + public static class TestObject { + + @JsonIgnore + private final String id; + @JsonIgnore + private final Properties properties; + + @JsonCreator + public TestObject(@JsonProperty("name") final String name) { + this.id = name; + properties = new Properties(); + } + + @JsonWrapper + public void setProperties(@JsonProperty("props") final Any props) { + // Set props + } + } + + public void test_ctor_and_setter_binding() throws IOException { + JsonIterator iter = JsonIterator.parse("{\"name\": \"test\", \"props\": {\"val\": \"42\"}}"); + iter.read(TestObject.class); + } +} diff --git a/src/test/java/com/jsoniter/TestAnnotationJsonIgnore.java b/src/test/java/com/jsoniter/TestAnnotationJsonIgnore.java new file mode 100644 index 00000000..5c48e1b8 --- /dev/null +++ b/src/test/java/com/jsoniter/TestAnnotationJsonIgnore.java @@ -0,0 +1,66 @@ +package com.jsoniter; + +import com.jsoniter.annotation.JsonCreator; +import com.jsoniter.annotation.JsonIgnore; +import com.jsoniter.annotation.JsonProperty; +import com.jsoniter.spi.DecodingMode; +import junit.framework.TestCase; +import org.junit.Test; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; +import java.io.Serializable; + +public class TestAnnotationJsonIgnore extends TestCase { + + public static class TestObject1 { + @JsonIgnore + public int field2; + } + + public void test_ignore() throws IOException { + JsonIterator iter = JsonIterator.parse("{'field2': 100}".replace('\'', '"')); + TestObject1 obj = iter.read(TestObject1.class); + assertEquals(0, obj.field2); + } + + public static class TestObject2 { + @JsonIgnore + public Serializable field2; + } + + public void test_ignore_no_constructor_field() throws IOException { + JsonIterator iter = JsonIterator.parse("{'field2': 100}".replace('\'', '"')); + TestObject2 obj = iter.read(TestObject2.class); + assertNull(obj.field2); + } + + public static class TestObject3 { + String field1; + @JsonIgnore + ActionListener fieldXXX; + + @JsonCreator + public TestObject3(@JsonProperty("field2") final String field) { + field1 = null; + fieldXXX = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + System.out.println("field2 is " + field); + } + }; + } + + @Override + public String toString() { + return "field1=" + field1 + ", field2=" + fieldXXX; + } + } + + public void test_json_ignore_with_creator() throws IOException { + JsonIterator iter = JsonIterator.parse("{\"field2\": \"test\"}"); + TestObject3 t = iter.read(TestObject3.class); + assertNotNull(t.fieldXXX); + } +} diff --git a/src/test/java/com/jsoniter/TestAnnotationJsonObject.java b/src/test/java/com/jsoniter/TestAnnotationJsonObject.java new file mode 100644 index 00000000..9e815048 --- /dev/null +++ b/src/test/java/com/jsoniter/TestAnnotationJsonObject.java @@ -0,0 +1,64 @@ +package com.jsoniter; + +import com.jsoniter.annotation.JsonExtraProperties; +import com.jsoniter.annotation.JsonObject; +import com.jsoniter.any.Any; +import com.jsoniter.spi.JsonException; +import junit.framework.TestCase; + +import java.io.IOException; +import java.util.Map; + +public class TestAnnotationJsonObject extends TestCase { + + @JsonObject(asExtraForUnknownProperties = true) + public static class TestObject9 { + @JsonExtraProperties + public Map extraProperties; + } + + public void test_extra_properties() throws IOException { + JsonIterator iter = JsonIterator.parse("{\"field1\": 100}"); + TestObject9 obj = iter.read(TestObject9.class); + assertEquals(100, obj.extraProperties.get("field1").toInt()); + } + + @JsonObject(asExtraForUnknownProperties = true) + public static class TestObject13 { + } + + public void test_unknown_properties() throws IOException { + JsonIterator iter = JsonIterator.parse("{\"field-1\": 100, \"field-1\": 101}"); + try { + iter.read(TestObject13.class); + fail(); + } catch (JsonException e) { + System.out.println(e); + } + } + + @JsonObject(unknownPropertiesBlacklist = {"field1"}) + public static class TestObject15 { + } + + public void test_unknown_properties_blacklist() throws IOException { + JsonIterator iter = JsonIterator.parse("{\"field1\": 100}"); + try { + iter.read(TestObject15.class); + fail(); + } catch (JsonException e) { + System.out.println(e); + } + } + + @JsonObject(asExtraForUnknownProperties = true) + public static class TestObject14 { + public int id; + } + + public void test_no_unknown_properties() throws IOException { + String json = "{ \"id\": 100 }"; + TestObject14 obj = JsonIterator.deserialize(json, TestObject14.class); + assertEquals(100, obj.id); + } +} diff --git a/src/test/java/com/jsoniter/TestAnnotationJsonProperty.java b/src/test/java/com/jsoniter/TestAnnotationJsonProperty.java new file mode 100644 index 00000000..6268c422 --- /dev/null +++ b/src/test/java/com/jsoniter/TestAnnotationJsonProperty.java @@ -0,0 +1,187 @@ +package com.jsoniter; + +import com.jsoniter.annotation.JsonCreator; +import com.jsoniter.annotation.JsonMissingProperties; +import com.jsoniter.annotation.JsonProperty; +import com.jsoniter.fuzzy.StringIntDecoder; +import com.jsoniter.output.JsonStream; +import com.jsoniter.spi.DecodingMode; +import com.jsoniter.spi.JsonException; +import junit.framework.TestCase; + +import java.io.IOException; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +public class TestAnnotationJsonProperty extends TestCase { + + public static class TestObject1 { + @JsonProperty(from = {"field-1"}) + public int field1; + } + + public void test_rename() throws IOException { + JsonIterator iter = JsonIterator.parse("{'field-1': 100}".replace('\'', '"')); + TestObject1 obj = iter.read(TestObject1.class); + assertEquals(100, obj.field1); + } + + public static class TestObject2 { + @JsonProperty(required = true) + public int field1; + + @JsonMissingProperties + public List missingProperties; + } + + public void test_required_properties() throws IOException { + JsonIterator iter = JsonIterator.parse("{}"); + TestObject2 obj = iter.read(TestObject2.class); + assertEquals(Arrays.asList("field1"), obj.missingProperties); + } + + public static class TestObject3 { + @JsonProperty(decoder = StringIntDecoder.class) + public int field1; + } + + public void test_property_decoder() throws IOException { + JsonIterator iter = JsonIterator.parse("{\"field1\": \"100\"}"); + TestObject3 obj = iter.read(TestObject3.class); + assertEquals(100, obj.field1); + } + + public static class TestObject4 { + @JsonProperty(decoder = StringIntDecoder.class) + public Integer field1; + } + + public void test_integer_property_decoder() throws IOException { + JsonIterator iter = JsonIterator.parse("{\"field1\": \"100\"}"); + TestObject4 obj = iter.read(TestObject4.class); + assertEquals(Integer.valueOf(100), obj.field1); + } + + public static class TestObject5 { + @JsonProperty(from = {"field_1", "field-1"}) + public int field1; + } + + public void test_bind_from_multiple_names() throws IOException { + JsonIterator iter = JsonIterator.parse("{\"field-1\": 100, \"field-1\": 101}"); + TestObject5 obj = iter.read(TestObject5.class); + assertEquals(101, obj.field1); + } + + public static class TestObject6 { + @JsonProperty(required = true) + public int field1; + + @JsonMissingProperties + public List missingProperties; + } + + public void test_required_properties_not_missing() throws IOException { + JsonIterator iter = JsonIterator.parse("{\"field1\": 100}"); + TestObject6 obj = iter.read(TestObject6.class); + assertNull(obj.missingProperties); + assertEquals(100, obj.field1); + } + + public static class TestObject7 { + @JsonProperty(implementation = LinkedList.class) + public List values; + } + + public void test_specify_property() throws IOException { + JsonIterator iter = JsonIterator.parse("{\"values\": [100]}"); + TestObject7 obj = iter.read(TestObject7.class); + assertEquals(Arrays.asList(100), obj.values); + assertEquals(LinkedList.class, obj.values.getClass()); + } + + public static class TestObject8 { + public String error; + @JsonProperty(value = "rs", required = true) + public boolean result; + @JsonProperty(value = "code",required = true) + public int code2; + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("code="); + builder.append(code2); + builder.append(" rs="); + builder.append(result); + return builder.toString(); + + } + } + + public void test_required() throws IOException { + String test ="{\"rs\":true,\"code\":200}"; + TestObject8 entity = JsonIterator.deserialize(test, TestObject8.class); + assertEquals(200, entity.code2); + } + + public static class TestObject9 { + private String field1 = "hello"; + + public String getField1() { + return field1; + } + + @JsonProperty("field-1") + public void setField1(String field1) { + this.field1 = field1; + } + } + + public void test_getter_and_setter() throws IOException { + String test ="{\"field-1\":\"hi\"}"; + TestObject9 entity = JsonIterator.deserialize(test, TestObject9.class); + assertEquals("hi", entity.getField1()); + } + + public static class TestObject10 { + private int field; + + @JsonCreator + public TestObject10(@JsonProperty("hello") int field) { + this.field = field; + } + + public int getField() { + return field; + } + } + + public void test_creator_with_json_property() { + String input = "{\"hello\":100}"; + TestObject10 obj = JsonIterator.deserialize(input, TestObject10.class); + assertEquals(100, obj.field); + assertEquals("{\"field\":100}", JsonStream.serialize(obj)); + } + + public static class TestObject11 { + @JsonProperty("hello") + public int field; + + public int getField() { + return field; + } + + public void setField(int field) { + this.field = field; + } + } + + public void test_field_and_getter_setter() { + String input = "{\"hello\":100}"; + TestObject11 obj = JsonIterator.deserialize(input, TestObject11.class); + assertEquals(100, obj.field); + } + +} diff --git a/src/test/java/com/jsoniter/TestAnnotationJsonWrapper.java b/src/test/java/com/jsoniter/TestAnnotationJsonWrapper.java new file mode 100644 index 00000000..eb9d2e07 --- /dev/null +++ b/src/test/java/com/jsoniter/TestAnnotationJsonWrapper.java @@ -0,0 +1,78 @@ +package com.jsoniter; + +import com.jsoniter.annotation.JsonIgnore; +import com.jsoniter.annotation.JsonProperty; +import com.jsoniter.annotation.JsonWrapper; +import com.jsoniter.annotation.JsonWrapperType; +import com.jsoniter.spi.DecodingMode; +import junit.framework.TestCase; + +import java.io.IOException; + +public class TestAnnotationJsonWrapper extends TestCase { + + static { +// JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); +// JsonIterator.setMode(DecodingMode.REFLECTION_MODE); + } + + public static class TestObject1 { + + private int _field1; + + @JsonWrapper + public void initialize(@JsonProperty("field1") int field1) { + this._field1 = field1; + } + } + + public void test_binding() throws IOException { + JsonIterator iter = JsonIterator.parse("{'field1': 100}".replace('\'', '"')); + TestObject1 obj = iter.read(TestObject1.class); + assertEquals(100, obj._field1); + } + + public static class TestObject2 { + + private int _field1; + + @JsonWrapper(JsonWrapperType.KEY_VALUE) + public void setProperties(String key, Object value) { + if (key.equals("field1")) { + _field1 = ((Long) value).intValue(); + } + } + } + + public void test_key_value() throws IOException { + JsonIterator iter = JsonIterator.parse("{'field1': 100}".replace('\'', '"')); + TestObject2 obj = iter.read(TestObject2.class); + assertEquals(100, obj._field1); + } + + public static class AAA { + @JsonProperty("name_1") + public String name; + + @JsonIgnore + public String partA; + @JsonIgnore + public String partB; + + @JsonWrapper + public void foreignFromJson(@JsonProperty(value = "parts", from ={"p2"}, required = false) String parts) { + if(parts == null){ + return; + } + String[] ps = parts.split(","); + partA = ps[0]; + partB = ps.length > 1 ? ps[1] : null; + } + } + + public void test_issue_104() { + String jsonStr = "{'name':'aaa', 'name_1':'bbb'}".replace('\'', '\"'); + AAA aaa = JsonIterator.deserialize(jsonStr, AAA.class); + assertEquals("bbb", aaa.name); + } +} diff --git a/src/test/java/com/jsoniter/TestArray.java b/src/test/java/com/jsoniter/TestArray.java index 45172275..92ea6cf4 100644 --- a/src/test/java/com/jsoniter/TestArray.java +++ b/src/test/java/com/jsoniter/TestArray.java @@ -15,7 +15,7 @@ public class TestArray extends TestCase { static { -// JsonIterator.setMode(DecodingMode.REFLECTION_MODE); +// JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); } public void test_empty_array() throws IOException { @@ -46,7 +46,7 @@ public void test_one_element() throws IOException { }); assertEquals(Arrays.asList(1), list); iter.reset(iter.buf); - assertArrayEquals(new Object[]{1.0}, iter.read(Object[].class)); + assertArrayEquals(new Object[]{1}, iter.read(Object[].class)); iter.reset(iter.buf); assertEquals(1, iter.read(Any[].class)[0].toInt()); iter.reset(iter.buf); @@ -78,13 +78,13 @@ public void test_two_elements() throws IOException { }); assertEquals(Arrays.asList(1, 2), list); iter.reset(iter.buf); - assertArrayEquals(new Object[]{1.0, 2.0}, iter.read(Object[].class)); + assertArrayEquals(new Object[]{1, 2}, iter.read(Object[].class)); iter.reset(iter.buf); assertEquals(1, iter.read(Any[].class)[0].toInt()); iter.reset(iter.buf); assertEquals(1, iter.readAny().toInt(0)); iter = JsonIterator.parse(" [ 1 , null, 2 ] "); - assertEquals(Arrays.asList(1.0D, null, 2.0D), iter.read()); + assertEquals(Arrays.asList(1, null, 2), iter.read()); } public void test_three_elements() throws IOException { @@ -104,7 +104,7 @@ public void test_three_elements() throws IOException { }); assertEquals(Arrays.asList(1, 2, 3), list); iter.reset(iter.buf); - assertArrayEquals(new Object[]{1.0, 2.0, 3.0}, iter.read(Object[].class)); + assertArrayEquals(new Object[]{1, 2, 3}, iter.read(Object[].class)); iter.reset(iter.buf); assertEquals(1, iter.read(Any[].class)[0].toInt()); iter.reset(iter.buf); @@ -130,7 +130,7 @@ public void test_four_elements() throws IOException { }); assertEquals(Arrays.asList(1, 2, 3, 4), list); iter.reset(iter.buf); - assertArrayEquals(new Object[]{1.0, 2.0, 3.0, 4.0}, iter.read(Object[].class)); + assertArrayEquals(new Object[]{1, 2, 3, 4}, iter.read(Object[].class)); iter.reset(iter.buf); assertEquals(1, iter.read(Any[].class)[0].toInt()); iter.reset(iter.buf); @@ -158,7 +158,7 @@ public void test_five_elements() throws IOException { }); assertEquals(Arrays.asList(1, 2, 3, 4, 5), list); iter.reset(iter.buf); - assertArrayEquals(new Object[]{1.0, 2.0, 3.0, 4.0, 5.0}, iter.read(Object[].class)); + assertArrayEquals(new Object[]{1, 2, 3, 4, 5}, iter.read(Object[].class)); iter.reset(iter.buf); assertEquals(1, iter.read(Any[].class)[0].toInt()); iter.reset(iter.buf); diff --git a/src/test/java/com/jsoniter/TestCustomizeType.java b/src/test/java/com/jsoniter/TestCustomizeType.java index 81f8496b..55d79696 100644 --- a/src/test/java/com/jsoniter/TestCustomizeType.java +++ b/src/test/java/com/jsoniter/TestCustomizeType.java @@ -11,45 +11,11 @@ public class TestCustomizeType extends TestCase { - public static class MyDate { - Date date; - } - static { - JsoniterSpi.registerTypeDecoder(MyDate.class, new Decoder() { - @Override - public Object decode(final JsonIterator iter) throws IOException { - return new MyDate() {{ - date = new Date(iter.readLong()); - }}; - } - }); // JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_STRICTLY); // JsonIterator.setMode(DecodingMode.REFLECTION_MODE); } - public void test_direct() throws IOException { - JsonIterator iter = JsonIterator.parse("1481365190000"); - MyDate date = iter.read(MyDate.class); - assertEquals(1481365190000L, date.date.getTime()); - } - - public static class FieldWithMyDate { - public MyDate field; - } - - public void test_as_field_type() throws IOException { - JsonIterator iter = JsonIterator.parse("{'field': 1481365190000}".replace('\'', '"')); - FieldWithMyDate obj = iter.read(FieldWithMyDate.class); - assertEquals(1481365190000L, obj.field.date.getTime()); - } - - public void test_as_array_element() throws IOException { - JsonIterator iter = JsonIterator.parse("[1481365190000]"); - MyDate[] dates = iter.read(MyDate[].class); - assertEquals(1481365190000L, dates[0].date.getTime()); - } - public static class MyDate2 { Date date; } diff --git a/src/test/java/com/jsoniter/TestDemo.java b/src/test/java/com/jsoniter/TestDemo.java index f22a1f65..cf42cb87 100644 --- a/src/test/java/com/jsoniter/TestDemo.java +++ b/src/test/java/com/jsoniter/TestDemo.java @@ -4,7 +4,6 @@ import com.jsoniter.any.Any; import com.jsoniter.fuzzy.MaybeEmptyArrayDecoder; import com.jsoniter.fuzzy.MaybeStringLongDecoder; -import com.jsoniter.output.EncodingMode; import com.jsoniter.output.JsonStream; import com.jsoniter.spi.Decoder; import com.jsoniter.spi.EmptyExtension; @@ -14,10 +13,8 @@ import java.io.IOException; import java.lang.reflect.Type; -import java.util.Date; import java.util.HashMap; import java.util.List; -import java.util.Map; public class TestDemo extends TestCase { public void test_bind_api() throws IOException { @@ -61,6 +58,10 @@ public void test_iterator_api_and_bind() throws IOException { System.out.println(user); } + public static class TestObject2 { + + } + public void test_empty_array_as_null() throws IOException { JsoniterSpi.registerExtension(new EmptyExtension() { @Override @@ -69,7 +70,7 @@ public Decoder createDecoder(final String cacheKey, final Type type) { // avoid infinite loop return null; } - if (type != Date.class) { + if (type != TestObject2.class) { return null; } return new Decoder() { @@ -93,7 +94,7 @@ public Object decode(JsonIterator iter1) throws IOException { } }); JsonIterator iter = JsonIterator.parse("[]"); - assertNull(iter.read(Date.class)); + assertNull(iter.read(TestObject2.class)); } public static class Order { @@ -164,4 +165,67 @@ public void test_utf8() { TestObject obj = JsonIterator.deserialize(input, TestObject.class); assertEquals(0, obj.commentCount); } + + public void test_deserialize() { + String str = "{\"port\":13110} "; + JsonIterator.deserialize(str.getBytes(), HashMap.class); + } + + public static class CollectionResponse { + public List results; + } + + public static class Feed { + public String id; + public String owner; + public String name; + } + + public void test_generics() { + CollectionResponse objs = JsonIterator.deserialize("{\n" + + "\"count\": 1,\n" + + "\"next\": null,\n" + + "\"previous\": null,\n" + + "\"results\": [\n" + + "{\n" + + "\"id\": \"f560fccb-4020-43c1-8a27-92507ef625bd\",\n" + + "\"search_terms\": [\n" + + "\"gigi hadid\"\n" + + "],\n" + + "\"owner\": \"...\",\n" + + "\"egress_nodes\": [\n" + + "\"DE\"\n" + + "],\n" + + "\"status\": \"ACTIVE\",\n" + + "\"expires_at\": null,\n" + + "\"available_sources\": [\n" + + "\"92c784ae-b7bf-4434-a6cc-740109d91cc8\"\n" + + "],\n" + + "\"available_egress_nodes\": [\n" + + "\"DE\"\n" + + "],\n" + + "\"created_at\": \"2017-07-27T13:29:20.935108Z\",\n" + + "\"name\": \"Test\",\n" + + "\"description\": \"\",\n" + + "\"start_date\": null,\n" + + "\"end_date\": null,\n" + + "\"match_all_include\": false,\n" + + "\"velocity\": 0.0666666666666667,\n" + + "\"storage_consumption\": 0.000011026778,\n" + + "\"consumption\": 0.000120833333333333,\n" + + "\"persistence_enabled\": true,\n" + + "\"sources\": [\n" + + "\"92c784ae-b7bf-4434-a6cc-740109d91cc8\"\n" + + "],\n" + + "\"permissions\": {\n" + + "\"has_read_access\": true,\n" + + "\"has_write_access\": true,\n" + + "\"has_share_access\": true,\n" + + "\"has_ownership\": true\n" + + "}\n" + + "}\n" + + "]\n" + + "}", new TypeLiteral>(){}); + assertEquals("f560fccb-4020-43c1-8a27-92507ef625bd", objs.results.get(0).id); + } } diff --git a/src/test/java/com/jsoniter/TestFloat.java b/src/test/java/com/jsoniter/TestFloat.java index 4c6ad145..5fc0f851 100644 --- a/src/test/java/com/jsoniter/TestFloat.java +++ b/src/test/java/com/jsoniter/TestFloat.java @@ -5,6 +5,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.math.BigDecimal; public class TestFloat extends TestCase { @@ -13,7 +14,9 @@ public class TestFloat extends TestCase { public void test_positive_negative() throws IOException { // positive assertEquals(12.3f, parseFloat("12.3,")); + assertEquals(729212.0233f, parseFloat("729212.0233,")); assertEquals(12.3d, parseDouble("12.3,")); + assertEquals(729212.0233d, parseDouble("729212.0233,")); // negative assertEquals(-12.3f, parseFloat("-12.3,")); assertEquals(-12.3d, parseDouble("-12.3,")); @@ -32,16 +35,26 @@ public void test_ieee_754() throws IOException { public void test_decimal_places() throws IOException { assertEquals(Long.MAX_VALUE, parseFloat("9223372036854775807,"), 0.01f); assertEquals(Long.MAX_VALUE, parseDouble("9223372036854775807,"), 0.01f); + assertEquals(Long.MIN_VALUE, parseDouble("-9223372036854775808,"), 0.01f); assertEquals(9923372036854775807f, parseFloat("9923372036854775807,"), 0.01f); + assertEquals(-9923372036854775808f, parseFloat("-9923372036854775808,"), 0.01f); assertEquals(9923372036854775807d, parseDouble("9923372036854775807,"), 0.01f); + assertEquals(-9923372036854775808d, parseDouble("-9923372036854775808,"), 0.01f); assertEquals(720368.54775807f, parseFloat("720368.54775807,"), 0.01f); + assertEquals(-720368.54775807f, parseFloat("-720368.54775807,"), 0.01f); assertEquals(720368.54775807d, parseDouble("720368.54775807,"), 0.01f); + assertEquals(-720368.54775807d, parseDouble("-720368.54775807,"), 0.01f); assertEquals(72036.854775807f, parseFloat("72036.854775807,"), 0.01f); assertEquals(72036.854775807d, parseDouble("72036.854775807,"), 0.01f); assertEquals(720368.54775807f, parseFloat("720368.547758075,"), 0.01f); assertEquals(720368.54775807d, parseDouble("720368.547758075,"), 0.01f); } + public void test_combination_of_dot_and_exponent() throws IOException { + double v = JsonIterator.parse("8.37377E9").readFloat(); + assertEquals(Double.valueOf("8.37377E9"), v, 1000d); + } + @Category(StreamingCategory.class) public void test_streaming() throws IOException { isStreaming = true; @@ -64,4 +77,24 @@ private double parseDouble(String input) throws IOException { return JsonIterator.parse(input).readDouble(); } } + + public void testBigDecimal() { + BigDecimal number = JsonIterator.deserialize("100.1", BigDecimal.class); + assertEquals(new BigDecimal("100.1"), number); + } + + public void testChooseDouble() { + Object number = JsonIterator.deserialize("1.1", Object.class); + assertEquals(1.1, number); + number = JsonIterator.deserialize("1.0", Object.class); + assertEquals(1.0, number); + } + + public void testInfinity() { + assertTrue(JsonIterator.deserialize("\"-infinity\"", Double.class) == Double.NEGATIVE_INFINITY); + assertTrue(JsonIterator.deserialize("\"-infinity\"", Float.class) == Float.NEGATIVE_INFINITY); + assertTrue(JsonIterator.deserialize("\"infinity\"", Double.class) == Double.POSITIVE_INFINITY); + assertTrue(JsonIterator.deserialize("\"infinity\"", Float.class) == Float.POSITIVE_INFINITY); + } + } diff --git a/src/test/java/com/jsoniter/TestGenerics.java b/src/test/java/com/jsoniter/TestGenerics.java index 91e07398..446dcc7f 100644 --- a/src/test/java/com/jsoniter/TestGenerics.java +++ b/src/test/java/com/jsoniter/TestGenerics.java @@ -1,9 +1,6 @@ package com.jsoniter; -import com.jsoniter.spi.Binding; -import com.jsoniter.spi.ClassDescriptor; -import com.jsoniter.spi.JsoniterSpi; -import com.jsoniter.spi.TypeLiteral; +import com.jsoniter.spi.*; import junit.framework.TestCase; import java.io.IOException; @@ -14,7 +11,7 @@ public class TestGenerics extends TestCase { static { -// JsonIterator.setMode(DecodingMode.REFLECTION_MODE); +// JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_STRICTLY); } public void test_int_list() throws IOException { @@ -78,12 +75,15 @@ public static class Class1 { public B[] field2; public List[] field3; public List field4; + public List>> getField6() { return null; } + public T getField7() { return null; } + public void setField8(List a) { } } @@ -96,23 +96,48 @@ public static class Class3 extends Class2 { } public void test_generic_super_class() throws IOException { - ClassDescriptor desc = JsoniterSpi.getDecodingClassDescriptor(Class3.class, true); + ClassDescriptor desc = ClassDescriptor.getDecodingClassDescriptor(new ClassInfo(Class3.class), true); Map fieldDecoderCacheKeys = new HashMap(); for (Binding field : desc.allDecoderBindings()) { fieldDecoderCacheKeys.put(field.name, field.valueTypeLiteral.getDecoderCacheKey()); } - for (Binding field : JsoniterSpi.getEncodingClassDescriptor(Class3.class, true).getters) { + for (Binding field : ClassDescriptor.getEncodingClassDescriptor(new ClassInfo(Class3.class), true).getters) { fieldDecoderCacheKeys.put(field.name, field.valueTypeLiteral.getDecoderCacheKey()); } - assertEquals(new HashMap() {{ - put("field1", "decoder.java.util.List_java.lang.String"); - put("field2", "decoder.java.lang.Integer_array"); - put("field3", "decoder.java.util.List_java.lang.Integer_array"); - put("field4", "decoder.java.util.List_java.lang.String_array"); - put("field5", "decoder.java.lang.Float"); - put("field6", "decoder.java.util.List_java.util.Map_java.lang.String_java.util.List_java.lang.Integer"); - put("field7", "decoder.java.lang.Object"); - put("field8", "decoder.java.util.List_java.lang.String"); - }}, fieldDecoderCacheKeys); + assertTrue(fieldDecoderCacheKeys.get("field1").endsWith("decoder.java.util.List_java.lang.String")); + assertTrue(fieldDecoderCacheKeys.get("field2").endsWith("decoder.java.lang.Integer_array")); + assertTrue(fieldDecoderCacheKeys.get("field3").endsWith("decoder.java.util.List_java.lang.Integer_array")); + assertTrue(fieldDecoderCacheKeys.get("field4").endsWith("decoder.java.util.List_java.lang.String_array")); + assertTrue(fieldDecoderCacheKeys.get("field5").endsWith("decoder.java.lang.Float")); + assertTrue(fieldDecoderCacheKeys.get("field6").endsWith("decoder.java.util.List_java.util.Map_java.lang.String_java.util.List_java.lang.Integer")); + assertTrue(fieldDecoderCacheKeys.get("field7").endsWith("decoder.java.lang.Object")); + assertTrue(fieldDecoderCacheKeys.get("field8").endsWith("decoder.java.util.List_java.lang.String")); + } + + public static class NetRes { + public int code; + public String desc; + public T results; + } + + public static class User { + public String name; + public int age; + } + + public void test_issue_103() { + String json = "{'code':1, 'desc':'OK', 'results':{'name':'aaa', 'age':18}}".replace('\'', '\"'); + NetRes res = JsonIterator.deserialize(json, new TypeLiteral>() { + }); + assertEquals(User.class, res.results.getClass()); + } + + public static class TestObject7 { + public List field; + } + + public void test_wildcard() throws IOException { + TestObject7 obj = JsonIterator.deserialize("{\"field\":[1]}", TestObject7.class); + assertEquals(1, obj.field.get(0)); } } diff --git a/src/test/java/com/jsoniter/TestGson.java b/src/test/java/com/jsoniter/TestGson.java new file mode 100644 index 00000000..bc7598da --- /dev/null +++ b/src/test/java/com/jsoniter/TestGson.java @@ -0,0 +1,268 @@ +package com.jsoniter; + +import com.google.gson.*; +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import com.google.gson.annotations.Since; +import com.google.gson.annotations.Until; +import com.jsoniter.extra.GsonCompatibilityMode; +import junit.framework.TestCase; + +import java.lang.reflect.Field; +import java.util.Date; +import java.util.TimeZone; + +public class TestGson extends TestCase { + + public static class TestObject1 { + @SerializedName("field-1") + public String field1; + } + + public void test_SerializedName() { + Gson gson = new Gson(); + TestObject1 obj = gson.fromJson("{\"field-1\":\"hello\"}", TestObject1.class); + assertEquals("hello", obj.field1); + obj = JsonIterator.deserialize(new GsonCompatibilityMode.Builder().build(), + "{\"field-1\":\"hello\"}", TestObject1.class); + assertEquals("hello", obj.field1); + } + + public static class TestObject2 { + @Expose(deserialize = false) + public String field1; + } + + public void test_Expose() { + // test if the iterator reuse will keep right config cache + JsonIterator.deserialize(new GsonCompatibilityMode.Builder().build(), + "{\"field-1\":\"hello\"}", TestObject2.class); + Gson gson = new GsonBuilder() + .excludeFieldsWithoutExposeAnnotation() + .create(); + TestObject2 obj = gson.fromJson("{\"field1\":\"hello\"}", TestObject2.class); + assertNull(obj.field1); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .excludeFieldsWithoutExposeAnnotation() + .build(); + obj = JsonIterator.deserialize(config, + "{\"field1\":\"hello\"}", TestObject2.class); + assertNull(obj.field1); + } + +// public void test_setDateFormat_no_op() { +// TimeZone orig = TimeZone.getDefault(); +// try { +// TimeZone.setDefault(TimeZone.getTimeZone("UTC")); +// Gson gson = new GsonBuilder().create(); +// Date obj = gson.fromJson("\"Jan 1, 1970 12:00:00 AM\"", Date.class); +// assertEquals(0, obj.getTime()); +// GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() +// .build(); +// obj = JsonIterator.deserialize(config, "\"Jan 1, 1970 12:00:00 AM\"", Date.class); +// assertEquals(0, obj.getTime()); +// } finally { +// TimeZone.setDefault(orig); +// } +// } + + public void test_setDateFormat_format() { + TimeZone orig = TimeZone.getDefault(); + try { + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + Gson gson = new GsonBuilder().setDateFormat("EEE, MMM d, yyyy hh:mm:ss a z").create(); + Date obj = gson.fromJson("\"Thu, Jan 1, 1970 12:00:00 AM UTC\"", Date.class); + assertEquals(0, obj.getTime()); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .setDateFormat("EEE, MMM d, yyyy hh:mm:ss a z") + .build(); + obj = JsonIterator.deserialize(config, "\"Thu, Jan 1, 1970 12:00:00 AM UTC\"", Date.class); + assertEquals(0, obj.getTime()); + } finally { + TimeZone.setDefault(orig); + } + } + + public static class TestObject3 { + public String field1; + } + + public void test_setFieldNamingStrategy() { + FieldNamingStrategy fieldNamingStrategy = new FieldNamingStrategy() { + @Override + public String translateName(Field f) { + return "_" + f.getName(); + } + }; + Gson gson = new GsonBuilder() + .setFieldNamingStrategy(fieldNamingStrategy) + .create(); + TestObject3 obj = gson.fromJson("{\"_field1\":\"hello\"}", TestObject3.class); + assertEquals("hello", obj.field1); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .setFieldNamingStrategy(fieldNamingStrategy) + .build(); + obj = JsonIterator.deserialize(config, "{\"_field1\":\"hello\"}", TestObject3.class); + assertEquals("hello", obj.field1); + } + + public void test_setFieldNamingPolicy() { + Gson gson = new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE) + .create(); + TestObject3 obj = gson.fromJson("{\"Field1\":\"hello\"}", TestObject3.class); + assertEquals("hello", obj.field1); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE) + .build(); + obj = JsonIterator.deserialize(config, "{\"Field1\":\"hello\"}", TestObject3.class); + assertEquals("hello", obj.field1); + } + + public static class TestObject5 { + @Since(3.0) + public String field1 = ""; + @Until(1.0) + public String field2 = ""; + @Since(2.0) + public String field3 = ""; + @Until(2.0) + public String field4 = ""; + } + + public void test_setVersion() { + Gson gson = new GsonBuilder() + .setVersion(2.0) + .create(); + TestObject5 obj = gson.fromJson("{\"field1\":\"field1\",\"field2\":\"field2\",\"field3\":\"field3\",\"field4\":\"field4\"}", + TestObject5.class); + assertEquals("", obj.field1); + assertEquals("", obj.field2); + assertEquals("field3", obj.field3); + assertEquals("", obj.field4); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .setVersion(2.0) + .build(); + obj = JsonIterator.deserialize(config, "{\"field1\":\"field1\",\"field2\":\"field2\",\"field3\":\"field3\",\"field4\":\"field4\"}", + TestObject5.class); + assertEquals("", obj.field1); + assertEquals("", obj.field2); + assertEquals("field3", obj.field3); + assertEquals("", obj.field4); + } + + public void test_addDeserializationExclusionStrategy() { + ExclusionStrategy exclusionStrategy = new ExclusionStrategy() { + @Override + public boolean shouldSkipField(FieldAttributes f) { + return !f.getName().equals("field3"); + } + + @Override + public boolean shouldSkipClass(Class clazz) { + return false; + } + }; + Gson gson = new GsonBuilder() + .addDeserializationExclusionStrategy(exclusionStrategy) + .create(); + TestObject5 obj = gson.fromJson("{\"field1\":\"field1\",\"field2\":\"field2\",\"field3\":\"field3\",\"field4\":\"field4\"}", + TestObject5.class); + assertEquals("", obj.field1); + assertEquals("", obj.field2); + assertEquals("field3", obj.field3); + assertEquals("", obj.field4); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .addDeserializationExclusionStrategy(exclusionStrategy) + .build(); + obj = JsonIterator.deserialize(config, "{\"field1\":\"field1\",\"field2\":\"field2\",\"field3\":\"field3\",\"field4\":\"field4\"}", + TestObject5.class); + assertEquals("", obj.field1); + assertEquals("", obj.field2); + assertEquals("field3", obj.field3); + assertEquals("", obj.field4); + } + + public void test_int_as_string() { + Gson gson = new Gson(); + String str = gson.fromJson("1.1", String.class); + assertEquals("1.1", str); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder().build(); + str = JsonIterator.deserialize(config, "1", String.class); + assertEquals("1", str); + } + + public void test_bool_as_string() { + Gson gson = new Gson(); + String str = gson.fromJson("true", String.class); + assertEquals("true", str); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder().build(); + str = JsonIterator.deserialize(config, "true", String.class); + assertEquals("true", str); + } + + public static class TestObject6 { + public boolean field; + } + + public void test_null_as_boolean() { + Gson gson = new Gson(); + TestObject6 obj = gson.fromJson("{\"field\":null}", TestObject6.class); + assertFalse(obj.field); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder().build(); + obj = JsonIterator.deserialize(config, "{\"field\":null}", TestObject6.class); + assertFalse(obj.field); + } + + public static class TestObject7 { + public long field; + } + + public void test_null_as_long() { + Gson gson = new Gson(); + TestObject7 obj = gson.fromJson("{\"field\":null}", TestObject7.class); + assertEquals(0, obj.field); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder().build(); + obj = JsonIterator.deserialize(config, "{\"field\":null}", TestObject7.class); + assertEquals(0, obj.field); + } + + public static class TestObject8 { + public int field; + } + + public void test_null_as_int() { + Gson gson = new Gson(); + TestObject8 obj = gson.fromJson("{\"field\":null}", TestObject8.class); + assertEquals(0, obj.field); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder().build(); + obj = JsonIterator.deserialize(config, "{\"field\":null}", TestObject8.class); + assertEquals(0, obj.field); + } + + public static class TestObject9 { + public float field; + } + + public void test_null_as_float() { + Gson gson = new Gson(); + TestObject9 obj = gson.fromJson("{\"field\":null}", TestObject9.class); + assertEquals(0.0f, obj.field); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder().build(); + obj = JsonIterator.deserialize(config, "{\"field\":null}", TestObject9.class); + assertEquals(0.0f, obj.field); + } + + public static class TestObject10 { + public double field; + } + + public void test_null_as_double() { + Gson gson = new Gson(); + TestObject10 obj = gson.fromJson("{\"field\":null}", TestObject10.class); + assertEquals(0.0d, obj.field); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder().build(); + obj = JsonIterator.deserialize(config, "{\"field\":null}", TestObject10.class); + assertEquals(0.0d, obj.field); + } +} diff --git a/src/test/java/com/jsoniter/TestInteger.java b/src/test/java/com/jsoniter/TestInteger.java index 71f21517..589dae02 100644 --- a/src/test/java/com/jsoniter/TestInteger.java +++ b/src/test/java/com/jsoniter/TestInteger.java @@ -6,29 +6,48 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +//import java.math.BigDecimal; +//import java.math.BigInteger; public class TestInteger extends TestCase { + static { +// JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_STRICTLY); + } + private boolean isStreaming; + public void test_char() throws IOException { + Character c = JsonIterator.deserialize("50", Character.class); + assertEquals(50, (int) c); + } + public void test_positive_negative_int() throws IOException { + assertEquals(0, parseInt("0")); assertEquals(4321, parseInt("4321")); assertEquals(54321, parseInt("54321")); assertEquals(654321, parseInt("654321")); assertEquals(7654321, parseInt("7654321")); assertEquals(87654321, parseInt("87654321")); assertEquals(987654321, parseInt("987654321")); + assertEquals(2147483647, parseInt("2147483647")); assertEquals(-4321, parseInt("-4321")); + assertEquals(-2147483648, parseInt("-2147483648")); } public void test_positive_negative_long() throws IOException { + assertEquals(0L, parseLong("0")); assertEquals(4321L, parseLong("4321")); assertEquals(54321L, parseLong("54321")); assertEquals(654321L, parseLong("654321")); assertEquals(7654321L, parseLong("7654321")); assertEquals(87654321L, parseLong("87654321")); assertEquals(987654321L, parseLong("987654321")); + assertEquals(9223372036854775807L, parseLong("9223372036854775807")); assertEquals(-4321L, parseLong("-4321")); + assertEquals(-9223372036854775808L, parseLong("-9223372036854775808")); } public void test_max_min_int() throws IOException { @@ -47,15 +66,46 @@ public void test_max_min_long() throws IOException { public void test_large_number() throws IOException { try { - JsonIterator.deserialize(Integer.toString(Integer.MIN_VALUE) + "1", Integer.class); + JsonIterator.deserialize("2147483648", Integer.class); fail(); } catch (JsonException e) { } + for (int i = 300000000; i < 2000000000; i += 10000000) { + try { + JsonIterator.deserialize(i + "0", Integer.class); + fail(); + } catch (JsonException e) { + } + try { + JsonIterator.deserialize(-i + "0", Integer.class); + fail(); + } catch (JsonException e) { + } + } try { - JsonIterator.deserialize(Long.toString(Long.MAX_VALUE) + "1", Long.class); + JsonIterator.deserialize("9223372036854775808", Long.class); fail(); } catch (JsonException e) { } + for (long i = 1000000000000000000L; i < 9000000000000000000L; i += 100000000000000000L) { + try { + JsonIterator.deserialize(i + "0", Long.class); + fail(); + } catch (JsonException e) { + } + try { + JsonIterator.deserialize(-i + "0", Long.class); + fail(); + } catch (JsonException e) { + } + } + } + + public void test_byte() throws IOException { + Byte val = JsonIterator.deserialize("120", Byte.class); + assertEquals(Byte.valueOf((byte) 120), val); + byte[] vals = JsonIterator.deserialize("[120]", byte[].class); + assertEquals((byte) 120, vals[0]); } @Category(StreamingCategory.class) @@ -68,13 +118,68 @@ public void test_streaming() throws IOException { test_large_number(); } + public void test_leading_zero() throws IOException { + assertEquals(Integer.valueOf(0), JsonIterator.deserialize("0", int.class)); + assertEquals(Long.valueOf(0), JsonIterator.deserialize("0", long.class)); + try { + JsonIterator.deserialize("01", int.class); + fail(); + } catch (JsonException e) { + } + try { + JsonIterator.deserialize("02147483647", int.class); + fail(); + } catch (JsonException e) { + } + try { + JsonIterator.deserialize("01", long.class); + fail(); + } catch (JsonException e) { + } + try { + JsonIterator.deserialize("09223372036854775807", long.class); + fail(); + } catch (JsonException e) { + } +/* FIXME if we should fail on parsing of leading zeroes for other numbers + try { + JsonIterator.deserialize("01", double.class); + fail(); + } catch (JsonException e) { + } + try { + JsonIterator.deserialize("01", float.class); + fail(); + } catch (JsonException e) { + } + try { + JsonIterator.deserialize("01", BigInteger.class); + fail(); + } catch (JsonException e) { + } + try { + JsonIterator.deserialize("01", BigDecimal.class); + fail(); + } catch (JsonException e) { + } +*/ + } + + public void test_max_int() throws IOException { + int[] ints = JsonIterator.deserialize("[2147483647,-2147483648]", int[].class); + assertEquals(Integer.MAX_VALUE, ints[0]); + assertEquals(Integer.MIN_VALUE, ints[1]); + } + private int parseInt(String input) throws IOException { if (isStreaming) { JsonIterator iter = JsonIterator.parse(new ByteArrayInputStream(input.getBytes()), 2); return iter.readInt(); } else { JsonIterator iter = JsonIterator.parse(input); - return iter.readInt(); + int v = iter.readInt(); + assertEquals(input.length(), iter.head); // iterator head should point on next non-parsed byte + return v; } } @@ -84,7 +189,24 @@ private long parseLong(String input) throws IOException { return iter.readLong(); } else { JsonIterator iter = JsonIterator.parse(input); - return iter.readLong(); + long v = iter.readLong(); + assertEquals(input.length(), iter.head); // iterator head should point on next non-parsed byte + return v; } } + + public void testBigInteger() { + BigInteger number = JsonIterator.deserialize("100", BigInteger.class); + assertEquals(new BigInteger("100"), number); + } + + public void testChooseInteger() { + Object number = JsonIterator.deserialize("100", Object.class); + assertEquals(100, number); + } + + public void testChooseLong() { + Object number = JsonIterator.deserialize(Long.valueOf(Long.MAX_VALUE).toString(), Object.class); + assertEquals(Long.MAX_VALUE, number); + } } diff --git a/src/test/java/com/jsoniter/TestJackson.java b/src/test/java/com/jsoniter/TestJackson.java new file mode 100644 index 00000000..7f95617e --- /dev/null +++ b/src/test/java/com/jsoniter/TestJackson.java @@ -0,0 +1,73 @@ +package com.jsoniter; + +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jsoniter.extra.JacksonCompatibilityMode; +import junit.framework.TestCase; + +import java.io.IOException; + +public class TestJackson extends TestCase { + + static { +// JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); + } + + private ObjectMapper objectMapper; + + public void setUp() { + objectMapper = new ObjectMapper(); + } + + public static class TestObject1 { + private int _id; + private String _name; + + @JsonAnySetter + public void setProperties(String key, Object value) { + if (key.equals("name")) { + _name = (String) value; + } else if (key.equals("id")) { + _id = ((Number) value).intValue(); + } + } + } + + public void test_JsonAnySetter() throws IOException { + TestObject1 obj = objectMapper.readValue("{\"name\":\"hello\",\"id\":100}", TestObject1.class); + assertEquals("hello", obj._name); + assertEquals(100, obj._id); + obj = JsonIterator.deserialize(new JacksonCompatibilityMode.Builder().build(), + "{\"name\":\"hello\",\"id\":100}", TestObject1.class); + assertEquals("hello", obj._name); + assertEquals(100, obj._id); + } + + public static class TestObject2 { + @JsonProperty("field-1") + public String field1; + } + + public void test_JsonProperty() throws IOException { + TestObject2 obj = objectMapper.readValue("{\"field-1\":\"hello\"}", TestObject2.class); + assertEquals("hello", obj.field1); + obj = JsonIterator.deserialize(new JacksonCompatibilityMode.Builder().build(), + "{\"field-1\":\"hello\"}", TestObject2.class); + assertEquals("hello", obj.field1); + } + + public static class TestObject3 { + @JsonIgnore + public String field1; + } + + public void test_JsonIgnore() throws IOException { + TestObject3 obj = objectMapper.readValue("{\"field1\":\"hello\"}", TestObject3.class); + assertNull(obj.field1); + obj = JsonIterator.deserialize(new JacksonCompatibilityMode.Builder().build(), + "{\"field1\":\"hello\"}", TestObject3.class); + assertNull(obj.field1); + } +} diff --git a/src/test/java/com/jsoniter/TestMap.java b/src/test/java/com/jsoniter/TestMap.java new file mode 100644 index 00000000..487b646d --- /dev/null +++ b/src/test/java/com/jsoniter/TestMap.java @@ -0,0 +1,83 @@ +package com.jsoniter; + +import com.jsoniter.extra.GsonCompatibilityMode; +import com.jsoniter.spi.Decoder; +import com.jsoniter.spi.JsoniterSpi; +import com.jsoniter.spi.TypeLiteral; +import junit.framework.TestCase; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +public class TestMap extends TestCase { + + static { +// JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); + } + + public void test_object_key() throws IOException { + Map map = JsonIterator.deserialize("{\"中文\":null}", new TypeLiteral>() { + }); + assertEquals(new HashMap() {{ + put("中文", null); + }}, map); + } + + public void test_string_key() throws IOException { + Map map = JsonIterator.deserialize("{\"中文\":null}", new TypeLiteral>() { + }); + assertEquals(new HashMap() {{ + put("中文", null); + }}, map); + } + + public void test_integer_key() throws IOException { + Map map = JsonIterator.deserialize("{\"100\":null}", new TypeLiteral>() { + }); + assertEquals(new HashMap() {{ + put(100, null); + }}, map); + } + + public static enum EnumKey { + KeyA, KeyB + } + + public void test_enum_key() { + Map map = JsonIterator.deserialize("{\"KeyA\":null}", new TypeLiteral>() { + }); + assertEquals(new HashMap() {{ + put(EnumKey.KeyA, null); + }}, map); + } + + public static class TestObject1 { + public int Field; + } + + public void test_MapKeyCodec() { + JsoniterSpi.registerMapKeyDecoder(TestObject1.class, new Decoder() { + @Override + public Object decode(JsonIterator iter) throws IOException { + TestObject1 obj = new TestObject1(); + obj.Field = Integer.valueOf(iter.readString()); + return obj; + } + }); + Map map = JsonIterator.deserialize("{\"100\":null}", new TypeLiteral>() { + }); + ArrayList keys = new ArrayList(map.keySet()); + assertEquals(1, keys.size()); + assertEquals(100, keys.get(0).Field); + // in new config + map = JsonIterator.deserialize( + new GsonCompatibilityMode.Builder().build(), + "{\"100\":null}", new TypeLiteral>() { + }); + keys = new ArrayList(map.keySet()); + assertEquals(1, keys.size()); + assertEquals(100, keys.get(0).Field); + } +} diff --git a/src/test/java/com/jsoniter/TestNested.java b/src/test/java/com/jsoniter/TestNested.java index c97cce3e..4c60813d 100644 --- a/src/test/java/com/jsoniter/TestNested.java +++ b/src/test/java/com/jsoniter/TestNested.java @@ -35,7 +35,7 @@ public void test_get_all_array_elements_via_any() throws IOException { assertEquals("[ 1, 3]", result.toString()); } - public void test_get_all_object_values_via_any() throws IOException { + public void skip_get_all_object_values_via_any() throws IOException { Any any = JsonIterator.deserialize("{\"field1\":[1,2],\"field2\":[3,4]}"); Any result = any.get('*', 1); assertEquals("{\"field1\":2,\"field2\":4}", result.toString()); @@ -58,4 +58,15 @@ public void test_get_all_with_some_invalid_path() throws IOException { result = any.get('*', 1); assertEquals("{\"field1\":2}", result.toString()); } + + public static class TestObject3 { + public com.jsoniter.output.TestNested.TestObject3 reference; + } + + public void test_recursive_class() { + // recursive reference will not be supported + // however recursive structure is supported + com.jsoniter.output.TestNested.TestObject3 obj = new com.jsoniter.output.TestNested.TestObject3(); + assertNull(JsonIterator.deserialize("{\"reference\":null}", TestObject3.class).reference); + } } diff --git a/src/test/java/com/jsoniter/TestNull.java b/src/test/java/com/jsoniter/TestNull.java new file mode 100644 index 00000000..56763039 --- /dev/null +++ b/src/test/java/com/jsoniter/TestNull.java @@ -0,0 +1,114 @@ +package com.jsoniter; + +import com.jsoniter.any.Any; +import com.jsoniter.spi.DecodingMode; +import junit.framework.TestCase; + +import java.math.BigDecimal; +import java.math.BigInteger; + +public class TestNull extends TestCase { + + static { +// JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_STRICTLY); + } + + public static class TestObject1 { + public Boolean field; + } + + public void test_null_as_Boolean() { + TestObject1 val = JsonIterator.deserialize("{\"field\":null}", TestObject1.class); + assertNull(val.field); + } + + public static class TestObject2 { + public Float field; + } + + public void test_null_as_Float() { + TestObject2 val = JsonIterator.deserialize("{\"field\":null}", TestObject2.class); + assertNull(val.field); + } + + public static class TestObject3 { + public Double field; + } + + public void test_null_as_Double() { + TestObject3 val = JsonIterator.deserialize("{\"field\":null}", TestObject3.class); + assertNull(val.field); + } + + public static class TestObject4 { + public Byte field; + } + + public void test_null_as_Byte() { + TestObject4 val = JsonIterator.deserialize("{\"field\":null}", TestObject4.class); + assertNull(val.field); + } + + public static class TestObject5 { + public Character field; + } + + public void test_null_as_Character() { + TestObject5 val = JsonIterator.deserialize("{\"field\":null}", TestObject5.class); + assertNull(val.field); + } + + public static class TestObject6 { + public Short field; + } + + public void test_null_as_Short() { + TestObject6 val = JsonIterator.deserialize("{\"field\":null}", TestObject6.class); + assertNull(val.field); + } + + public static class TestObject7 { + public Integer field; + } + + public void test_null_as_Integer() { + TestObject7 val = JsonIterator.deserialize("{\"field\":null}", TestObject7.class); + assertNull(val.field); + } + + public static class TestObject8 { + public Long field; + } + + public void test_null_as_Long() { + TestObject8 val = JsonIterator.deserialize("{\"field\":null}", TestObject8.class); + assertNull(val.field); + } + + public static class TestObject9 { + public BigDecimal field; + } + + public void test_null_as_BigDecimal() { + TestObject9 val = JsonIterator.deserialize("{\"field\":null}", TestObject9.class); + assertNull(val.field); + } + + public static class TestObject10 { + public BigInteger field; + } + + public void test_null_as_BigInteger() { + TestObject10 val = JsonIterator.deserialize("{\"field\":null}", TestObject10.class); + assertNull(val.field); + } + + public static class TestObject11 { + public Any field; + } + + public void test_null_as_Any() { + TestObject11 val = JsonIterator.deserialize("{\"field\":null}", TestObject11.class); + assertNull(val.field.object()); + } +} diff --git a/src/test/java/com/jsoniter/TestObject.java b/src/test/java/com/jsoniter/TestObject.java index dd6dbb28..357e4472 100644 --- a/src/test/java/com/jsoniter/TestObject.java +++ b/src/test/java/com/jsoniter/TestObject.java @@ -1,9 +1,9 @@ package com.jsoniter; import com.jsoniter.annotation.JsonProperty; -import com.jsoniter.annotation.JsoniterAnnotationSupport; import com.jsoniter.any.Any; import com.jsoniter.fuzzy.MaybeEmptyArrayDecoder; +import com.jsoniter.spi.DecodingMode; import com.jsoniter.spi.EmptyExtension; import com.jsoniter.spi.JsonException; import com.jsoniter.spi.JsoniterSpi; @@ -12,7 +12,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; import java.util.Map; public class TestObject extends TestCase { @@ -37,7 +36,7 @@ public void test_empty_object() throws IOException { assertNull(simpleObj.field1); iter.reset(iter.buf); Object obj = iter.read(Object.class); - assertEquals(0, ((Map)obj).size()); + assertEquals(0, ((Map) obj).size()); iter.reset(iter.buf); Any any = iter.readAny(); assertEquals(0, any.size()); @@ -57,7 +56,7 @@ public void test_one_field() throws IOException { assertEquals("hello", any.toString("field1")); assertEquals(ValueType.INVALID, any.get("field2").valueType()); iter.reset(iter.buf); - assertEquals("hello", ((Map)iter.read()).get("field1")); + assertEquals("hello", ((Map) iter.read()).get("field1")); } public void test_two_fields() throws IOException { @@ -167,6 +166,7 @@ public enum MyEnum { WORLD, WOW } + public MyEnum field1; } @@ -185,15 +185,20 @@ public void test_enum() throws IOException { assertEquals(TestObject5.MyEnum.WOW, obj.field1); } + public static class TestObject6_field1 { + public int a; + } + public static class TestObject6 { @JsonProperty(decoder = MaybeEmptyArrayDecoder.class) - public Map field1; + public TestObject6_field1 field1; } public void test_maybe_empty_array_field() { - JsoniterAnnotationSupport.enable(); TestObject6 obj = JsonIterator.deserialize("{\"field1\":[]}", TestObject6.class); assertNull(obj.field1); + obj = JsonIterator.deserialize("{\"field1\":{\"a\":1}}", TestObject6.class); + assertEquals(1, obj.field1.a); } public void test_iterator() { @@ -224,6 +229,7 @@ private static class PrivateSub extends PublicSuper { public static class TestObject7 { public PrivateSub field1; + public void setFieldXXX(PrivateSub obj) { } } @@ -233,9 +239,32 @@ public void test_private_ref() throws IOException { assertNull(obj.field1); } - public void test_object_lazy_any_to_string() { + public static class TestObject8 { + public String field1; + + @JsonProperty(from = {"field-1"}) + public void setField1(String obj) { + field1 = "!!!" + obj; + } + } + + public void test_setter_is_preferred() throws IOException { + TestObject8 obj = JsonIterator.deserialize("{\"field-1\":\"hello\"}", TestObject8.class); + assertEquals("!!!hello", obj.field1); + } + + public void skip_object_lazy_any_to_string() { Any any = JsonIterator.deserialize("{\"field1\":1,\"field2\":2,\"field3\":3}"); any.asMap().put("field4", Any.wrap(4)); assertEquals("{\"field1\":1,\"field3\":3,\"field2\":2,\"field4\":4}", any.toString()); } + + public static class TestObject9 { + public int 字段; + } + + public void test_non_ascii_field() { + TestObject9 obj = JsonIterator.deserialize("{\"字段\":100}", TestObject9.class); + assertEquals(100, obj.字段); + } } diff --git a/src/test/java/com/jsoniter/TestOmitValue.java b/src/test/java/com/jsoniter/TestOmitValue.java new file mode 100644 index 00000000..80c11e76 --- /dev/null +++ b/src/test/java/com/jsoniter/TestOmitValue.java @@ -0,0 +1,137 @@ +package com.jsoniter; + +import com.jsoniter.spi.OmitValue.*; +import junit.framework.TestCase; + +public class TestOmitValue extends TestCase { + + public void test_shouldOmitInputPositiveOutputFalse() { + + // Arrange + final ZeroByte objectUnderTest = new ZeroByte(); + final Object val = (byte)1; + + // Act + final boolean retval = objectUnderTest.shouldOmit(val); + + // Assert result + assertEquals(false, retval); + } + + public void test_shouldOmitInputPositiveOutputFalse2() { + + // Arrange + final ZeroInt objectUnderTest = new ZeroInt(); + final Object val = 1; + + // Act + final boolean retval = objectUnderTest.shouldOmit(val); + + // Assert result + assertEquals(false, retval); + } + + public void test_shouldOmitInputPositiveOutputFalse3() { + + // Arrange + final ZeroLong objectUnderTest = new ZeroLong(); + final Object val = 1L; + + // Act + final boolean retval = objectUnderTest.shouldOmit(val); + + // Assert result + assertEquals(false, retval); + } + + public void test_shouldOmitInputZeroOutputTrue() { + + // Arrange + final ZeroLong objectUnderTest = new ZeroLong(); + final Object val = 0L; + + // Act + final boolean retval = objectUnderTest.shouldOmit(val); + + // Assert result + assertEquals(true, retval); + } + + public void test_shouldOmitInputPositiveOutputFalse4() { + + // Arrange + final ZeroShort objectUnderTest = new ZeroShort(); + final Object val = (short)1; + + // Act + final boolean retval = objectUnderTest.shouldOmit(val); + + // Assert result + assertEquals(false, retval); + } + + public void test_shouldOmitInputTrueOutputFalse() { + + // Arrange + final False objectUnderTest = new False(); + final Object val = true; + + // Act + final boolean retval = objectUnderTest.shouldOmit(val); + + // Assert result + assertEquals(false, retval); + } + + public void test_shouldOmitInputNotNullOutputFalse() { + + // Arrange + final ZeroChar objectUnderTest = new ZeroChar(); + final Object val = '\u0001'; + + // Act + final boolean retval = objectUnderTest.shouldOmit(val); + + // Assert result + assertEquals(false, retval); + } + + public void test_shouldOmitInputPositiveOutputFalse5() { + + // Arrange + final ZeroDouble objectUnderTest = new ZeroDouble(); + final Object val = 0x0.0000000000001p-1022 /* 4.94066e-324 */; + + // Act + final boolean retval = objectUnderTest.shouldOmit(val); + + // Assert result + assertEquals(false, retval); + } + + public void test_shouldOmitInputZeroOutputTrue2() { + + // Arrange + final ZeroDouble objectUnderTest = new ZeroDouble(); + final Object val = 0.0; + + // Act + final boolean retval = objectUnderTest.shouldOmit(val); + + // Assert result + assertEquals(true, retval); + } + + public void test_shouldOmitInputPositiveOutputFalse6() { + + // Arrange + final ZeroFloat objectUnderTest = new ZeroFloat(); + final Object val = 0x1p-149f /* 1.4013e-45 */; + + // Act + final boolean retval = objectUnderTest.shouldOmit(val); + + // Assert result + assertEquals(false, retval); + } +} diff --git a/src/test/java/com/jsoniter/TestReflection.java b/src/test/java/com/jsoniter/TestReflection.java deleted file mode 100644 index 203a92ea..00000000 --- a/src/test/java/com/jsoniter/TestReflection.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.jsoniter; - -import com.jsoniter.spi.JsoniterSpi; -import junit.framework.TestCase; - -import java.io.IOException; - -public class TestReflection extends TestCase { - - public static class PackageLocal { - String field; - } - - public void test_package_local() throws IOException { - JsoniterSpi.registerTypeDecoder(PackageLocal.class, ReflectionDecoderFactory.create(PackageLocal.class)); - JsonIterator iter = JsonIterator.parse("{'field': 'hello'}".replace('\'', '"')); - PackageLocal obj = iter.read(PackageLocal.class); - assertEquals("hello", obj.field); - } - - public static class Inherited extends PackageLocal { - } - - public void test_inherited() throws IOException { - JsoniterSpi.registerTypeDecoder(Inherited.class, ReflectionDecoderFactory.create(Inherited.class)); - JsonIterator iter = JsonIterator.parse("{'field': 'hello'}".replace('\'', '"')); - Inherited obj = iter.read(Inherited.class); - assertEquals("hello", obj.field); - } - - public static class ObjectWithInt { - private int field; - } - - public void test_int_field() throws IOException { - JsoniterSpi.registerTypeDecoder(ObjectWithInt.class, ReflectionDecoderFactory.create(ObjectWithInt.class)); - JsonIterator iter = JsonIterator.parse("{'field': 100}".replace('\'', '"')); - ObjectWithInt obj = iter.read(ObjectWithInt.class); - assertEquals(100, obj.field); - } -} diff --git a/src/test/java/com/jsoniter/TestSlice.java b/src/test/java/com/jsoniter/TestSlice.java index 9648eb09..668372c6 100644 --- a/src/test/java/com/jsoniter/TestSlice.java +++ b/src/test/java/com/jsoniter/TestSlice.java @@ -1,5 +1,6 @@ package com.jsoniter; +import com.jsoniter.spi.Slice; import junit.framework.TestCase; import java.util.HashMap; @@ -18,4 +19,32 @@ public void test_hashcode() { assertEquals("hello", map.get(Slice.make("hello"))); assertEquals("world", map.get(Slice.make("world"))); } + + public void test_equalsInputNotNullOutputFalse2() { + + // Arrange + final byte[] byteArray = {(byte)2, (byte)1}; + final Slice objectUnderTest = new Slice(byteArray, 0, 1073741825); + final byte[] byteArray1 = {(byte)0}; + final Slice o = new Slice(byteArray1, 0, 1073741825); + + // Act + final boolean retval = objectUnderTest.equals(o); + + // Assert result + assertEquals(false, retval); + } + + public void test_equalsInputNotNullOutputFalse() { + + // Arrange + final Slice objectUnderTest = new Slice(null, 0, -2147483646); + final Slice o = new Slice(null, 0, 2); + + // Act + final boolean retval = objectUnderTest.equals(o); + + // Assert result + assertEquals(false, retval); + } } diff --git a/src/test/java/com/jsoniter/TestSpiPropertyDecoder.java b/src/test/java/com/jsoniter/TestSpiPropertyDecoder.java new file mode 100644 index 00000000..63924920 --- /dev/null +++ b/src/test/java/com/jsoniter/TestSpiPropertyDecoder.java @@ -0,0 +1,45 @@ +package com.jsoniter; + +import com.jsoniter.spi.Decoder; +import com.jsoniter.spi.JsoniterSpi; +import com.jsoniter.spi.TypeLiteral; +import junit.framework.TestCase; + +import java.io.IOException; + +public class TestSpiPropertyDecoder extends TestCase { + + static { +// JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); + } + + public static class TestObject1 { + public String field; + } + + public void test_PropertyDecoder() { + JsoniterSpi.registerPropertyDecoder(TestObject1.class, "field", new Decoder() { + @Override + public Object decode(JsonIterator iter) throws IOException { + iter.skip(); + return "hello"; + } + }); + TestObject1 obj = JsonIterator.deserialize("{\"field\":100}", TestObject1.class); + assertEquals("hello", obj.field); + } + + public void test_PropertyDecoder_for_type_literal() { + TypeLiteral> typeLiteral = new TypeLiteral>() { + }; + JsoniterSpi.registerPropertyDecoder(typeLiteral, "field", new Decoder() { + @Override + public Object decode(JsonIterator iter) throws IOException { + iter.skip(); + return "world"; + } + }); + TestObject1 obj = JsonIterator.deserialize("{\"field\":100}", typeLiteral); + assertEquals("world", obj.field); + } +} diff --git a/src/test/java/com/jsoniter/TestSpiTypeDecoder.java b/src/test/java/com/jsoniter/TestSpiTypeDecoder.java new file mode 100644 index 00000000..3127e181 --- /dev/null +++ b/src/test/java/com/jsoniter/TestSpiTypeDecoder.java @@ -0,0 +1,125 @@ +package com.jsoniter; + +import com.jsoniter.spi.Decoder; +import com.jsoniter.spi.JsonException; +import com.jsoniter.spi.JsoniterSpi; +import com.jsoniter.spi.TypeLiteral; +import junit.framework.TestCase; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +public class TestSpiTypeDecoder extends TestCase { + + static { +// JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); + } + + public static class TestObject1 { + public int field1; + } + + public void test_TypeDecoder() throws IOException { + JsoniterSpi.registerTypeDecoder(TestObject1.class, new Decoder() { + @Override + public Object decode(JsonIterator iter) throws IOException { + iter.skip(); + TestObject1 obj = new TestObject1(); + obj.field1 = 101; + return obj; + } + }); + TestObject1 obj = JsonIterator.deserialize( + "{'field1': 100}".replace('\'', '"'), TestObject1.class); + assertEquals(101, obj.field1); + } + + public void test_TypeDecoder_for_generics() throws IOException { + TypeLiteral> typeLiteral = new TypeLiteral>() { + }; + JsoniterSpi.registerTypeDecoder(typeLiteral, new Decoder() { + @Override + public Object decode(JsonIterator iter) throws IOException { + iter.skip(); + TestObject1 obj = new TestObject1(); + obj.field1 = 101; + return Arrays.asList(obj); + } + }); + List objs = JsonIterator.deserialize( + "{'field1': 100}".replace('\'', '"'), typeLiteral); + assertEquals(101, objs.get(0).field1); + } + + public static class MyDate { + Date date; + } + + static { + JsoniterSpi.registerTypeDecoder(MyDate.class, new Decoder() { + @Override + public Object decode(final JsonIterator iter) throws IOException { + return new MyDate() {{ + date = new Date(iter.readLong()); + }}; + } + }); + } + + public void test_direct() throws IOException { + JsonIterator iter = JsonIterator.parse("1481365190000"); + MyDate date = iter.read(MyDate.class); + assertEquals(1481365190000L, date.date.getTime()); + } + + public static class FieldWithMyDate { + public MyDate field; + } + + public void test_as_field_type() throws IOException { + JsonIterator iter = JsonIterator.parse("{'field': 1481365190000}".replace('\'', '"')); + FieldWithMyDate obj = iter.read(FieldWithMyDate.class); + assertEquals(1481365190000L, obj.field.date.getTime()); + } + + public void test_as_array_element() throws IOException { + JsonIterator iter = JsonIterator.parse("[1481365190000]"); + MyDate[] dates = iter.read(MyDate[].class); + assertEquals(1481365190000L, dates[0].date.getTime()); + } + + public static class MyList { + public List list; + } + + public void test_list_or_single_element() { + final TypeLiteral> listOfString = new TypeLiteral>() { + }; + JsoniterSpi.registerTypeDecoder(MyList.class, new Decoder() { + @Override + public Object decode(JsonIterator iter) throws IOException { + ValueType valueType = iter.whatIsNext(); + MyList myList = new MyList(); + switch (valueType) { + case ARRAY: + myList.list = iter.read(listOfString); + return myList; + case STRING: + myList.list = new ArrayList(); + myList.list.add(iter.readString()); + return myList; + default: + throw new JsonException("unexpected input"); + } + } + }); + MyList list1 = JsonIterator.deserialize("\"hello\"", MyList.class); + assertEquals("hello", list1.list.get(0)); + MyList list2 = JsonIterator.deserialize("[\"hello\",\"world\"]", MyList.class); + assertEquals("hello", list2.list.get(0)); + assertEquals("world", list2.list.get(1)); + } +} diff --git a/src/test/java/com/jsoniter/TestString.java b/src/test/java/com/jsoniter/TestString.java index 6b1ffa3e..07238a9d 100644 --- a/src/test/java/com/jsoniter/TestString.java +++ b/src/test/java/com/jsoniter/TestString.java @@ -95,6 +95,26 @@ public void test_incomplete_string() throws IOException { } } + public void test_invalid_string() throws IOException { + for (String str : new String[]{ + "\"\\x0008\"", + "\"\\u000Z\"", + "\"\\u000\"", + "\"\\u00\"", + "\"\\u0\"", + "\"\\\"", + "\"\\udd1e\"", + "\"\\ud834\"", + "\"\\ud834\\x\"", + "\"\\ud834\\ud834\"", + }) { + try {JsonIterator.deserialize(str, String.class); + } catch (JsonException e) { + } catch (IndexOutOfBoundsException e) { + } + } + } + public void test_long_string() throws IOException { JsonIterator iter = JsonIterator.parse("\"[\\\"LL\\\",\\\"MM\\\\\\/LW\\\",\\\"JY\\\",\\\"S\\\",\\\"C\\\",\\\"IN\\\",\\\"ME \\\\\\/ LE\\\"]\""); assertEquals("[\"LL\",\"MM\\/LW\",\"JY\",\"S\",\"C\",\"IN\",\"ME \\/ LE\"]", iter.readString()); diff --git a/src/test/java/com/jsoniter/any/TestArray.java b/src/test/java/com/jsoniter/any/TestArray.java index 216dd162..9744fec8 100644 --- a/src/test/java/com/jsoniter/any/TestArray.java +++ b/src/test/java/com/jsoniter/any/TestArray.java @@ -1,6 +1,8 @@ package com.jsoniter.any; import com.jsoniter.JsonIterator; +import com.jsoniter.output.EncodingMode; +import com.jsoniter.output.JsonStream; import junit.framework.TestCase; import java.util.ArrayList; @@ -63,4 +65,16 @@ public void test_fill_partial_then_iterate() { assertEquals(3, iter.next().toInt()); assertFalse(iter.hasNext()); } + + public void test_equals_and_hashcode() { + Any obj1 = JsonIterator.deserialize("[1,2,3]"); + Any obj2 = JsonIterator.deserialize("[1, 2, 3]"); + assertEquals(obj1, obj2); + assertEquals(obj1.hashCode(), obj2.hashCode()); + } + + public void test_null() { + Any x = JsonIterator.deserialize("{\"test\":null}"); + assertFalse(x.get("test").iterator().hasNext()); + } } diff --git a/src/test/java/com/jsoniter/any/TestList.java b/src/test/java/com/jsoniter/any/TestList.java index 91bd7429..0dcd5799 100644 --- a/src/test/java/com/jsoniter/any/TestList.java +++ b/src/test/java/com/jsoniter/any/TestList.java @@ -1,9 +1,12 @@ package com.jsoniter.any; +import com.jsoniter.JsonIterator; import junit.framework.TestCase; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; public class TestList extends TestCase { public void test_size() { @@ -12,14 +15,14 @@ public void test_size() { } public void test_to_boolean() { - Any any = Any.wrap(Arrays.asList()); + Any any = Any.wrap(Collections.emptyList()); assertFalse(any.toBoolean()); any = Any.wrap(Arrays.asList("hello", 1)); assertTrue(any.toBoolean()); } public void test_to_int() { - Any any = Any.wrap(Arrays.asList()); + Any any = Any.wrap(Collections.emptyList()); assertEquals(0, any.toInt()); any = Any.wrap(Arrays.asList("hello", 1)); assertEquals(2, any.toInt()); @@ -31,7 +34,7 @@ public void test_get() { } public void test_get_from_nested() { - Any any = Any.wrap(Arrays.asList(Arrays.asList("hello"), Arrays.asList("world"))); + Any any = Any.wrap(Arrays.asList(Collections.singletonList("hello"), Collections.singletonList("world"))); assertEquals("hello", any.get(0, 0).toString()); assertEquals("[\"hello\",\"world\"]", any.get('*', 0).toString()); } @@ -51,4 +54,10 @@ public void test_to_string() { any.asList().add(Any.wrap(4)); assertEquals("[1,2,3,4]", any.toString()); } + + public void test_for_each() { + Any a = JsonIterator.deserialize("[]"); + Iterator iter = a.iterator(); + assertFalse(iter.hasNext()); + } } diff --git a/src/test/java/com/jsoniter/any/TestLong.java b/src/test/java/com/jsoniter/any/TestLong.java new file mode 100644 index 00000000..247dbe8f --- /dev/null +++ b/src/test/java/com/jsoniter/any/TestLong.java @@ -0,0 +1,28 @@ +package com.jsoniter.any; + +import com.jsoniter.spi.JsonException; +import junit.framework.TestCase; + +public class TestLong extends TestCase { + public void test_to_string_should_trim() { + Any any = Any.lazyLong(" 1000".getBytes(), 0, " 1000".length()); + assertEquals("1000", any.toString()); + } + + public void test_should_fail_with_leading_zero() { + byte[] bytes = "01".getBytes(); + Any any = Any.lazyLong(bytes, 0, bytes.length); + try { + any.toLong(); + fail("This should fail."); + } catch (JsonException e) { + + } + } + + public void test_should_work_with_zero() { + byte[] bytes = "0".getBytes(); + Any any = Any.lazyLong(bytes, 0, bytes.length); + assertEquals(0L, any.toLong()); + } +} diff --git a/src/test/java/com/jsoniter/any/TestMap.java b/src/test/java/com/jsoniter/any/TestMap.java index f025fb63..e5bc31f6 100644 --- a/src/test/java/com/jsoniter/any/TestMap.java +++ b/src/test/java/com/jsoniter/any/TestMap.java @@ -1,6 +1,5 @@ package com.jsoniter.any; -import com.jsoniter.JsonIterator; import junit.framework.TestCase; import java.util.HashMap; diff --git a/src/test/java/com/jsoniter/extra/TestJdkDatetime.java b/src/test/java/com/jsoniter/extra/TestJdkDatetime.java index 07273260..4f57a859 100644 --- a/src/test/java/com/jsoniter/extra/TestJdkDatetime.java +++ b/src/test/java/com/jsoniter/extra/TestJdkDatetime.java @@ -1,7 +1,6 @@ package com.jsoniter.extra; import com.jsoniter.JsonIterator; -import com.jsoniter.extra.JdkDatetimeSupport; import com.jsoniter.output.JsonStream; import junit.framework.TestCase; @@ -10,7 +9,7 @@ public class TestJdkDatetime extends TestCase { - public void test() { + public void skip_test() { JdkDatetimeSupport.enable("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); assertEquals("\"1970-01-01T08:00:00.000+0800\"", JsonStream.serialize(new Date(0))); Date obj = JsonIterator.deserialize("\"1970-01-01T08:00:00.000+0800\"", Date.class); diff --git a/src/test/java/com/jsoniter/output/TestAnnotation.java b/src/test/java/com/jsoniter/output/TestAnnotation.java deleted file mode 100644 index 6472f01b..00000000 --- a/src/test/java/com/jsoniter/output/TestAnnotation.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.jsoniter.output; - -import com.jsoniter.annotation.JsonIgnore; -import com.jsoniter.annotation.JsonProperty; -import com.jsoniter.annotation.JsonUnwrapper; -import com.jsoniter.annotation.JsoniterAnnotationSupport; -import com.jsoniter.spi.Encoder; -import junit.framework.TestCase; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -public class TestAnnotation extends TestCase { - static { - JsoniterAnnotationSupport.enable(); -// JsonStream.setMode(EncodingMode.DYNAMIC_MODE); - } - - private ByteArrayOutputStream baos; - private JsonStream stream; - - public void setUp() { - baos = new ByteArrayOutputStream(); - stream = new JsonStream(baos, 4096); - } - - public static class TestObject1 { - @JsonProperty(to = {"field-1"}) - public String field1; - } - - public void test_property() throws IOException { - TestObject1 obj = new TestObject1(); - obj.field1 = "hello"; - stream.writeVal(obj); - stream.close(); - assertEquals("{\"field-1\":\"hello\"}", baos.toString()); - } - - public static class TestObject2 { - @JsonProperty(encoder = Encoder.StringIntEncoder.class) - public int field1; - } - - public void test_encoder() throws IOException { - TestObject2 obj = new TestObject2(); - obj.field1 = 100; - stream.writeVal(obj); - stream.close(); - assertEquals("{\"field1\":\"100\"}", baos.toString()); - } - - public static class TestObject3 { - @JsonIgnore - public int field1; - } - - public void test_ignore() throws IOException { - TestObject3 obj = new TestObject3(); - obj.field1 = 100; - stream.writeVal(obj); - stream.close(); - assertEquals("{}", baos.toString()); - } - - public static class TestObject4 { - public int field1; - - public int getField1() { - return field1; - } - } - - public void test_name_conflict() throws IOException { - TestObject4 obj = new TestObject4(); - stream.writeVal(obj); - stream.close(); - assertEquals("{\"field1\":0}", baos.toString()); - } - - public static class TestObject5 { - @JsonUnwrapper - public void unwrap(JsonStream stream) throws IOException { - stream.writeObjectField("hello"); - stream.writeVal("world"); - } - } - - public void test_unwrapper() throws IOException { - TestObject5 obj = new TestObject5(); - stream.writeVal(obj); - stream.close(); - assertEquals("{\"hello\":\"world\"}", baos.toString()); - } -} diff --git a/src/test/java/com/jsoniter/output/TestAnnotationJsonIgnore.java b/src/test/java/com/jsoniter/output/TestAnnotationJsonIgnore.java new file mode 100644 index 00000000..a7fab10d --- /dev/null +++ b/src/test/java/com/jsoniter/output/TestAnnotationJsonIgnore.java @@ -0,0 +1,46 @@ +package com.jsoniter.output; + +import com.jsoniter.annotation.JsonIgnore; +import junit.framework.TestCase; + +import java.io.IOException; + +public class TestAnnotationJsonIgnore extends TestCase { + + public static class TestObject1 { + @JsonIgnore + public int field1; + } + + public void test_ignore() throws IOException { + TestObject1 obj = new TestObject1(); + obj.field1 = 100; + assertEquals("{}", JsonStream.serialize(obj)); + } + + public static class TestObject2 { + @JsonIgnore(ignoreEncoding = false) + public int field1; + } + + public void test_ignore_decoding_only() throws IOException { + TestObject2 obj = new TestObject2(); + obj.field1 = 100; + assertEquals("{\"field1\":100}", JsonStream.serialize(obj)); + } + + public static class TestPrivateVariables { + @JsonIgnore + private String field1; + + public String getField1() { + return field1; + } + } + + public void test_private_serialize() throws IOException { + TestPrivateVariables obj = new TestPrivateVariables(); + obj.field1 = "hello"; + assertEquals("{}", JsonStream.serialize(obj)); + } +} diff --git a/src/test/java/com/jsoniter/output/TestAnnotationJsonProperty.java b/src/test/java/com/jsoniter/output/TestAnnotationJsonProperty.java new file mode 100644 index 00000000..495941df --- /dev/null +++ b/src/test/java/com/jsoniter/output/TestAnnotationJsonProperty.java @@ -0,0 +1,71 @@ +package com.jsoniter.output; + +import com.jsoniter.annotation.JsonProperty; +import com.jsoniter.spi.Encoder; +import junit.framework.TestCase; + +import java.io.IOException; + +public class TestAnnotationJsonProperty extends TestCase { + + static { +// JsonStream.setMode(EncodingMode.DYNAMIC_MODE); + } + + public static class TestObject1 { + @JsonProperty(to = {"field-1"}) + public String field1; + } + + public void test_property() throws IOException { + TestObject1 obj = new TestObject1(); + obj.field1 = "hello"; + String output = JsonStream.serialize(obj); + assertEquals("{\"field-1\":\"hello\"}", output); + } + + + public static class TestObject2 { + @JsonProperty(encoder = Encoder.StringIntEncoder.class) + public int field1; + } + + public void test_encoder() throws IOException { + TestObject2 obj = new TestObject2(); + obj.field1 = 100; + String output = JsonStream.serialize(obj); + assertEquals("{\"field1\":\"100\"}", output); + } + + public static class TestObject3 { + public String field1 = "hello"; + + @JsonProperty("field-1") + public String getField1() { + return field1; + } + } + + public void test_getter() throws IOException { + String output = JsonStream.serialize(new TestObject3()); + assertEquals("{\"field-1\":\"hello\"}", output); + } + + public static class TestObject4 { + private String field1 = "hello"; + + @JsonProperty("field-1") + public String getField1() { + return field1; + } + + public void setField1(String field1) { + this.field1 = field1; + } + } + + public void test_getter_and_setter() throws IOException { + String output = JsonStream.serialize(new TestObject4()); + assertEquals("{\"field-1\":\"hello\"}", output); + } +} diff --git a/src/test/java/com/jsoniter/output/TestAnnotationJsonUnwrapper.java b/src/test/java/com/jsoniter/output/TestAnnotationJsonUnwrapper.java new file mode 100644 index 00000000..9ad9f66c --- /dev/null +++ b/src/test/java/com/jsoniter/output/TestAnnotationJsonUnwrapper.java @@ -0,0 +1,51 @@ +package com.jsoniter.output; + +import com.jsoniter.annotation.JsonUnwrapper; +import junit.framework.TestCase; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class TestAnnotationJsonUnwrapper extends TestCase { + + private ByteArrayOutputStream baos; + private JsonStream stream; + + public void setUp() { + baos = new ByteArrayOutputStream(); + stream = new JsonStream(baos, 4096); + } + + public static class TestObject1 { + @JsonUnwrapper + public void unwrap(JsonStream stream) throws IOException { + stream.writeObjectField("hello"); + stream.writeVal("world"); + } + } + + public void test_unwrapper() throws IOException { + TestObject1 obj = new TestObject1(); + stream.writeVal(obj); + stream.close(); + assertEquals("{\"hello\":\"world\"}", baos.toString()); + } + + public static class TestObject2 { + @JsonUnwrapper + public Map getProperties() { + HashMap properties = new HashMap(); + properties.put(100, "hello"); + return properties; + } + } + + public void test_unwrapper_with_map() throws IOException { + TestObject2 obj = new TestObject2(); + stream.writeVal(obj); + stream.close(); + assertEquals("{\"100\":\"hello\"}", baos.toString()); + } +} diff --git a/src/test/java/com/jsoniter/output/TestAny.java b/src/test/java/com/jsoniter/output/TestAny.java index f7d01023..5e4ce3e8 100644 --- a/src/test/java/com/jsoniter/output/TestAny.java +++ b/src/test/java/com/jsoniter/output/TestAny.java @@ -2,13 +2,23 @@ import com.jsoniter.ValueType; import com.jsoniter.any.*; +import com.jsoniter.spi.JsonException; import junit.framework.TestCase; +import org.junit.Rule; +import org.junit.rules.ExpectedException; import java.util.Arrays; import java.util.HashMap; public class TestAny extends TestCase { + @Rule + public final ExpectedException exception = ExpectedException.none(); + + static { +// JsonStream.setMode(EncodingMode.DYNAMIC_MODE); + } + public void test_int() { Any any = Any.wrap(100); assertEquals(ValueType.NUMBER, any.valueType()); @@ -116,7 +126,13 @@ public void test_array() { assertEquals("[1,2,3]", any.toString()); } - public void test_map() { + public void test_not_found() { + Any any = Any.wrap(new int[]{1, 2, 3}); + exception.expect(JsonException.class); + any.get("not", "found", "path"); + } + + public void skip_map() { HashMap val = new HashMap(); val.put("hello", 1); val.put("world", "!!"); @@ -133,7 +149,7 @@ public static class MyClass { public Any field2; } - public void test_my_class() { + public void skip_my_class() { MyClass val = new MyClass(); val.field1 = "hello"; val.field2 = Any.wrap(new long[]{1, 2}); diff --git a/src/test/java/com/jsoniter/output/TestArray.java b/src/test/java/com/jsoniter/output/TestArray.java index 29cec1ca..80626f1e 100644 --- a/src/test/java/com/jsoniter/output/TestArray.java +++ b/src/test/java/com/jsoniter/output/TestArray.java @@ -1,5 +1,6 @@ package com.jsoniter.output; +import com.jsoniter.spi.Config; import com.jsoniter.spi.TypeLiteral; import junit.framework.TestCase; @@ -116,4 +117,36 @@ public void test_arrays_as_list() throws IOException { public void test_default_empty_collection() throws IOException { assertEquals("[]", JsonStream.serialize(Collections.emptySet())); } + + public void test_indention() { + Config cfg = new Config.Builder() + .encodingMode(EncodingMode.REFLECTION_MODE) + .indentionStep(2) + .build(); + assertEquals("[\n" + + " 1,\n" + + " 2\n" + + "]", JsonStream.serialize(cfg, new int[]{1, 2})); + cfg = new Config.Builder() + .encodingMode(EncodingMode.DYNAMIC_MODE) + .indentionStep(2) + .build(); + assertEquals("[\n" + + " 1,\n" + + " 2\n" + + "]", JsonStream.serialize(cfg, new int[]{1, 2})); + } + + public void test_indention_with_empty_array() { + Config cfg = new Config.Builder() + .encodingMode(EncodingMode.REFLECTION_MODE) + .indentionStep(2) + .build(); + assertEquals("[]", JsonStream.serialize(cfg, new int[]{})); + cfg = new Config.Builder() + .encodingMode(EncodingMode.DYNAMIC_MODE) + .indentionStep(2) + .build(); + assertEquals("[]", JsonStream.serialize(cfg, new int[]{})); + } } diff --git a/src/test/java/com/jsoniter/output/TestCollection.java b/src/test/java/com/jsoniter/output/TestCollection.java new file mode 100644 index 00000000..ffe12eae --- /dev/null +++ b/src/test/java/com/jsoniter/output/TestCollection.java @@ -0,0 +1,41 @@ +package com.jsoniter.output; + +import com.jsoniter.spi.Config; +import junit.framework.TestCase; + +import java.util.HashSet; + +public class TestCollection extends TestCase { + + public void test_indention() { + HashSet set = new HashSet(); + set.add(1); + Config cfg = new Config.Builder() + .encodingMode(EncodingMode.REFLECTION_MODE) + .indentionStep(2) + .build(); + assertEquals("[\n" + + " 1\n" + + "]", JsonStream.serialize(cfg, set)); + cfg = new Config.Builder() + .encodingMode(EncodingMode.DYNAMIC_MODE) + .indentionStep(2) + .build(); + assertEquals("[\n" + + " 1\n" + + "]", JsonStream.serialize(cfg, set)); + } + + public void test_indention_with_empty_array() { + Config cfg = new Config.Builder() + .encodingMode(EncodingMode.REFLECTION_MODE) + .indentionStep(2) + .build(); + assertEquals("[]", JsonStream.serialize(cfg, new HashSet())); + cfg = new Config.Builder() + .encodingMode(EncodingMode.DYNAMIC_MODE) + .indentionStep(2) + .build(); + assertEquals("[]", JsonStream.serialize(cfg, new HashSet())); + } +} diff --git a/src/test/java/com/jsoniter/output/TestCustomizeField.java b/src/test/java/com/jsoniter/output/TestCustomizeField.java deleted file mode 100644 index 1b8a03ca..00000000 --- a/src/test/java/com/jsoniter/output/TestCustomizeField.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.jsoniter.output; - -import com.jsoniter.any.Any; -import com.jsoniter.spi.Encoder; -import com.jsoniter.spi.JsoniterSpi; -import junit.framework.TestCase; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -public class TestCustomizeField extends TestCase { - - private ByteArrayOutputStream baos; - private JsonStream stream; - - public void setUp() { - baos = new ByteArrayOutputStream(); - stream = new JsonStream(baos, 4096); - } - - public static class TestObject1 { - public String field1; - } - - public void test_customize_field_decoder() throws IOException { - JsoniterSpi.registerPropertyEncoder(TestObject1.class, "field1", new Encoder() { - @Override - public void encode(Object obj, JsonStream stream) throws IOException { - String str = (String) obj; - stream.writeVal(Integer.valueOf(str)); - } - - @Override - public Any wrap(Object obj) { - throw new UnsupportedOperationException(); - } - }); - TestObject1 obj = new TestObject1(); - obj.field1 = "100"; - stream.writeVal(obj); - stream.close(); - assertEquals("{'field1':100}".replace('\'', '"'), baos.toString()); - } -} diff --git a/src/test/java/com/jsoniter/output/TestCustomizeType.java b/src/test/java/com/jsoniter/output/TestCustomizeType.java deleted file mode 100644 index 2ac910fb..00000000 --- a/src/test/java/com/jsoniter/output/TestCustomizeType.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.jsoniter.output; - -import com.jsoniter.spi.EmptyEncoder; -import com.jsoniter.spi.JsoniterSpi; -import junit.framework.TestCase; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Date; - -public class TestCustomizeType extends TestCase { - - private ByteArrayOutputStream baos; - private JsonStream stream; - - public void setUp() { - baos = new ByteArrayOutputStream(); - stream = new JsonStream(baos, 4096); - } - - public static class MyDate { - Date date; - } - - public void test() throws IOException { - JsoniterSpi.registerTypeEncoder(MyDate.class, new EmptyEncoder() { - @Override - public void encode(Object obj, JsonStream stream) throws IOException { - MyDate date = (MyDate) obj; - stream.writeVal(date.date.getTime()); - } - }); - MyDate myDate = new MyDate(); - myDate.date = new Date(1481365190000L); - stream.writeVal(myDate); - stream.close(); - assertEquals("1481365190000", baos.toString()); - } -} diff --git a/src/test/java/com/jsoniter/output/TestFloat.java b/src/test/java/com/jsoniter/output/TestFloat.java new file mode 100644 index 00000000..faf72d31 --- /dev/null +++ b/src/test/java/com/jsoniter/output/TestFloat.java @@ -0,0 +1,17 @@ +package com.jsoniter.output; + +import junit.framework.TestCase; + +import java.math.BigDecimal; + +public class TestFloat extends TestCase { + public void testBigDecimal() { + assertEquals("100.1", JsonStream.serialize(new BigDecimal("100.1"))); + } + public void test_infinity() { + assertEquals("\"Infinity\"", JsonStream.serialize(Double.POSITIVE_INFINITY)); + assertEquals("\"Infinity\"", JsonStream.serialize(Float.POSITIVE_INFINITY)); + assertEquals("\"-Infinity\"", JsonStream.serialize(Double.NEGATIVE_INFINITY)); + assertEquals("\"-Infinity\"", JsonStream.serialize(Float.NEGATIVE_INFINITY)); + } +} diff --git a/src/test/java/com/jsoniter/output/TestGenerics.java b/src/test/java/com/jsoniter/output/TestGenerics.java new file mode 100644 index 00000000..ff06d780 --- /dev/null +++ b/src/test/java/com/jsoniter/output/TestGenerics.java @@ -0,0 +1,57 @@ +package com.jsoniter.output; + +import junit.framework.TestCase; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TestGenerics extends TestCase { + static { +// JsonStream.setMode(EncodingMode.DYNAMIC_MODE); + } + + private ByteArrayOutputStream baos; + private JsonStream stream; + + public void setUp() { + baos = new ByteArrayOutputStream(); + stream = new JsonStream(baos, 4096); + } + + public interface TestObject6Interface { + A getHello(); + } + + public static class TestObject6 implements TestObject6Interface { + public Integer getHello() { + return 0; + } + } + + public void test_inherited_getter_is_not_duplicate() throws IOException { + TestObject6 obj = new TestObject6(); + stream.writeVal(obj); + stream.close(); + assertEquals("{\"hello\":0}", baos.toString()); + } + + public static class TestObject7 { + public List field; + public Map field2; + } + + public void test_wildcard() throws IOException { + TestObject7 obj = new TestObject7(); + ArrayList list = new ArrayList(); + list.add(1); + obj.field = list; + HashMap map = new HashMap(); + map.put("hello", 1); + obj.field2 = map; + assertEquals("{\"field\":[1],\"field2\":{\"hello\":1}}", JsonStream.serialize(obj)); + } +} diff --git a/src/test/java/com/jsoniter/output/TestGson.java b/src/test/java/com/jsoniter/output/TestGson.java new file mode 100644 index 00000000..afa78828 --- /dev/null +++ b/src/test/java/com/jsoniter/output/TestGson.java @@ -0,0 +1,297 @@ +package com.jsoniter.output; + +import com.google.gson.*; +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import com.google.gson.annotations.Since; +import com.google.gson.annotations.Until; +import com.jsoniter.extra.GsonCompatibilityMode; +import com.jsoniter.spi.JsoniterSpi; +import junit.framework.TestCase; + +import java.lang.reflect.Field; +import java.text.DateFormat; +import java.util.Date; +import java.util.TimeZone; + +public class TestGson extends TestCase { + + public static class TestObject1 { + @SerializedName("field-1") + public String field1; + } + + public void test_SerializedName_on_field() { + Gson gson = new GsonBuilder().create(); + TestObject1 obj = new TestObject1(); + obj.field1 = "hello"; + String output = gson.toJson(obj); + assertEquals("{\"field-1\":\"hello\"}", output); + output = JsonStream.serialize(new GsonCompatibilityMode.Builder().build(), obj); + assertEquals("{\"field-1\":\"hello\"}", output); + } + + public static class TestObject2 { + @Expose(serialize = false) + public String field1; + } + + public void test_Expose() { + Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); + TestObject2 obj = new TestObject2(); + obj.field1 = "hello"; + String output = gson.toJson(obj); + assertEquals("{}", output); + + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .excludeFieldsWithoutExposeAnnotation().build(); + output = JsonStream.serialize(config, obj); + assertEquals("{}", output); + } + + public static class TestObject3 { + public String getField1() { + return "hello"; + } + } + + public void test_getter_should_be_ignored() { + Gson gson = new GsonBuilder().create(); + TestObject3 obj = new TestObject3(); + String output = gson.toJson(obj); + assertEquals("{}", output); + output = JsonStream.serialize(new GsonCompatibilityMode.Builder().build(), obj); + assertEquals("{}", output); + } + + public static class TestObject4 { + public String field1; + } + + public void test_excludeFieldsWithoutExposeAnnotation() { + Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); + TestObject4 obj = new TestObject4(); + obj.field1 = "hello"; + String output = gson.toJson(obj); + assertEquals("{}", output); + + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .excludeFieldsWithoutExposeAnnotation().build(); + output = JsonStream.serialize(config, obj); + assertEquals("{}", output); + } + + public static class TestObject5 { + public String field1; + public int field2; + } + + public void test_serializeNulls() { + TestObject5 obj = new TestObject5(); + Gson gson = new GsonBuilder().create(); + String output = gson.toJson(obj); + assertEquals("{\"field2\":0}", output); + + gson = new GsonBuilder().serializeNulls().create(); + output = gson.toJson(obj); + assertEquals("{\"field1\":null,\"field2\":0}", output); + + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .build(); + output = JsonStream.serialize(config, obj); + assertEquals("{\"field2\":0}", output); + + config = new GsonCompatibilityMode.Builder() + .serializeNulls().build(); + output = JsonStream.serialize(config, obj); + assertEquals("{\"field1\":null,\"field2\":0}", output); + } + + public void test_setDateFormat_with_style() { + TimeZone orig = TimeZone.getDefault(); + try { + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + Gson gson = new GsonBuilder() + .setDateFormat(DateFormat.LONG, DateFormat.LONG) + .create(); + String output = gson.toJson(new Date(0)); + assertEquals("\"January 1, 1970 12:00:00 AM UTC\"", output); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .setDateFormat(DateFormat.LONG, DateFormat.LONG) + .build(); + output = JsonStream.serialize(config, new Date(0)); + assertEquals("\"January 1, 1970 12:00:00 AM UTC\"", output); + } finally { + TimeZone.setDefault(orig); + } + } + + public void test_setDateFormat_with_format() { + TimeZone orig = TimeZone.getDefault(); + try { + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + Gson gson = new GsonBuilder() + .setDateFormat("EEE, MMM d, yyyy hh:mm:ss a z") + .create(); + String output = gson.toJson(new Date(0)); + assertEquals("\"Thu, Jan 1, 1970 12:00:00 AM UTC\"", output); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .setDateFormat("EEE, MMM d, yyyy hh:mm:ss a z") + .build(); + output = JsonStream.serialize(config, new Date(0)); + assertEquals("\"Thu, Jan 1, 1970 12:00:00 AM UTC\"", output); + } finally { + TimeZone.setDefault(orig); + } + } + + public void test_setFieldNamingStrategy() { + FieldNamingStrategy fieldNamingStrategy = new FieldNamingStrategy() { + @Override + public String translateName(Field f) { + return "_" + f.getName(); + } + }; + Gson gson = new GsonBuilder() + .setFieldNamingStrategy(fieldNamingStrategy) + .create(); + TestObject4 obj = new TestObject4(); + obj.field1 = "hello"; + String output = gson.toJson(obj); + assertEquals("{\"_field1\":\"hello\"}", output); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .setFieldNamingStrategy(fieldNamingStrategy) + .build(); + output = JsonStream.serialize(config, obj); + assertEquals("{\"_field1\":\"hello\"}", output); + } + + public void test_setFieldNamingPolicy() { + Gson gson = new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE) + .create(); + TestObject4 obj = new TestObject4(); + obj.field1 = "hello"; + String output = gson.toJson(obj); + assertEquals("{\"Field1\":\"hello\"}", output); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE) + .build(); + output = JsonStream.serialize(config, obj); + assertEquals("{\"Field1\":\"hello\"}", output); + } + + public void test_setPrettyPrinting() { + if (JsoniterSpi.getCurrentConfig().encodingMode() != EncodingMode.REFLECTION_MODE) { + return; + } + Gson gson = new GsonBuilder() + .setPrettyPrinting() + .create(); + TestObject4 obj = new TestObject4(); + obj.field1 = "hello"; + String output = gson.toJson(obj); + assertEquals("{\n" + + " \"field1\": \"hello\"\n" + + "}", output); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .setPrettyPrinting() + .build(); + output = JsonStream.serialize(config, obj); + assertEquals("{\n" + + " \"field1\": \"hello\"\n" + + "}", output); + } + + public void test_disableHtmlEscaping_off() { + Gson gson = new GsonBuilder() + .disableHtmlEscaping() + .create(); + String output = gson.toJson("中文"); + assertEquals("\"中文\"", output); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .disableHtmlEscaping() + .build(); + output = JsonStream.serialize(config, "中文"); + assertEquals("\"中文\"", output); + } + + public void test_disableHtmlEscaping_on() { + Gson gson = new GsonBuilder() + .create(); + String output = gson.toJson(" "); + assertEquals("\"\\u003chtml\\u003e\\u0026nbsp;\\u003c/html\\u003e\"", output); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .build(); + output = JsonStream.serialize(config, " "); + assertEquals("\"\\u003chtml\\u003e\\u0026nbsp;\\u003c/html\\u003e\"", output); + } + + public static class TestObject6 { + @Since(3.0) + public String field1 = "field1"; + @Until(1.0) + public String field2 = "field2"; + @Since(2.0) + public String field3 = "field3"; + @Until(2.0) + public String field4 = "field4"; + } + + public void test_setVersion() { + TestObject6 obj = new TestObject6(); + Gson gson = new GsonBuilder() + .setVersion(2.0) + .create(); + String output = gson.toJson(obj); + assertEquals("{\"field3\":\"field3\"}", output); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .setVersion(2.0) + .build(); + output = JsonStream.serialize(config, obj); + assertEquals("{\"field3\":\"field3\"}", output); + } + + public void test_addSerializationExclusionStrategy() { + TestObject6 obj = new TestObject6(); + ExclusionStrategy exclusionStrategy = new ExclusionStrategy() { + @Override + public boolean shouldSkipField(FieldAttributes f) { + return !f.getName().equals("field3"); + } + + @Override + public boolean shouldSkipClass(Class clazz) { + return false; + } + }; + Gson gson = new GsonBuilder() + .addSerializationExclusionStrategy(exclusionStrategy) + .create(); + String output = gson.toJson(obj); + assertEquals("{\"field3\":\"field3\"}", output); + GsonCompatibilityMode config = new GsonCompatibilityMode.Builder() + .addSerializationExclusionStrategy(exclusionStrategy) + .build(); + output = JsonStream.serialize(config, obj); + assertEquals("{\"field3\":\"field3\"}", output); + } + + + private static class TestObject { + private String test; + } + + public void test_surrogate() { + GsonCompatibilityMode gsonConfig = + new GsonCompatibilityMode.Builder() + .disableHtmlEscaping() + .build(); + + String input = "{\"test\":\"lorem-\uD83D\uDC44\uD83D\uDC40\"}"; + TestObject testObject = new Gson().fromJson(input, TestObject.class); + + System.out.println("Gson: " + new Gson().toJson(testObject)); + System.out.println("jsoniter: " + JsonStream.serialize(gsonConfig, testObject)); + } +} diff --git a/src/test/java/com/jsoniter/output/TestInteger.java b/src/test/java/com/jsoniter/output/TestInteger.java new file mode 100644 index 00000000..64dc1ce9 --- /dev/null +++ b/src/test/java/com/jsoniter/output/TestInteger.java @@ -0,0 +1,11 @@ +package com.jsoniter.output; + +import junit.framework.TestCase; + +import java.math.BigInteger; + +public class TestInteger extends TestCase { + public void testBigInteger() { + assertEquals("100", JsonStream.serialize(new BigInteger("100"))); + } +} diff --git a/src/test/java/com/jsoniter/output/TestJackson.java b/src/test/java/com/jsoniter/output/TestJackson.java new file mode 100644 index 00000000..9d2bd821 --- /dev/null +++ b/src/test/java/com/jsoniter/output/TestJackson.java @@ -0,0 +1,71 @@ +package com.jsoniter.output; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.jsoniter.extra.JacksonCompatibilityMode; +import junit.framework.TestCase; + +import java.util.HashMap; +import java.util.Map; + +public class TestJackson extends TestCase { + + static { +// JsonStream.setMode(EncodingMode.DYNAMIC_MODE); + } + + private ObjectMapper objectMapper; + + public void setUp() { + objectMapper = new ObjectMapper(); + } + + public static class TestObject1 { + @JsonAnyGetter + public Map getProperties() { + HashMap properties = new HashMap(); + properties.put(100, "hello"); + return properties; + } + } + + public void test_JsonAnyGetter() throws JsonProcessingException { + String output = objectMapper.writeValueAsString(new TestObject1()); + assertEquals("{\"100\":\"hello\"}", output); + output = JsonStream.serialize(new JacksonCompatibilityMode.Builder().build(), new TestObject1()); + assertEquals("{\"100\":\"hello\"}", output); + } + + public static class TestObject2 { + @JsonProperty("field-1") + public String field1; + } + + public void test_JsonProperty() throws JsonProcessingException { + TestObject2 obj = new TestObject2(); + obj.field1 = "hello"; + String output = objectMapper.writeValueAsString(obj); + assertEquals("{\"field-1\":\"hello\"}", output); + output = JsonStream.serialize(new JacksonCompatibilityMode.Builder().build(), obj); + assertEquals("{\"field-1\":\"hello\"}", output); + } + + public static class TestObject3 { + @JsonIgnore + public String field1; + } + + public void test_JsonIgnore() throws JsonProcessingException { + objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + TestObject3 obj = new TestObject3(); + obj.field1 = "hello"; + String output = objectMapper.writeValueAsString(obj); + assertEquals("{}", output); + output = JsonStream.serialize(new JacksonCompatibilityMode.Builder().build(), obj); + assertEquals("{}", output); + } +} diff --git a/src/test/java/com/jsoniter/output/TestList.java b/src/test/java/com/jsoniter/output/TestList.java new file mode 100644 index 00000000..665fcd3f --- /dev/null +++ b/src/test/java/com/jsoniter/output/TestList.java @@ -0,0 +1,42 @@ +package com.jsoniter.output; + +import com.jsoniter.spi.Config; +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.Arrays; + +public class TestList extends TestCase { + + public void test_indention() { + Config cfg = new Config.Builder() + .encodingMode(EncodingMode.REFLECTION_MODE) + .indentionStep(2) + .build(); + assertEquals("[\n" + + " 1,\n" + + " 2\n" + + "]", JsonStream.serialize(cfg, Arrays.asList(1, 2))); + cfg = new Config.Builder() + .encodingMode(EncodingMode.DYNAMIC_MODE) + .indentionStep(2) + .build(); + assertEquals("[\n" + + " 1,\n" + + " 2\n" + + "]", JsonStream.serialize(cfg, Arrays.asList(1, 2))); + } + + public void test_indention_with_empty_array() { + Config cfg = new Config.Builder() + .encodingMode(EncodingMode.REFLECTION_MODE) + .indentionStep(2) + .build(); + assertEquals("[]", JsonStream.serialize(cfg, new ArrayList())); + cfg = new Config.Builder() + .encodingMode(EncodingMode.DYNAMIC_MODE) + .indentionStep(2) + .build(); + assertEquals("[]", JsonStream.serialize(cfg, new ArrayList())); + } +} diff --git a/src/test/java/com/jsoniter/output/TestMap.java b/src/test/java/com/jsoniter/output/TestMap.java index b7b31807..2b91d6f0 100644 --- a/src/test/java/com/jsoniter/output/TestMap.java +++ b/src/test/java/com/jsoniter/output/TestMap.java @@ -1,10 +1,14 @@ package com.jsoniter.output; +import com.jsoniter.spi.Config; +import com.jsoniter.spi.Encoder; +import com.jsoniter.spi.JsoniterSpi; import com.jsoniter.spi.TypeLiteral; import junit.framework.TestCase; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; @@ -38,7 +42,8 @@ public void test_empty() throws IOException { } public void test_null() throws IOException { - stream.writeVal(new TypeLiteral(){}, null); + stream.writeVal(new TypeLiteral() { + }, null); stream.close(); assertEquals("null".replace('\'', '"'), baos.toString()); } @@ -46,8 +51,109 @@ public void test_null() throws IOException { public void test_value_is_null() throws IOException { HashMap obj = new HashMap(); obj.put("hello", null); - stream.writeVal(new TypeLiteral>(){}, obj); + stream.writeVal(new TypeLiteral>() { + }, obj); stream.close(); assertEquals("{\"hello\":null}", baos.toString()); } + + public void test_integer_key() throws IOException { + HashMap obj = new HashMap(); + obj.put(100, null); + stream.writeVal(new TypeLiteral>() { + }, obj); + stream.close(); + assertEquals("{\"100\":null}", baos.toString()); + } + + public static enum EnumKey { + KeyA, KeyB + } + + public void test_enum_key() throws IOException { + HashMap obj = new HashMap(); + obj.put(EnumKey.KeyA, null); + stream.writeVal(new TypeLiteral>() { + }, obj); + stream.close(); + assertEquals("{\"KeyA\":null}", baos.toString()); + } + + public static class TestObject1 { + public int Field; + } + + public void test_MapKeyCodec() { + JsoniterSpi.registerMapKeyEncoder(TestObject1.class, new Encoder() { + @Override + public void encode(Object obj, JsonStream stream) throws IOException { + TestObject1 mapKey = (TestObject1) obj; + stream.writeVal(String.valueOf(mapKey.Field)); + } + }); + HashMap obj = new HashMap(); + obj.put(new TestObject1(), null); + String output = JsonStream.serialize(new TypeLiteral>() { + }, obj); + assertEquals("{\"0\":null}", output); + } + + public void skip_indention() { + Map map = new HashMap(); + map.put("field1", "1"); + map.put("field2", "2"); + Config dynamicCfg = new Config.Builder() + .indentionStep(2) + .encodingMode(EncodingMode.DYNAMIC_MODE) + .build(); + String output = JsonStream.serialize(dynamicCfg, map); + assertEquals("{\n" + + " \"field1\": \"1\",\n" + + " \"field2\": \"2\"\n" + + "}", output); + Config reflectionCfg = new Config.Builder() + .indentionStep(2) + .encodingMode(EncodingMode.REFLECTION_MODE) + .build(); + output = JsonStream.serialize(reflectionCfg, map); + assertEquals("{\n" + + " \"field1\": \"1\",\n" + + " \"field2\": \"2\"\n" + + "}", output); + } + + public void test_indention_with_empty_map() { + Config config = JsoniterSpi.getCurrentConfig().copyBuilder() + .indentionStep(2) + .encodingMode(EncodingMode.REFLECTION_MODE) + .build(); + assertEquals("{}", JsonStream.serialize(config, new HashMap())); + config = JsoniterSpi.getCurrentConfig().copyBuilder() + .indentionStep(2) + .encodingMode(EncodingMode.DYNAMIC_MODE) + .build(); + assertEquals("{}", JsonStream.serialize(config, new HashMap())); + } + + public void test_int_as_map_key() { + HashMap m = new HashMap(); + m.put(1, "2"); + assertEquals("{\"1\":\"2\"}", JsonStream.serialize(new TypeLiteral>() { + }, m)); + } + + public void test_object_key() { + HashMap m = new HashMap(); + m.put(1, 2); + assertEquals("{\"1\":2}", JsonStream.serialize(m)); + } + + public void test_multiple_keys() { + HashMap map = new HashMap(); + map.put("destination", "test_destination_value"); + map.put("amount", new BigDecimal("0.0000101101")); + map.put("password", "test_pass"); + final String serialized = JsonStream.serialize(map); + assertEquals(-1, serialized.indexOf("::")); + } } diff --git a/src/test/java/com/jsoniter/output/TestNested.java b/src/test/java/com/jsoniter/output/TestNested.java index 4c4263b0..00fd8d6d 100644 --- a/src/test/java/com/jsoniter/output/TestNested.java +++ b/src/test/java/com/jsoniter/output/TestNested.java @@ -1,5 +1,7 @@ package com.jsoniter.output; +import com.jsoniter.annotation.JsonProperty; +import com.jsoniter.spi.JsoniterSpi; import com.jsoniter.spi.TypeLiteral; import junit.framework.TestCase; @@ -12,6 +14,10 @@ public class TestNested extends TestCase { + static { +// JsonStream.setMode(EncodingMode.DYNAMIC_MODE); + } + private ByteArrayOutputStream baos; private JsonStream stream; @@ -29,21 +35,21 @@ public void test_array_of_objects() throws IOException { TestObject1 obj1 = new TestObject1(); obj1.field1 = "1"; obj1.field2 = "2"; - stream.writeVal(new TestObject1[]{obj1}); - stream.close(); - assertEquals("[{'field1':'1','field2':'2'}]".replace('\'', '"'), baos.toString()); + String output = JsonStream.serialize(new TestObject1[]{obj1}); + assertTrue(output.contains("field1")); + assertTrue(output.contains("field2")); } public void test_collection_of_objects() throws IOException { final TestObject1 obj1 = new TestObject1(); obj1.field1 = "1"; obj1.field2 = "2"; - stream.writeVal(new TypeLiteral>() { + String output = JsonStream.serialize(new TypeLiteral>() { }, new ArrayList() {{ add(obj1); }}); - stream.close(); - assertEquals("[{'field1':'1','field2':'2'}]".replace('\'', '"'), baos.toString()); + assertTrue(output.contains("field1")); + assertTrue(output.contains("field2")); } public static class TestObject2 { @@ -51,39 +57,65 @@ public static class TestObject2 { } public void test_object_of_array() throws IOException { - stream.indentionStep = 2; - TestObject2 obj = new TestObject2(); - obj.objs = new TestObject1[1]; - obj.objs[0] = new TestObject1(); - obj.objs[0].field1 = "1"; - obj.objs[0].field2 = "2"; - stream.writeVal(obj); - stream.close(); - assertEquals("{\n" + - " \"objs\":[\n" + - " {\n" + - " \"field1\":\"1\",\n" + - " \"field2\":\"2\"\n" + - " }\n" + - " ]\n" + - "}".replace('\'', '"'), baos.toString()); + if (JsoniterSpi.getCurrentConfig().encodingMode() != EncodingMode.REFLECTION_MODE) { + return; + } + JsonStream.setIndentionStep(2); + try { + TestObject2 obj = new TestObject2(); + obj.objs = new TestObject1[1]; + obj.objs[0] = new TestObject1(); + obj.objs[0].field1 = "1"; + obj.objs[0].field2 = "2"; + stream.writeVal(obj); + stream.close(); + assertEquals("{\n" + + " \"objs\": [\n" + + " {\n" + + " \"field1\": \"1\",\n" + + " \"field2\": \"2\"\n" + + " }\n" + + " ]\n" + + "}".replace('\'', '"'), baos.toString()); + } finally { + JsonStream.setIndentionStep(0); + } } public void test_map_of_objects() throws IOException { - stream.indentionStep = 2; - final TestObject1 obj1 = new TestObject1(); - obj1.field1 = "1"; - obj1.field2 = "2"; - stream.writeVal(new TypeLiteral>() { - }, new HashMap() {{ - put("hello", obj1); - }}); - stream.close(); - assertEquals("{\n" + - " \"hello\":{\n" + - " \"field1\":\"1\",\n" + - " \"field2\":\"2\"\n" + - " }\n" + - "}".replace('\'', '"'), baos.toString()); + if (JsoniterSpi.getCurrentConfig().encodingMode() != EncodingMode.REFLECTION_MODE) { + return; + } + JsonStream.setIndentionStep(2); + try { + final TestObject1 obj1 = new TestObject1(); + obj1.field1 = "1"; + obj1.field2 = "2"; + stream.writeVal(new TypeLiteral>() { + }, new HashMap() {{ + put("hello", obj1); + }}); + stream.close(); + assertEquals("{\n" + + " \"hello\": {\n" + + " \"field1\": \"1\",\n" + + " \"field2\": \"2\"\n" + + " }\n" + + "}".replace('\'', '"'), baos.toString()); + } finally { + JsonStream.setIndentionStep(0); + } + } + + public static class TestObject3 { + @JsonProperty(defaultValueToOmit = "void") + public TestObject3 reference; + } + + public void test_recursive_class() { + // recursive reference will not be supported + // however recursive structure is supported + TestObject3 obj = new TestObject3(); + assertEquals("{\"reference\":null}", JsonStream.serialize(obj)); } } diff --git a/src/test/java/com/jsoniter/output/TestObject.java b/src/test/java/com/jsoniter/output/TestObject.java index 013a9831..9563b966 100644 --- a/src/test/java/com/jsoniter/output/TestObject.java +++ b/src/test/java/com/jsoniter/output/TestObject.java @@ -2,7 +2,9 @@ import com.jsoniter.annotation.JsonIgnore; import com.jsoniter.annotation.JsonProperty; -import com.jsoniter.annotation.JsoniterAnnotationSupport; +import com.jsoniter.spi.Config; +import com.jsoniter.spi.JsonException; +import com.jsoniter.spi.JsoniterSpi; import com.jsoniter.spi.TypeLiteral; import junit.framework.TestCase; @@ -13,7 +15,6 @@ public class TestObject extends TestCase { static { - JsoniterAnnotationSupport.enable(); // JsonStream.setMode(EncodingMode.DYNAMIC_MODE); } @@ -46,14 +47,6 @@ public String getField1() { } } - public void test_getter() throws IOException { - TestObject2 obj = new TestObject2(); - obj.field1 = "hello"; - stream.writeVal(obj); - stream.close(); - assertEquals("{'field1':'hello'}".replace('\'', '"'), baos.toString()); - } - public void test_null() throws IOException { stream.writeVal(new TypeLiteral() { }, null); @@ -78,7 +71,7 @@ public void test_null_field() throws IOException { TestObject4 obj = new TestObject4(); stream.writeVal(obj); stream.close(); - assertEquals("{}".replace('\'', '"'), baos.toString()); + assertEquals("{\"field1\":null}".replace('\'', '"'), baos.toString()); } public static enum MyEnum { @@ -95,6 +88,13 @@ public void test_enum() throws IOException { stream.writeVal(obj); stream.close(); assertEquals("{'field1':'HELLO'}".replace('\'', '"'), baos.toString()); + Config cfg = new Config.Builder() + .encodingMode(EncodingMode.DYNAMIC_MODE) + .indentionStep(2) + .build(); + assertEquals("{\n" + + " \"field1\": \"HELLO\"\n" + + "}", JsonStream.serialize(cfg, obj)); } public static class TestObject6 { @@ -113,20 +113,19 @@ public void test_array_field_is_null() throws IOException { TestObject6 obj = new TestObject6(); stream.writeVal(obj); stream.close(); - assertEquals("{}", baos.toString()); + assertEquals("{\"field1\":null}", baos.toString()); } public static class TestObject7 { private int[] field1; - @JsonProperty(omitNull = false) + @JsonProperty(defaultValueToOmit = "void") public int[] getField1() { return field1; } } public void test_array_field_is_null_via_getter() throws IOException { - JsoniterAnnotationSupport.enable(); TestObject7 obj = new TestObject7(); stream.writeVal(obj); stream.close(); @@ -139,76 +138,77 @@ public static class TestObject8 { } public void test_not_nullable() { - JsoniterAnnotationSupport.enable(); TestObject8 obj = new TestObject8(); obj.field1 = new String[]{"hello"}; - assertEquals("{\"field1\":[\"hello\"]}", JsonStream.serialize(obj)); - if (Codegen.mode == EncodingMode.DYNAMIC_MODE) { - try { - JsonStream.serialize(new TestObject8()); - fail(); - } catch (NullPointerException e) { - } + Config config = new Config.Builder() + .encodingMode(EncodingMode.DYNAMIC_MODE) + .build(); + assertEquals("{\"field1\":[\"hello\"]}", + JsonStream.serialize(config, obj)); + try { + JsonStream.serialize(config, new TestObject8()); + fail(); + } catch (NullPointerException ignore) { } } public static class TestObject9 { - @JsonProperty(collectionValueNullable = false) + @JsonProperty(collectionValueNullable = false, defaultValueToOmit = "null") public String[] field1; - @JsonProperty(collectionValueNullable = false) + @JsonProperty(collectionValueNullable = false, defaultValueToOmit = "null") public List field2; - @JsonProperty(collectionValueNullable = false) + @JsonProperty(collectionValueNullable = false, defaultValueToOmit = "null") public Set field3; - @JsonProperty(collectionValueNullable = false) + @JsonProperty(collectionValueNullable = false, defaultValueToOmit = "null") public Map field4; } public void test_collection_value_not_nullable() { - JsoniterAnnotationSupport.enable(); TestObject9 obj = new TestObject9(); obj.field1 = new String[]{"hello"}; assertEquals("{\"field1\":[\"hello\"]}", JsonStream.serialize(obj)); - if (Codegen.mode == EncodingMode.DYNAMIC_MODE) { - obj = new TestObject9(); - obj.field1 = new String[]{null}; - try { - JsonStream.serialize(obj); - fail(); - } catch (NullPointerException e) { - } - - obj = new TestObject9(); - obj.field2 = new ArrayList(); - obj.field2.add(null); - try { - JsonStream.serialize(obj); - fail(); - } catch (NullPointerException e) { - } - - obj = new TestObject9(); - obj.field3 = new HashSet(); - obj.field3.add(null); - try { - JsonStream.serialize(obj); - fail(); - } catch (NullPointerException e) { - } - - obj = new TestObject9(); - obj.field4 = new HashMap(); - obj.field4.put("hello", null); - try { - JsonStream.serialize(obj); - fail(); - } catch (NullPointerException e) { - } + Config config = new Config.Builder() + .encodingMode(EncodingMode.DYNAMIC_MODE) + .build(); + obj = new TestObject9(); + obj.field1 = new String[]{null}; + try { + JsonStream.serialize(config, obj); + fail(); + } catch (NullPointerException ignore) { + } + + obj = new TestObject9(); + obj.field2 = new ArrayList(); + obj.field2.add(null); + try { + JsonStream.serialize(config, obj); + fail(); + } catch (NullPointerException ignore) { + } + + obj = new TestObject9(); + obj.field3 = new HashSet(); + obj.field3.add(null); + try { + JsonStream.serialize(config, obj); + fail(); + } catch (NullPointerException ignore) { + } + + obj = new TestObject9(); + obj.field4 = new HashMap(); + obj.field4.put("hello", null); + try { + JsonStream.serialize(config, obj); + fail(); + } catch (NullPointerException ignore) { } } public static class TestObject10 { - @JsonProperty(omitNull = false) + @JsonProperty(defaultValueToOmit = "void") public String field1; } @@ -217,22 +217,209 @@ public void test_not_omit_null() { } public static class TestObject11 { + @JsonProperty(defaultValueToOmit = "null") public String field1; + @JsonProperty(defaultValueToOmit = "null") public String field2; - public String field3; + @JsonProperty(nullable = false) + public Integer field3; } public void test_omit_null() { -// JsonStream.setMode(EncodingMode.DYNAMIC_MODE); - assertEquals("{}", JsonStream.serialize(new TestObject11())); + assertEquals("{\"field3\":null}", JsonStream.serialize(new TestObject11())); TestObject11 obj = new TestObject11(); obj.field1 = "hello"; - assertEquals("{\"field1\":\"hello\"}", JsonStream.serialize(obj)); + assertEquals("{\"field1\":\"hello\",\"field3\":null}", JsonStream.serialize(obj)); obj = new TestObject11(); obj.field2 = "hello"; - assertEquals("{\"field2\":\"hello\"}", JsonStream.serialize(obj)); + assertEquals("{\"field2\":\"hello\",\"field3\":null}", JsonStream.serialize(obj)); obj = new TestObject11(); - obj.field3 = "hello"; - assertEquals("{\"field3\":\"hello\"}", JsonStream.serialize(obj)); + obj.field3 = 3; + assertEquals("{\"field3\":3}", JsonStream.serialize(obj)); + } + + + public static class TestObject12 { + public int field1; + + public int getField1() { + return field1; + } + } + + public void test_name_conflict() throws IOException { + TestObject12 obj = new TestObject12(); + stream.writeVal(obj); + stream.close(); + assertEquals("{\"field1\":0}", baos.toString()); + } + + private static class TestObject13 { + } + + public void test_private_class() { + EncodingMode encodingMode = JsoniterSpi.getCurrentConfig().encodingMode(); + if (EncodingMode.REFLECTION_MODE.equals(encodingMode)) { + return; + } + try { + JsonStream.serialize(new TestObject13()); + fail("should throw JsonException"); + } catch (JsonException ignore) { + + } + } + + public static class TestObject14 { + @JsonProperty(nullable = true, defaultValueToOmit = "null") + public String field1; + @JsonProperty(nullable = false) + public String field2; + @JsonProperty(nullable = true, defaultValueToOmit = "void") + public String field3; + } + + public void test_indention() { + Config dynamicCfg = new Config.Builder() + .indentionStep(2) + .encodingMode(EncodingMode.DYNAMIC_MODE) + .build(); + TestObject14 obj = new TestObject14(); + obj.field1 = "1"; + obj.field2 = "2"; + String output = JsonStream.serialize(dynamicCfg, obj); + assertEquals("{\n" + + " \"field1\": \"1\",\n" + + " \"field2\": \"2\",\n" + + " \"field3\": null\n" + + "}", output); + Config reflectionCfg = new Config.Builder() + .indentionStep(2) + .encodingMode(EncodingMode.REFLECTION_MODE) + .build(); + output = JsonStream.serialize(reflectionCfg, obj); + assertEquals("{\n" + + " \"field1\": \"1\",\n" + + " \"field2\": \"2\",\n" + + " \"field3\": null\n" + + "}", output); + } + + public static class TestObject15 { + @JsonProperty(defaultValueToOmit = "null") + public Integer i1; + @JsonProperty(defaultValueToOmit = "null") + public Integer i2; + } + + public void test_indention_with_empty_object() { + Config config = JsoniterSpi.getCurrentConfig().copyBuilder() + .indentionStep(2) + .encodingMode(EncodingMode.REFLECTION_MODE) + .build(); + assertEquals("{}", JsonStream.serialize(config, new TestObject15())); + config = JsoniterSpi.getCurrentConfig().copyBuilder() + .indentionStep(2) + .encodingMode(EncodingMode.DYNAMIC_MODE) + .build(); + assertEquals("{}", JsonStream.serialize(config, new TestObject15())); + } + + public static class TestObject16 { + @JsonProperty(defaultValueToOmit = "void") + public Integer i; + } + + public void test_missing_notFirst() { + Config cfg = JsoniterSpi.getCurrentConfig().copyBuilder() + .indentionStep(2) + .encodingMode(EncodingMode.DYNAMIC_MODE) + .build(); + assertEquals("{\n" + + " \"i\": null\n" + + "}", JsonStream.serialize(cfg, new TestObject16())); + } + + public static class TestObject17 { + public boolean b; + public int i; + public byte bt; + public short s; + public long l = 1; + public float f; + public double d = 1; + public char e; + } + + public void test_omit_default() { + Config cfg = new Config.Builder() + .omitDefaultValue(true) + .build(); + assertEquals("{\"l\":1,\"d\":1}", JsonStream.serialize(cfg, new TestObject17())); + cfg = new Config.Builder() + .omitDefaultValue(true) + .encodingMode(EncodingMode.DYNAMIC_MODE) + .build(); + assertEquals("{\"l\":1,\"d\":1}", JsonStream.serialize(cfg, new TestObject17())); + } + + public static class TestObject18 { + @JsonProperty(defaultValueToOmit = "true") + public boolean b = true; + @JsonProperty(defaultValueToOmit = "true") + public Boolean B = true; + @JsonProperty(defaultValueToOmit = "1") + public int i = 1; + @JsonProperty(defaultValueToOmit = "1") + public Integer I = 1; + @JsonProperty(defaultValueToOmit = "1") + public byte bt = 1; + @JsonProperty(defaultValueToOmit = "1") + public Byte BT = 1; + @JsonProperty(defaultValueToOmit = "1") + public short s = 1; + @JsonProperty(defaultValueToOmit = "1") + public Short S = 1; + @JsonProperty(defaultValueToOmit = "1") + public long l = 1L; + @JsonProperty(defaultValueToOmit = "1") + public Long L = 1L; + @JsonProperty(defaultValueToOmit = "1") + public float f = 1F; + @JsonProperty(defaultValueToOmit = "1") + public Float F = 1F; + @JsonProperty(defaultValueToOmit = "1") + public double d = 1D; + @JsonProperty(defaultValueToOmit = "1") + public Double D = 1D; + @JsonProperty(defaultValueToOmit = "a") + public char c = 'a'; + @JsonProperty(defaultValueToOmit = "a") + public Character C = 'a'; + } + + public void test_omit_selft_defined() { + Config cfg = new Config.Builder() + .omitDefaultValue(true) + .build(); + assertEquals("{}", JsonStream.serialize(cfg, new TestObject18())); + cfg = new Config.Builder() + .omitDefaultValue(true) + .encodingMode(EncodingMode.DYNAMIC_MODE) + .build(); + assertEquals("{}", JsonStream.serialize(cfg, new TestObject18())); + } + + public static class TestObject19 { + public transient int hello; + + public int getHello() { + return hello; + } + } + + public void test_transient_field_getter() { + String output = JsonStream.serialize(new TestObject19()); + assertEquals("{}", output); } } diff --git a/src/test/java/com/jsoniter/output/TestSpiPropertyEncoder.java b/src/test/java/com/jsoniter/output/TestSpiPropertyEncoder.java new file mode 100644 index 00000000..4fd1ae24 --- /dev/null +++ b/src/test/java/com/jsoniter/output/TestSpiPropertyEncoder.java @@ -0,0 +1,44 @@ +package com.jsoniter.output; + +import com.jsoniter.spi.Encoder; +import com.jsoniter.spi.JsoniterSpi; +import com.jsoniter.spi.TypeLiteral; +import junit.framework.TestCase; +import java.io.IOException; + +public class TestSpiPropertyEncoder extends TestCase { + + public static class TestObject1 { + public String field1; + } + + public void test_PropertyEncoder() throws IOException { + JsoniterSpi.registerPropertyEncoder(TestObject1.class, "field1", new Encoder() { + @Override + public void encode(Object obj, JsonStream stream) throws IOException { + String str = (String) obj; + stream.writeVal(Integer.valueOf(str)); + } + }); + TestObject1 obj = new TestObject1(); + obj.field1 = "100"; + String output = JsonStream.serialize(obj); + assertEquals("{'field1':100}".replace('\'', '"'), output); + } + + public void test_PropertyEncoder_for_type_literal() throws IOException { + TypeLiteral> typeLiteral = new TypeLiteral>() { + }; + JsoniterSpi.registerPropertyEncoder(typeLiteral, "field1", new Encoder() { + @Override + public void encode(Object obj, JsonStream stream) throws IOException { + String str = (String) obj; + stream.writeVal(Integer.valueOf(str) + 1); + } + }); + TestObject1 obj = new TestObject1(); + obj.field1 = "100"; + String output = JsonStream.serialize(typeLiteral, obj); + assertEquals("{'field1':101}".replace('\'', '"'), output); + } +} diff --git a/src/test/java/com/jsoniter/output/TestSpiTypeEncoder.java b/src/test/java/com/jsoniter/output/TestSpiTypeEncoder.java new file mode 100644 index 00000000..c3b7c53b --- /dev/null +++ b/src/test/java/com/jsoniter/output/TestSpiTypeEncoder.java @@ -0,0 +1,53 @@ +package com.jsoniter.output; + +import com.jsoniter.spi.Encoder; +import com.jsoniter.spi.JsoniterSpi; +import com.jsoniter.spi.TypeLiteral; +import junit.framework.TestCase; + +import java.io.IOException; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +public class TestSpiTypeEncoder extends TestCase { + + static { +// JsonStream.setMode(EncodingMode.DYNAMIC_MODE); + } + + public static class MyDate { + Date date; + } + + public void test_TypeEncoder() throws IOException { + JsoniterSpi.registerTypeEncoder(MyDate.class, new Encoder() { + @Override + public void encode(Object obj, JsonStream stream) throws IOException { + MyDate date = (MyDate) obj; + stream.writeVal(date.date.getTime()); + } + }); + System.out.println(JsoniterSpi.getCurrentConfig().configName()); + MyDate myDate = new MyDate(); + myDate.date = new Date(1481365190000L); + String output = JsonStream.serialize(myDate); + assertEquals("1481365190000", output); + } + + public void test_TypeEncoder_for_type_literal() { + TypeLiteral> typeLiteral = new TypeLiteral>() { + }; + JsoniterSpi.registerTypeEncoder(typeLiteral, new Encoder() { + @Override + public void encode(Object obj, JsonStream stream) throws IOException { + List dates = (List) obj; + stream.writeVal(dates.get(0).date.getTime()); + } + }); + MyDate myDate = new MyDate(); + myDate.date = new Date(1481365190000L); + String output = JsonStream.serialize(typeLiteral, Collections.singletonList(myDate)); + assertEquals("1481365190000", output); + } +} diff --git a/src/test/java/com/jsoniter/output/TestStreamBuffer.java b/src/test/java/com/jsoniter/output/TestStreamBuffer.java new file mode 100644 index 00000000..7148cf3c --- /dev/null +++ b/src/test/java/com/jsoniter/output/TestStreamBuffer.java @@ -0,0 +1,68 @@ +package com.jsoniter.output; + +import com.jsoniter.spi.Config; +import com.jsoniter.spi.JsoniterSpi; +import junit.framework.TestCase; + +import java.io.IOException; + +public class TestStreamBuffer extends TestCase { + + public void test_write_string() throws IOException { + JsonStream jsonStream = new JsonStream(null, 32); + jsonStream.writeVal("01234567"); + jsonStream.writeVal("01234567"); + jsonStream.writeVal("012345678"); + jsonStream.writeVal(""); + assertEquals(33, jsonStream.buffer().len()); + } + + public void test_write_raw() throws IOException { + JsonStream jsonStream = new JsonStream(null, 32); + jsonStream.writeRaw("0123456789"); + jsonStream.writeRaw("0123456789"); + jsonStream.writeRaw("0123456789"); + jsonStream.writeRaw("0123456789"); + assertEquals(40, jsonStream.buffer().len()); + } + + public void test_write_bytes() throws IOException { + JsonStream jsonStream = new JsonStream(null, 32); + jsonStream.write("0123456789".getBytes()); + jsonStream.write("0123456789".getBytes()); + jsonStream.write("0123456789".getBytes()); + jsonStream.write("0123456789".getBytes()); + assertEquals(40, jsonStream.buffer().len()); + } + + public void test_write_indention() throws IOException { + Config oldConfig = JsoniterSpi.getCurrentConfig(); + try { + JsoniterSpi.setCurrentConfig(new Config.Builder().indentionStep(32).build()); + JsonStream jsonStream = new JsonStream(null, 32); + jsonStream.writeArrayStart(); + jsonStream.writeIndention(); + assertEquals(34, jsonStream.buffer().len()); + } finally { + JsoniterSpi.setCurrentConfig(oldConfig); + } + } + + public void test_write_int() throws IOException { + JsonStream jsonStream = new JsonStream(null, 32); + jsonStream.writeVal(123456789); + jsonStream.writeVal(123456789); + jsonStream.writeVal(123456789); + jsonStream.writeVal(123456789); + assertEquals(36, jsonStream.buffer().len()); + } + + public void test_write_long() throws IOException { + JsonStream jsonStream = new JsonStream(null, 32); + jsonStream.writeVal(123456789L); + jsonStream.writeVal(123456789L); + jsonStream.writeVal(123456789L); + jsonStream.writeVal(123456789L); + assertEquals(36, jsonStream.buffer().len()); + } +} diff --git a/src/test/java/com/jsoniter/output/TestString.java b/src/test/java/com/jsoniter/output/TestString.java new file mode 100644 index 00000000..186c770a --- /dev/null +++ b/src/test/java/com/jsoniter/output/TestString.java @@ -0,0 +1,40 @@ +package com.jsoniter.output; + +import com.jsoniter.spi.Config; +import com.jsoniter.spi.Config.Builder; +import com.jsoniter.spi.JsoniterSpi; +import java.io.ByteArrayOutputStream; +import junit.framework.TestCase; + +public class TestString extends TestCase { + + public static final String UTF8_GREETING = "Привет čau 你好 ~"; + + public void test_unicode() { + String output = JsonStream.serialize(new Config.Builder().escapeUnicode(false).build(), "中文"); + assertEquals("\"中文\"", output); + } + public void test_unicode_tilde() { + final String tilde = "~"; + String output = JsonStream.serialize(new Config.Builder().escapeUnicode(false).build(), tilde); + assertEquals("\""+tilde+"\"", output); + } + public void test_escape_unicode() { + final Config config = new Builder().escapeUnicode(false).build(); + + assertEquals("\""+UTF8_GREETING+"\"", JsonStream.serialize(config, UTF8_GREETING)); + assertEquals("\""+UTF8_GREETING+"\"", JsonStream.serialize(config.escapeUnicode(), UTF8_GREETING.getClass(), UTF8_GREETING)); + } + public void test_escape_control_character() { + String output = JsonStream.serialize(new String(new byte[]{0})); + assertEquals("\"\\u0000\"", output); + } + public void test_serialize_into_output_stream() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + boolean escapeUnicode = JsoniterSpi.getCurrentConfig().escapeUnicode(); + JsoniterSpi.setCurrentConfig(JsoniterSpi.getCurrentConfig().copyBuilder().escapeUnicode(false).build()); + JsonStream.serialize(String.class, UTF8_GREETING, baos); + JsoniterSpi.setCurrentConfig(JsoniterSpi.getCurrentConfig().copyBuilder().escapeUnicode(escapeUnicode).build()); + assertEquals("\"" + UTF8_GREETING + "\"", baos.toString()); + } +} diff --git a/src/test/java/com/jsoniter/suite/AllTestCases.java b/src/test/java/com/jsoniter/suite/AllTestCases.java index 5c71f7bd..d3196ed4 100644 --- a/src/test/java/com/jsoniter/suite/AllTestCases.java +++ b/src/test/java/com/jsoniter/suite/AllTestCases.java @@ -1,22 +1,63 @@ package com.jsoniter.suite; import com.jsoniter.*; -import com.jsoniter.TestArray; -import com.jsoniter.any.*; -import com.jsoniter.output.TestAny; -import com.jsoniter.output.TestCustomizeField; -import com.jsoniter.output.TestMap; -import com.jsoniter.output.TestNative; +import com.jsoniter.TestFloat; +import com.jsoniter.TestGenerics; +import com.jsoniter.TestGson; +import com.jsoniter.TestNested; +import com.jsoniter.TestObject; +import com.jsoniter.TestString; +import com.jsoniter.any.TestList; +import com.jsoniter.any.TestLong; +import com.jsoniter.output.*; +import com.jsoniter.output.TestInteger; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) -@Suite.SuiteClasses({TestAnnotation.class, TestArray.class, TestCustomizeType.class, TestDemo.class, - TestExisting.class, TestGenerics.class, TestGenerics.class, TestIO.class, TestNested.class, - TestObject.class, TestReadAny.class, TestReflection.class, TestSkip.class, TestSlice.class, - TestString.class, TestWhatIsNext.class, com.jsoniter.output.TestAnnotation.class, - TestAny.class, com.jsoniter.output.TestArray.class, TestCustomizeField.class, com.jsoniter.output.TestCustomizeType.class, - TestMap.class, TestNative.class, TestNested.class, TestObject.class, TestBoolean.class, TestFloat.class, - TestList.class, com.jsoniter.any.TestArray.class}) +@Suite.SuiteClasses({ + com.jsoniter.TestAnnotationJsonIgnore.class, + com.jsoniter.output.TestAnnotationJsonIgnore.class, + com.jsoniter.TestAnnotationJsonProperty.class, + com.jsoniter.output.TestAnnotationJsonProperty.class, + TestAnnotationJsonWrapper.class, + TestAnnotationJsonUnwrapper.class, + TestAnnotation.class, + TestAnnotationJsonCreator.class, + com.jsoniter.output.TestGenerics.class, + TestCustomizeType.class, TestDemo.class, + TestExisting.class, TestGenerics.class, TestGenerics.class, TestIO.class, + TestNested.class, + com.jsoniter.output.TestNested.class, + TestObject.class, + com.jsoniter.output.TestObject.class, + TestReadAny.class, TestSkip.class, TestSlice.class, + TestString.class, + com.jsoniter.output.TestString.class, + TestWhatIsNext.class, + TestAny.class, + com.jsoniter.output.TestArray.class, + com.jsoniter.any.TestArray.class, + com.jsoniter.TestArray.class, + TestSpiPropertyEncoder.class, + com.jsoniter.TestMap.class, + com.jsoniter.output.TestMap.class, + TestNative.class, + TestBoolean.class, TestFloat.class, com.jsoniter.output.TestFloat.class, + TestList.class, TestInteger.class, com.jsoniter.output.TestInteger.class, + com.jsoniter.output.TestJackson.class, + com.jsoniter.TestJackson.class, + TestSpiTypeEncoder.class, + TestSpiTypeDecoder.class, + TestSpiPropertyDecoder.class, + TestGson.class, + com.jsoniter.output.TestGson.class, + TestStreamBuffer.class, + IterImplForStreamingTest.class, + TestCollection.class, + TestList.class, + TestAnnotationJsonObject.class, + TestLong.class, + TestOmitValue.class}) public abstract class AllTestCases { } diff --git a/src/test/java/com/jsoniter/suite/ExtraTests.java b/src/test/java/com/jsoniter/suite/ExtraTests.java index eed220d1..79b9f70d 100644 --- a/src/test/java/com/jsoniter/suite/ExtraTests.java +++ b/src/test/java/com/jsoniter/suite/ExtraTests.java @@ -5,7 +5,7 @@ import org.junit.runners.Suite; @RunWith(Suite.class) -@Suite.SuiteClasses({TestBase64.class, TestJdkDatetime.class, TestNamingStrategy.class, TestPreciseFloat.class}) +@Suite.SuiteClasses({TestBase64.class, TestNamingStrategy.class, TestPreciseFloat.class}) public class ExtraTests { } diff --git a/src/test/java/com/jsoniter/suite/NonStreamingTests4Hash.java b/src/test/java/com/jsoniter/suite/NonStreamingTests4Hash.java index bc4eb269..6470f2c4 100644 --- a/src/test/java/com/jsoniter/suite/NonStreamingTests4Hash.java +++ b/src/test/java/com/jsoniter/suite/NonStreamingTests4Hash.java @@ -1,6 +1,6 @@ package com.jsoniter.suite; -import com.jsoniter.DecodingMode; +import com.jsoniter.spi.DecodingMode; import com.jsoniter.JsonIterator; import com.jsoniter.StreamingCategory; import com.jsoniter.output.EncodingMode; diff --git a/src/test/java/com/jsoniter/suite/NonStreamingTests4Strict.java b/src/test/java/com/jsoniter/suite/NonStreamingTests4Strict.java index d5e20ff4..8bfea647 100644 --- a/src/test/java/com/jsoniter/suite/NonStreamingTests4Strict.java +++ b/src/test/java/com/jsoniter/suite/NonStreamingTests4Strict.java @@ -1,6 +1,6 @@ package com.jsoniter.suite; -import com.jsoniter.DecodingMode; +import com.jsoniter.spi.DecodingMode; import com.jsoniter.JsonIterator; import com.jsoniter.StreamingCategory; import com.jsoniter.output.EncodingMode; diff --git a/src/test/tweets.json b/src/test/tweets.json new file mode 100644 index 00000000..9e07d4ac --- /dev/null +++ b/src/test/tweets.json @@ -0,0 +1,1802 @@ +[ + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 22:33:07 +0000 2011", + "truncated": true, + "retweeted_status": { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 22:31:38 +0000 2011", + "truncated": false, + "id_str": "60833028892667904", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "Watch the @NHL's newest History Will Be Made video featuring last night's incredible comeback by the #Sharks http:\/\/bit.ly\/i86L60 #SJSLAK", + "id": 60833028892667904, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "San Jose Sharks", + "profile_sidebar_border_color": "f3901d", + "profile_background_tile": false, + "profile_sidebar_fill_color": "000000", + "created_at": "Tue Mar 31 20:57:57 +0000 2009", + "location": "San Jose, CA", + "profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/1014299634\/twitter9_normal.jpg", + "follow_request_sent": false, + "profile_link_color": "f3901d", + "is_translator": false, + "id_str": "27961547", + "favourites_count": 23, + "contributors_enabled": true, + "url": "http:\/\/SJSHARKS.com", + "default_profile": false, + "utc_offset": -28800, + "id": 27961547, + "profile_use_background_image": true, + "listed_count": 1732, + "lang": "en", + "protected": false, + "followers_count": 29603, + "profile_text_color": "00788b", + "profile_background_color": "00788b", + "time_zone": "Pacific Time (US & Canada)", + "description": "The Official Twitter Page of the San Jose Sharks.", + "notifications": false, + "geo_enabled": false, + "verified": true, + "profile_background_image_url": "http:\/\/a3.twimg.com\/profile_background_images\/32524835\/twitter7.jpg", + "default_profile_image": false, + "friends_count": 78, + "statuses_count": 1699, + "screen_name": "SanJoseSharks", + "following": false, + "show_all_inline_media": false + }, + "source": "\u003Ca href=\"http:\/\/www.tweetdeck.com\" rel=\"nofollow\"\u003ETweetDeck\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + "id_str": "60833399132258306", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "RT @SanJoseSharks: Watch the @NHL's newest History Will Be Made video featuring last night's incredible comeback by the #Sharks http:\/\/b ...", + "id": 60833399132258306, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "brooke", + "profile_sidebar_border_color": "000000", + "profile_background_tile": false, + "profile_sidebar_fill_color": "F5C7C9", + "created_at": "Mon Mar 24 17:16:26 +0000 2008", + "location": "california", + "profile_image_url": "http:\/\/a1.twimg.com\/profile_images\/58567276\/2755186893_c7b31b651b_b_normal.jpg", + "follow_request_sent": false, + "profile_link_color": "777777", + "is_translator": false, + "id_str": "14208894", + "favourites_count": 4, + "contributors_enabled": false, + "url": null, + "default_profile": false, + "utc_offset": -28800, + "id": 14208894, + "profile_use_background_image": true, + "listed_count": 3, + "lang": "en", + "protected": false, + "followers_count": 97, + "profile_text_color": "000000", + "profile_background_color": "000000", + "time_zone": "Pacific Time (US & Canada)", + "description": "i'm neat", + "notifications": false, + "geo_enabled": false, + "verified": false, + "profile_background_image_url": "http:\/\/a0.twimg.com\/profile_background_images\/66730001\/ahflowerscribble.br.jpg", + "default_profile_image": false, + "friends_count": 96, + "statuses_count": 3319, + "screen_name": "b2therooke", + "following": true, + "show_all_inline_media": false + }, + "source": "\u003Ca href=\"http:\/\/mobile.twitter.com\" rel=\"nofollow\"\u003ETwitter for Android\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 22:25:45 +0000 2011", + "truncated": true, + "retweeted_status": { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 21:15:13 +0000 2011", + "truncated": false, + "id_str": "60813797417422848", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "Just added support for file sharing regions upload of the same file in AHC. Mini bittorent supports like :-). In #jcloud soon. #ahc 1.6.4", + "id": 60813797417422848, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "jfarcand", + "profile_sidebar_border_color": "BDDCAD", + "profile_background_tile": false, + "profile_sidebar_fill_color": "DDFFCC", + "created_at": "Mon Dec 22 02:46:16 +0000 2008", + "location": "Pr\u00e9vost", + "profile_image_url": "http:\/\/a2.twimg.com\/profile_images\/1292846058\/pouet_normal.png", + "profile_link_color": "0084B4", + "is_translator": false, + "id_str": "18298703", + "follow_request_sent": false, + "contributors_enabled": false, + "favourites_count": 0, + "url": "http:\/\/jfarcand.wordpress.com\/", + "default_profile": false, + "utc_offset": -21600, + "id": 18298703, + "profile_use_background_image": true, + "listed_count": 56, + "lang": "en", + "protected": false, + "followers_count": 681, + "profile_text_color": "333333", + "profile_background_color": "9AE4E8", + "time_zone": "Central Time (US & Canada)", + "geo_enabled": false, + "description": "Objecteur de croissance, Open Source worker, etc ... I am the creator of @atmo_framework", + "notifications": false, + "verified": false, + "profile_background_image_url": "http:\/\/a2.twimg.com\/profile_background_images\/56304182\/IMG_0157.jpg", + "default_profile_image": false, + "statuses_count": 2018, + "friends_count": 98, + "screen_name": "jfarcand", + "show_all_inline_media": false, + "following": false + }, + "source": "\u003Ca href=\"http:\/\/twitter.com\" rel=\"nofollow\"\u003ETweetie for Mac\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + "id_str": "60831548257214464", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "RT @jfarcand: Just added support for file sharing regions upload of the same file in AHC. Mini bittorent supports like :-). In #jcloud s ...", + "id": 60831548257214464, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Stuart McCulloch", + "profile_sidebar_border_color": "C0DEED", + "profile_background_tile": false, + "profile_sidebar_fill_color": "DDEEF6", + "created_at": "Sun Sep 07 05:22:51 +0000 2008", + "location": "Kenilworth", + "profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/59578326\/hmm_normal.png", + "profile_link_color": "0084B4", + "is_translator": false, + "id_str": "16166414", + "follow_request_sent": false, + "contributors_enabled": false, + "favourites_count": 0, + "url": "http:\/\/mcculls.blogspot.com", + "default_profile": true, + "utc_offset": 0, + "id": 16166414, + "profile_use_background_image": true, + "listed_count": 16, + "lang": "en", + "protected": false, + "followers_count": 227, + "profile_text_color": "333333", + "profile_background_color": "C0DEED", + "time_zone": "London", + "geo_enabled": false, + "description": "Three computers, two cats, one adorable wife", + "notifications": false, + "verified": false, + "profile_background_image_url": "http:\/\/a3.twimg.com\/a\/1303316982\/images\/themes\/theme1\/bg.png", + "default_profile_image": false, + "statuses_count": 1923, + "friends_count": 76, + "screen_name": "mcculls", + "show_all_inline_media": false, + "following": true + }, + "source": "\u003Ca href=\"http:\/\/twitter.com\/\" rel=\"nofollow\"\u003ETwitter for iPhone\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 22:18:12 +0000 2011", + "truncated": false, + "retweeted_status": { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 14:20:13 +0000 2011", + "truncated": false, + "id_str": "60709359147171840", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "Girls believe in what they hear and boys in what they see, that's why girls wear make up and boys lie...", + "id": 60709359147171840, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Marina Orlova", + "profile_sidebar_border_color": "fff8ad", + "profile_background_tile": true, + "profile_sidebar_fill_color": "f6ffd1", + "created_at": "Wed Jun 25 04:44:45 +0000 2008", + "location": "Los Angeles", + "profile_image_url": "http:\/\/a3.twimg.com\/profile_images\/1286057133\/Marina0024MasterTIF_normal.jpg", + "follow_request_sent": false, + "profile_link_color": "0099CC", + "is_translator": false, + "id_str": "15227650", + "favourites_count": 2, + "contributors_enabled": false, + "url": "http:\/\/www.hotforwords.com", + "default_profile": false, + "utc_offset": -28800, + "id": 15227650, + "profile_use_background_image": true, + "listed_count": 1639, + "lang": "en", + "protected": false, + "followers_count": 38649, + "profile_text_color": "333333", + "profile_background_color": "FFF04D", + "time_zone": "Pacific Time (US & Canada)", + "description": "Philologist. Putting the LOL in PhiLOLogy :-)", + "notifications": false, + "geo_enabled": false, + "verified": false, + "profile_background_image_url": "http:\/\/a3.twimg.com\/profile_background_images\/199908976\/_MG_8441-1.JPG", + "default_profile_image": false, + "friends_count": 164, + "statuses_count": 7112, + "screen_name": "hotforwords", + "following": false, + "show_all_inline_media": true + }, + "source": "\u003Ca href=\"http:\/\/twitter.com\/\" rel=\"nofollow\"\u003ETwitter for iPhone\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + "id_str": "60829646584938496", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "RT @hotforwords: Girls believe in what they hear and boys in what they see, that's why girls wear make up and boys lie...", + "id": 60829646584938496, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Graham Cale", + "profile_sidebar_border_color": "94a4ae", + "profile_background_tile": false, + "profile_sidebar_fill_color": "1d110a", + "created_at": "Thu Nov 25 21:29:04 +0000 2010", + "location": "Kitchener, ON", + "profile_image_url": "http:\/\/a1.twimg.com\/profile_images\/1181276241\/image_normal.jpg", + "follow_request_sent": false, + "profile_link_color": "94a4ae", + "is_translator": false, + "id_str": "219781561", + "favourites_count": 0, + "contributors_enabled": false, + "url": "http:\/\/www.google.com\/reader\/shared\/graham.cale", + "default_profile": false, + "utc_offset": -21600, + "id": 219781561, + "profile_use_background_image": true, + "listed_count": 0, + "lang": "en", + "protected": false, + "followers_count": 14, + "profile_text_color": "a98e6f", + "profile_background_color": "251810", + "time_zone": "Central Time (US & Canada)", + "description": "news junkie.", + "notifications": false, + "geo_enabled": false, + "verified": false, + "profile_background_image_url": "http:\/\/a0.twimg.com\/profile_background_images\/178310231\/x50baa944daf25caf24f2a2ff5a19f59.jpg", + "default_profile_image": false, + "friends_count": 49, + "statuses_count": 128, + "screen_name": "grahamcale", + "following": true, + "show_all_inline_media": false + }, + "source": "\u003Ca href=\"http:\/\/mobile.twitter.com\" rel=\"nofollow\"\u003ETwitter for Android\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 22:17:15 +0000 2011", + "truncated": true, + "retweeted_status": { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 19:49:18 +0000 2011", + "truncated": false, + "id_str": "60792172630380544", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "We just played wedraw.tv, great fun, iPhone, iPad, Androids & GoogleTV all playing together. TV social gaming win. Great job @thinkmovl", + "id": 60792172630380544, + "retweet_count": 1, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Richard Leggett", + "profile_sidebar_border_color": "eeeeee", + "profile_background_tile": true, + "profile_sidebar_fill_color": "efefef", + "created_at": "Fri Feb 13 09:30:10 +0000 2009", + "location": "Milton Keynes", + "profile_image_url": "http:\/\/a3.twimg.com\/profile_images\/1167979493\/concretecowboy_icon_normal.jpg", + "profile_link_color": "009999", + "is_translator": false, + "follow_request_sent": false, + "id_str": "20758989", + "favourites_count": 11, + "url": "http:\/\/www.richardleggett.co.uk", + "contributors_enabled": false, + "default_profile": false, + "utc_offset": 0, + "id": 20758989, + "profile_use_background_image": true, + "listed_count": 86, + "lang": "en", + "protected": false, + "followers_count": 879, + "profile_text_color": "333333", + "profile_background_color": "131516", + "time_zone": "London", + "geo_enabled": false, + "description": "Founder of Valis Interactive. Develops Mobile Android\/Win Phone 7\/iOS, Flash, Flex, AIR.", + "notifications": false, + "verified": false, + "profile_background_image_url": "http:\/\/a2.twimg.com\/a\/1302888170\/images\/themes\/theme14\/bg.gif", + "default_profile_image": false, + "statuses_count": 7427, + "friends_count": 424, + "screen_name": "richardleggett", + "show_all_inline_media": true, + "following": false + }, + "source": "\u003Ca href=\"http:\/\/www.tweetdeck.com\" rel=\"nofollow\"\u003ETweetDeck\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + "id_str": "60829408910520320", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "RT @richardleggett: We just played wedraw.tv, great fun, iPhone, iPad, Androids & GoogleTV all playing together. TV social gaming win. G ...", + "id": 60829408910520320, + "retweet_count": 1, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Charlie Collins", + "profile_sidebar_border_color": "BDDCAD", + "profile_background_tile": false, + "profile_sidebar_fill_color": "DDFFCC", + "created_at": "Mon Jan 12 16:01:37 +0000 2009", + "location": "Atlanta, GA, US", + "profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/599143959\/ch5_normal.jpg", + "profile_link_color": "0084B4", + "is_translator": false, + "follow_request_sent": false, + "id_str": "18904477", + "favourites_count": 7, + "url": "http:\/\/www.google.com\/profiles\/charlie.collins", + "contributors_enabled": false, + "default_profile": false, + "utc_offset": -18000, + "id": 18904477, + "profile_use_background_image": true, + "listed_count": 26, + "lang": "en", + "protected": false, + "followers_count": 250, + "profile_text_color": "333333", + "profile_background_color": "9AE4E8", + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "description": "Father, husband, code grunt, author.", + "notifications": false, + "verified": false, + "profile_background_image_url": "http:\/\/a1.twimg.com\/profile_background_images\/5453734\/orange_wall_cut.jpg", + "default_profile_image": false, + "statuses_count": 2748, + "friends_count": 167, + "screen_name": "CharlieCollins", + "show_all_inline_media": false, + "following": false + }, + "source": "\u003Ca href=\"http:\/\/seesmic.com\/app\" rel=\"nofollow\"\u003ESeesmic Web\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 22:13:24 +0000 2011", + "truncated": false, + "id_str": "60828439833350144", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "Saskboy Rips Down This Wall: http:\/\/t.co\/cPeDaB9 - Brad Wall unfairly attacks @M_Ignatieff, so I fairly rip Wall. #skpoli #cdnpoli #elxn41", + "id": 60828439833350144, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Saskboy K.", + "profile_sidebar_border_color": "181A1E", + "profile_background_tile": false, + "profile_sidebar_fill_color": "252429", + "created_at": "Thu Jun 07 04:43:39 +0000 2007", + "location": "Saskatchewan, Canada", + "profile_image_url": "http:\/\/a1.twimg.com\/profile_images\/1174293129\/Img_6178riders_normal.jpg", + "is_translator": false, + "profile_link_color": "2FC2EF", + "follow_request_sent": false, + "id_str": "6634632", + "favourites_count": 957, + "default_profile": false, + "url": "http:\/\/www.abandonedstuff.com", + "contributors_enabled": false, + "utc_offset": -21600, + "id": 6634632, + "profile_use_background_image": true, + "listed_count": 58, + "lang": "en", + "protected": false, + "followers_count": 858, + "profile_text_color": "666666", + "profile_background_color": "1A1B1F", + "time_zone": "Saskatchewan", + "description": "A Saskatchewan Blogger living in Regina", + "notifications": false, + "geo_enabled": false, + "verified": false, + "profile_background_image_url": "http:\/\/a2.twimg.com\/profile_background_images\/10281422\/twitrgrid.JPG", + "friends_count": 580, + "statuses_count": 5715, + "default_profile_image": false, + "screen_name": "saskboy", + "following": true, + "show_all_inline_media": false + }, + "source": "\u003Ca href=\"http:\/\/twitter.com\/tweetbutton\" rel=\"nofollow\"\u003ETweet Button\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 22:02:52 +0000 2011", + "truncated": true, + "retweeted_status": { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 21:55:37 +0000 2011", + "truncated": false, + "id_str": "60823963495956480", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "$3.99 on @AmazonMP3! @BombaEstereo @DJAfro Los @AmgsInvisibles @Monareta @Nortec_Fussible @ThePinkerTones http:\/\/amzn.to\/e6GWZG", + "id": 60823963495956480, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Nacional Records", + "profile_sidebar_border_color": "bfbfbf", + "profile_background_tile": true, + "profile_sidebar_fill_color": "c9c9c9", + "created_at": "Wed Apr 15 23:29:11 +0000 2009", + "location": "North Hollywood, CA", + "profile_image_url": "http:\/\/a3.twimg.com\/profile_images\/1081992285\/iTunes-essentials3_normal.jpg", + "is_translator": false, + "follow_request_sent": false, + "profile_link_color": "c34242", + "id_str": "31560203", + "favourites_count": 1, + "contributors_enabled": true, + "default_profile": false, + "url": "http:\/\/www.nacionalrecords.com\/", + "utc_offset": -28800, + "id": 31560203, + "profile_use_background_image": true, + "listed_count": 329, + "lang": "en", + "protected": false, + "followers_count": 8899, + "profile_text_color": "1c1f23", + "profile_background_color": "07090b", + "time_zone": "Pacific Time (US & Canada)", + "geo_enabled": false, + "description": "Manu Chao, Fabulosos Cadillacs, Nortec Collective, Aterciopelados, Amigos Invisibles, Mexican Institute of Sound, Bomba Estereo, Ana Tijoux, Pacha Massive y mas", + "notifications": false, + "verified": true, + "profile_background_image_url": "http:\/\/a1.twimg.com\/profile_background_images\/124317630\/x044fab92d37ca58eac62f5d9a8db1a1.png", + "statuses_count": 7366, + "default_profile_image": false, + "friends_count": 2579, + "screen_name": "NacionalRecords", + "following": false, + "show_all_inline_media": false + }, + "source": "web", + "in_reply_to_status_id": null + }, + "id_str": "60825788555079681", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "RT @NacionalRecords: $3.99 on @AmazonMP3! @BombaEstereo @DJAfro Los @AmgsInvisibles @Monareta @Nortec_Fussible @ThePinkerTones http:\/\/am ...", + "id": 60825788555079681, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Amazon MP3", + "profile_sidebar_border_color": "99cc33", + "profile_background_tile": false, + "profile_sidebar_fill_color": "ebebeb", + "created_at": "Mon May 12 04:02:08 +0000 2008", + "location": "Seattle", + "profile_image_url": "http:\/\/a3.twimg.com\/profile_images\/340137940\/twitteravatar_normal.jpeg", + "is_translator": false, + "follow_request_sent": false, + "profile_link_color": "669933", + "id_str": "14740219", + "favourites_count": 28, + "contributors_enabled": true, + "default_profile": false, + "url": "http:\/\/www.amazonmp3.com", + "utc_offset": -28800, + "id": 14740219, + "profile_use_background_image": true, + "listed_count": 8251, + "lang": "en", + "protected": false, + "followers_count": 1539675, + "profile_text_color": "000000", + "profile_background_color": "000000", + "time_zone": "Pacific Time (US & Canada)", + "geo_enabled": false, + "description": "Daily Deals and special sales on DRM-free, play-anywhere music downloads from Amazon. The official Amazon MP3 twitter feed.", + "notifications": false, + "verified": true, + "profile_background_image_url": "http:\/\/a3.twimg.com\/profile_background_images\/53396456\/bg_w_url.gif", + "statuses_count": 3433, + "default_profile_image": false, + "friends_count": 698, + "screen_name": "amazonmp3", + "following": true, + "show_all_inline_media": false + }, + "source": "\u003Ca href=\"http:\/\/www.tweetdeck.com\" rel=\"nofollow\"\u003ETweetDeck\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 22:02:02 +0000 2011", + "truncated": false, + "retweeted_status": { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 21:18:23 +0000 2011", + "truncated": false, + "id_str": "60814594846887936", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "Only 5 days left to apply for our Start Up, Boot Up competition! Nice write up today on it - http:\/\/t.co\/tHFTXZg #contegix", + "id": 60814594846887936, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Matthew E. Porter", + "profile_sidebar_border_color": "C0DEED", + "profile_background_tile": false, + "profile_sidebar_fill_color": "DDEEF6", + "created_at": "Fri Nov 30 16:15:23 +0000 2007", + "location": "Saint Louis, MO", + "profile_image_url": "http:\/\/a3.twimg.com\/profile_images\/1242839739\/161641_664286491_2805878_n_normal.jpg", + "profile_link_color": "0084B4", + "is_translator": false, + "follow_request_sent": false, + "id_str": "10742502", + "contributors_enabled": false, + "favourites_count": 14, + "url": "http:\/\/www.porterhome.com\/blog\/matthew", + "default_profile": true, + "utc_offset": -21600, + "id": 10742502, + "profile_use_background_image": true, + "listed_count": 45, + "lang": "en", + "protected": false, + "followers_count": 694, + "profile_text_color": "333333", + "profile_background_color": "C0DEED", + "geo_enabled": false, + "time_zone": "Central Time (US & Canada)", + "description": "Father, geek, entrepreneur - in that order.", + "notifications": false, + "verified": false, + "profile_background_image_url": "http:\/\/a3.twimg.com\/a\/1302646548\/images\/themes\/theme1\/bg.png", + "default_profile_image": false, + "friends_count": 592, + "statuses_count": 4804, + "show_all_inline_media": false, + "screen_name": "meporter", + "following": true + }, + "source": "\u003Ca href=\"http:\/\/itunes.apple.com\/us\/app\/twitter\/id409789998?mt=12\" rel=\"nofollow\"\u003ETwitter for Mac\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + "id_str": "60825579515162624", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "RT @meporter: Only 5 days left to apply for our Start Up, Boot Up competition! Nice write up today on it - http:\/\/t.co\/tHFTXZg #contegix", + "id": 60825579515162624, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Alex Miller", + "profile_sidebar_border_color": "000000", + "profile_background_tile": true, + "profile_sidebar_fill_color": "b9e250", + "created_at": "Mon Apr 28 14:04:13 +0000 2008", + "location": "St. Louis", + "profile_image_url": "http:\/\/a3.twimg.com\/profile_images\/547362531\/nachos_normal.jpg", + "profile_link_color": "43556b", + "is_translator": false, + "follow_request_sent": false, + "id_str": "14569541", + "contributors_enabled": false, + "favourites_count": 13, + "url": "http:\/\/tech.puredanger.com", + "default_profile": false, + "utc_offset": -21600, + "id": 14569541, + "profile_use_background_image": true, + "listed_count": 378, + "lang": "en", + "protected": false, + "followers_count": 3977, + "profile_text_color": "000000", + "profile_background_color": "000000", + "geo_enabled": false, + "time_zone": "Central Time (US & Canada)", + "description": "Java, Clojure, JVM, concurrency, sem web, Strange Loop, Lambda Lounge, Revelytix, nachos, beer, music", + "notifications": false, + "verified": false, + "profile_background_image_url": "http:\/\/a1.twimg.com\/profile_background_images\/56421963\/DSC_0081.JPG", + "default_profile_image": false, + "friends_count": 4286, + "statuses_count": 7697, + "show_all_inline_media": true, + "screen_name": "puredanger", + "following": false + }, + "source": "\u003Ca href=\"http:\/\/www.nambu.com\/\" rel=\"nofollow\"\u003ENambu\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 22:01:45 +0000 2011", + "truncated": false, + "id_str": "60825505389219840", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "RT @strangeloop_stl Have I mentioned that Steve Yegge is coming to #strangeloop this year? 'cause he is.", + "id": 60825505389219840, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Alex Miller", + "profile_sidebar_border_color": "000000", + "profile_background_tile": true, + "profile_sidebar_fill_color": "b9e250", + "created_at": "Mon Apr 28 14:04:13 +0000 2008", + "location": "St. Louis", + "profile_image_url": "http:\/\/a3.twimg.com\/profile_images\/547362531\/nachos_normal.jpg", + "is_translator": false, + "profile_link_color": "43556b", + "follow_request_sent": false, + "id_str": "14569541", + "favourites_count": 13, + "default_profile": false, + "url": "http:\/\/tech.puredanger.com", + "contributors_enabled": false, + "utc_offset": -21600, + "id": 14569541, + "profile_use_background_image": true, + "listed_count": 378, + "lang": "en", + "protected": false, + "followers_count": 3977, + "profile_text_color": "000000", + "profile_background_color": "000000", + "time_zone": "Central Time (US & Canada)", + "description": "Java, Clojure, JVM, concurrency, sem web, Strange Loop, Lambda Lounge, Revelytix, nachos, beer, music", + "notifications": false, + "geo_enabled": false, + "verified": false, + "profile_background_image_url": "http:\/\/a1.twimg.com\/profile_background_images\/56421963\/DSC_0081.JPG", + "friends_count": 4286, + "statuses_count": 7697, + "default_profile_image": false, + "screen_name": "puredanger", + "following": true, + "show_all_inline_media": true + }, + "source": "\u003Ca href=\"http:\/\/www.nambu.com\/\" rel=\"nofollow\"\u003ENambu\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 21:48:21 +0000 2011", + "truncated": false, + "id_str": "60822136159350784", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "I and @stuartsierra will be teaching @pragstudio #clojure June 22-24 http:\/\/bit.ly\/dgMFHJ", + "id": 60822136159350784, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "stuarthalloway", + "profile_sidebar_border_color": "C0DEED", + "profile_background_tile": false, + "profile_sidebar_fill_color": "DDEEF6", + "created_at": "Thu Mar 20 14:38:29 +0000 2008", + "location": "Chapel Hill, NC, USA", + "profile_image_url": "http:\/\/a2.twimg.com\/profile_images\/51921564\/stu-small_normal.png", + "is_translator": false, + "follow_request_sent": false, + "profile_link_color": "0084B4", + "id_str": "14184390", + "favourites_count": 0, + "contributors_enabled": false, + "default_profile": true, + "url": "http:\/\/thinkrelevance.com", + "utc_offset": -18000, + "id": 14184390, + "listed_count": 308, + "profile_use_background_image": true, + "lang": "en", + "protected": false, + "followers_count": 2710, + "profile_text_color": "333333", + "profile_background_color": "C0DEED", + "geo_enabled": false, + "time_zone": "Quito", + "description": "husband\/father\/coder\/runner", + "notifications": false, + "verified": false, + "profile_background_image_url": "http:\/\/a3.twimg.com\/a\/1302724321\/images\/themes\/theme1\/bg.png", + "statuses_count": 628, + "friends_count": 276, + "default_profile_image": false, + "show_all_inline_media": false, + "screen_name": "stuarthalloway", + "following": true + }, + "source": "\u003Ca href=\"http:\/\/www.socialoomph.com\" rel=\"nofollow\"\u003ESocialOomph\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 21:43:51 +0000 2011", + "truncated": true, + "retweeted_status": { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 21:38:11 +0000 2011", + "truncated": false, + "id_str": "60819576446926848", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "I say on July 1st we all organize a #democracymob across the country so they all know we won't go away after May #elxn41 are you with me?", + "id": 60819576446926848, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Mark McCaw", + "profile_sidebar_border_color": "adf1fc", + "profile_background_tile": false, + "profile_sidebar_fill_color": "000000", + "created_at": "Mon Jan 17 13:50:36 +0000 2011", + "location": "Moncton, NB Canada", + "profile_image_url": "http:\/\/a1.twimg.com\/profile_images\/1219569613\/Mark_At_Niagara_normal.jpg", + "is_translator": false, + "follow_request_sent": false, + "profile_link_color": "fa8459", + "id_str": "239378797", + "favourites_count": 1, + "default_profile": false, + "contributors_enabled": false, + "url": null, + "utc_offset": -14400, + "id": 239378797, + "profile_use_background_image": true, + "listed_count": 8, + "lang": "en", + "protected": false, + "followers_count": 197, + "profile_text_color": "947974", + "profile_background_color": "030103", + "time_zone": "Atlantic Time (Canada)", + "geo_enabled": true, + "description": "Learning junkie, political junkie, hockey junkie but not a junkie junkie.", + "notifications": false, + "verified": false, + "friends_count": 64, + "profile_background_image_url": "http:\/\/a0.twimg.com\/profile_background_images\/213468121\/x975ab12b9c65eb76ce02ca3194b75c7.jpg", + "statuses_count": 3349, + "default_profile_image": false, + "screen_name": "bigpicguy", + "following": false, + "show_all_inline_media": false + }, + "source": "web", + "in_reply_to_status_id": null + }, + "id_str": "60821003848261633", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "RT @bigpicguy: I say on July 1st we all organize a #democracymob across the country so they all know we won't go away after May #elxn41 ...", + "id": 60821003848261633, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Saskboy K.", + "profile_sidebar_border_color": "181A1E", + "profile_background_tile": false, + "profile_sidebar_fill_color": "252429", + "created_at": "Thu Jun 07 04:43:39 +0000 2007", + "location": "Saskatchewan, Canada", + "profile_image_url": "http:\/\/a1.twimg.com\/profile_images\/1174293129\/Img_6178riders_normal.jpg", + "is_translator": false, + "follow_request_sent": false, + "profile_link_color": "2FC2EF", + "id_str": "6634632", + "favourites_count": 957, + "default_profile": false, + "contributors_enabled": false, + "url": "http:\/\/www.abandonedstuff.com", + "utc_offset": -21600, + "id": 6634632, + "profile_use_background_image": true, + "listed_count": 57, + "lang": "en", + "protected": false, + "followers_count": 856, + "profile_text_color": "666666", + "profile_background_color": "1A1B1F", + "time_zone": "Saskatchewan", + "geo_enabled": false, + "description": "A Saskatchewan Blogger living in Regina", + "notifications": false, + "verified": false, + "friends_count": 580, + "profile_background_image_url": "http:\/\/a2.twimg.com\/profile_background_images\/10281422\/twitrgrid.JPG", + "statuses_count": 5709, + "default_profile_image": false, + "screen_name": "saskboy", + "following": true, + "show_all_inline_media": false + }, + "source": "web", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 21:41:50 +0000 2011", + "truncated": true, + "retweeted_status": { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 21:39:36 +0000 2011", + "truncated": false, + "id_str": "60819933671596032", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "Scary to see how little he knows! MT @MacleansMag SK Premier Brad Wall decides that the nation needs his constitut... http:\/\/bit.ly\/gTUmrS", + "id": 60819933671596032, + "retweet_count": 2, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Tom Flemming", + "profile_sidebar_border_color": "659430", + "profile_background_tile": true, + "profile_sidebar_fill_color": "a5b0b3", + "created_at": "Tue Mar 17 14:49:36 +0000 2009", + "location": "Hamilton, ON", + "profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/1288479752\/tomflickr_normal.jpg", + "is_translator": false, + "follow_request_sent": false, + "profile_link_color": "9c8109", + "id_str": "24892031", + "favourites_count": 750, + "contributors_enabled": false, + "url": "http:\/\/www.diigo.com\/user\/tomflem", + "default_profile": false, + "utc_offset": -18000, + "id": 24892031, + "listed_count": 79, + "profile_use_background_image": true, + "lang": "en", + "protected": false, + "followers_count": 894, + "profile_text_color": "574410", + "profile_background_color": "a2dbab", + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "description": "Formerly a health sciences librarian at McMaster University (ON) and at Dalhousie University (NS) in Canada", + "notifications": false, + "verified": false, + "profile_background_image_url": "http:\/\/a1.twimg.com\/profile_background_images\/47634442\/stones.jpg", + "friends_count": 389, + "statuses_count": 21099, + "default_profile_image": false, + "screen_name": "tomflem", + "show_all_inline_media": false, + "following": true + }, + "source": "web", + "in_reply_to_status_id": null + }, + "id_str": "60820493967691776", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "RT @tomflem: Scary to see how little he knows! MT @MacleansMag SK Premier Brad Wall decides that the nation needs his constitut... http ...", + "id": 60820493967691776, + "retweet_count": 2, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Saskboy K.", + "profile_sidebar_border_color": "181A1E", + "profile_background_tile": false, + "profile_sidebar_fill_color": "252429", + "created_at": "Thu Jun 07 04:43:39 +0000 2007", + "location": "Saskatchewan, Canada", + "profile_image_url": "http:\/\/a1.twimg.com\/profile_images\/1174293129\/Img_6178riders_normal.jpg", + "is_translator": false, + "follow_request_sent": false, + "profile_link_color": "2FC2EF", + "id_str": "6634632", + "favourites_count": 955, + "contributors_enabled": false, + "url": "http:\/\/www.abandonedstuff.com", + "default_profile": false, + "utc_offset": -21600, + "id": 6634632, + "listed_count": 57, + "profile_use_background_image": true, + "lang": "en", + "protected": false, + "followers_count": 857, + "profile_text_color": "666666", + "profile_background_color": "1A1B1F", + "time_zone": "Saskatchewan", + "geo_enabled": false, + "description": "A Saskatchewan Blogger living in Regina", + "notifications": false, + "verified": false, + "profile_background_image_url": "http:\/\/a2.twimg.com\/profile_background_images\/10281422\/twitrgrid.JPG", + "friends_count": 580, + "statuses_count": 5707, + "default_profile_image": false, + "screen_name": "saskboy", + "show_all_inline_media": false, + "following": false + }, + "source": "web", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 21:41:30 +0000 2011", + "truncated": true, + "retweeted_status": { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 21:39:56 +0000 2011", + "truncated": false, + "id_str": "60820015481499648", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "RT @bigpicguy: @CBC just said the government was brought down bec opposition didn't like budget? HELLO CONTEMPT #elxn41 #youthvote...", + "id": 60820015481499648, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "#CPC can't hv my nam", + "profile_sidebar_border_color": "9fa621", + "profile_background_tile": false, + "profile_sidebar_fill_color": "481802", + "created_at": "Wed Dec 30 21:15:44 +0000 2009", + "location": "Canada", + "profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/1305144508\/DSCF0258_normal.JPG", + "profile_link_color": "ad5c34", + "is_translator": false, + "follow_request_sent": false, + "id_str": "100597465", + "contributors_enabled": false, + "default_profile": false, + "favourites_count": 5, + "url": null, + "utc_offset": -18000, + "id": 100597465, + "profile_use_background_image": true, + "listed_count": 20, + "lang": "en", + "protected": false, + "followers_count": 311, + "profile_text_color": "828282", + "profile_background_color": "000000", + "geo_enabled": false, + "time_zone": "Quito", + "description": "Fair-minded curious painter, news junkie, S**t Disturber. #tahrir supporter. Comet was my first dog. ", + "notifications": false, + "verified": false, + "profile_background_image_url": "http:\/\/a1.twimg.com\/profile_background_images\/63007779\/nvchocolate.br.jpg", + "default_profile_image": false, + "friends_count": 118, + "statuses_count": 7460, + "show_all_inline_media": false, + "screen_name": "CometsMum", + "following": false + }, + "source": "\u003Ca href=\"http:\/\/www.hootsuite.com\" rel=\"nofollow\"\u003EHootSuite\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + "id_str": "60820409276309504", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "RT @CometsMum: RT @bigpicguy: @CBC just said the government was brought down bec opposition didn't like budget? HELLO CONTEMPT #elxn41 # ...", + "id": 60820409276309504, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Saskboy K.", + "profile_sidebar_border_color": "181A1E", + "profile_background_tile": false, + "profile_sidebar_fill_color": "252429", + "created_at": "Thu Jun 07 04:43:39 +0000 2007", + "location": "Saskatchewan, Canada", + "profile_image_url": "http:\/\/a1.twimg.com\/profile_images\/1174293129\/Img_6178riders_normal.jpg", + "profile_link_color": "2FC2EF", + "is_translator": false, + "follow_request_sent": false, + "id_str": "6634632", + "contributors_enabled": false, + "default_profile": false, + "favourites_count": 955, + "url": "http:\/\/www.abandonedstuff.com", + "utc_offset": -21600, + "id": 6634632, + "profile_use_background_image": true, + "listed_count": 57, + "lang": "en", + "protected": false, + "followers_count": 857, + "profile_text_color": "666666", + "profile_background_color": "1A1B1F", + "geo_enabled": false, + "time_zone": "Saskatchewan", + "description": "A Saskatchewan Blogger living in Regina", + "notifications": false, + "verified": false, + "profile_background_image_url": "http:\/\/a2.twimg.com\/profile_background_images\/10281422\/twitrgrid.JPG", + "default_profile_image": false, + "friends_count": 580, + "statuses_count": 5707, + "show_all_inline_media": false, + "screen_name": "saskboy", + "following": true + }, + "source": "web", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 21:29:31 +0000 2011", + "truncated": false, + "id_str": "60817395828277248", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "You're An Animal, Radio Collared: http:\/\/t.co\/sDalUAo iOS4 tracking file on your iPhone gives your secrets away.", + "id": 60817395828277248, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Saskboy K.", + "profile_sidebar_border_color": "181A1E", + "profile_background_tile": false, + "profile_sidebar_fill_color": "252429", + "created_at": "Thu Jun 07 04:43:39 +0000 2007", + "location": "Saskatchewan, Canada", + "profile_image_url": "http:\/\/a1.twimg.com\/profile_images\/1174293129\/Img_6178riders_normal.jpg", + "is_translator": false, + "follow_request_sent": false, + "profile_link_color": "2FC2EF", + "id_str": "6634632", + "favourites_count": 955, + "contributors_enabled": false, + "default_profile": false, + "url": "http:\/\/www.abandonedstuff.com", + "utc_offset": -21600, + "id": 6634632, + "listed_count": 57, + "profile_use_background_image": true, + "lang": "en", + "protected": false, + "followers_count": 857, + "profile_text_color": "666666", + "profile_background_color": "1A1B1F", + "geo_enabled": false, + "time_zone": "Saskatchewan", + "description": "A Saskatchewan Blogger living in Regina", + "notifications": false, + "verified": false, + "profile_background_image_url": "http:\/\/a2.twimg.com\/profile_background_images\/10281422\/twitrgrid.JPG", + "statuses_count": 5705, + "friends_count": 580, + "default_profile_image": false, + "show_all_inline_media": false, + "screen_name": "saskboy", + "following": true + }, + "source": "\u003Ca href=\"http:\/\/twitter.com\/tweetbutton\" rel=\"nofollow\"\u003ETweet Button\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 21:27:40 +0000 2011", + "truncated": false, + "id_str": "60816929274867712", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "Watch now: http:\/\/ndp.ca\/hhuNZ Join me online for an #NDP Town Hall live from Thunder Bay. Be a part of it. #Cdnpoli #elxn41", + "id": 60816929274867712, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Jack Layton", + "profile_sidebar_border_color": "FF6600", + "profile_background_tile": false, + "profile_sidebar_fill_color": "FFFFFF", + "created_at": "Tue Jul 22 04:44:38 +0000 2008", + "location": "", + "profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/1291903917\/Jack2_normal.jpg", + "follow_request_sent": null, + "profile_link_color": "FF6600", + "is_translator": false, + "id_str": "15526563", + "favourites_count": 0, + "contributors_enabled": false, + "default_profile": false, + "url": "http:\/\/www.ndp.ca", + "utc_offset": -18000, + "id": 15526563, + "profile_use_background_image": true, + "listed_count": 2673, + "lang": "en", + "protected": false, + "followers_count": 84617, + "profile_text_color": "000000", + "profile_background_color": "505052", + "time_zone": "Eastern Time (US & Canada)", + "description": "Leader, Canada's New Democrats.", + "notifications": null, + "geo_enabled": false, + "verified": false, + "profile_background_image_url": "http:\/\/a1.twimg.com\/profile_background_images\/224700669\/English.jpg", + "default_profile_image": false, + "friends_count": 11387, + "statuses_count": 892, + "screen_name": "jacklayton", + "following": null, + "show_all_inline_media": false + }, + "source": "\u003Ca href=\"http:\/\/www.hootsuite.com\" rel=\"nofollow\"\u003EHootSuite\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 21:14:50 +0000 2011", + "truncated": false, + "id_str": "60813699442675712", + "in_reply_to_user_id_str": "14738204", + "contributors": null, + "text": "@cbeust @tirsen SB Options is Guicified, supports .properties files and has a slightly neater API in my opinion.", + "id": 60813699442675712, + "retweet_count": 0, + "in_reply_to_status_id_str": "60723367321419776", + "geo": null, + "retweeted": false, + "in_reply_to_user_id": 14738204, + "in_reply_to_screen_name": "cbeust", + "place": null, + "user": { + "name": "Dhanji R. Prasanna", + "profile_sidebar_border_color": "eeeeee", + "profile_background_tile": true, + "profile_sidebar_fill_color": "efefef", + "created_at": "Mon Apr 28 01:03:24 +0000 2008", + "location": "Sydney, Australia", + "profile_image_url": "http:\/\/a3.twimg.com\/profile_images\/53414948\/dj_sp_normal.jpg", + "follow_request_sent": false, + "profile_link_color": "009999", + "is_translator": false, + "id_str": "14563623", + "favourites_count": 65, + "contributors_enabled": false, + "default_profile": false, + "url": "http:\/\/www.wideplay.com", + "utc_offset": 36000, + "id": 14563623, + "profile_use_background_image": true, + "listed_count": 141, + "lang": "en", + "protected": false, + "followers_count": 1271, + "profile_text_color": "333333", + "profile_background_color": "131516", + "time_zone": "Sydney", + "geo_enabled": true, + "description": "Senior Custodial Engineer at Google", + "notifications": true, + "verified": false, + "profile_background_image_url": "http:\/\/a2.twimg.com\/a\/1302724321\/images\/themes\/theme14\/bg.gif", + "default_profile_image": false, + "statuses_count": 6326, + "friends_count": 179, + "screen_name": "dhanji", + "following": true, + "show_all_inline_media": false + }, + "source": "\u003Ca href=\"http:\/\/itunes.apple.com\/us\/app\/twitter\/id409789998?mt=12\" rel=\"nofollow\"\u003ETwitter for Mac\u003C\/a\u003E", + "in_reply_to_status_id": 60723367321419776 + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 21:13:34 +0000 2011", + "truncated": false, + "id_str": "60813378960105473", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "Gas Fracking\/Drilling Emergency in Bradford County - WNEP http:\/\/t.co\/7jEqBA5 Energy company destroying fresh water supply for locals", + "id": 60813378960105473, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Saskboy K.", + "profile_sidebar_border_color": "181A1E", + "profile_background_tile": false, + "profile_sidebar_fill_color": "252429", + "created_at": "Thu Jun 07 04:43:39 +0000 2007", + "location": "Saskatchewan, Canada", + "profile_image_url": "http:\/\/a1.twimg.com\/profile_images\/1174293129\/Img_6178riders_normal.jpg", + "follow_request_sent": false, + "profile_link_color": "2FC2EF", + "id_str": "6634632", + "is_translator": false, + "favourites_count": 955, + "contributors_enabled": false, + "default_profile": false, + "url": "http:\/\/www.abandonedstuff.com", + "utc_offset": -21600, + "id": 6634632, + "profile_use_background_image": true, + "listed_count": 57, + "lang": "en", + "protected": false, + "followers_count": 857, + "profile_text_color": "666666", + "profile_background_color": "1A1B1F", + "geo_enabled": false, + "time_zone": "Saskatchewan", + "description": "A Saskatchewan Blogger living in Regina", + "notifications": false, + "verified": false, + "profile_background_image_url": "http:\/\/a2.twimg.com\/profile_background_images\/10281422\/twitrgrid.JPG", + "default_profile_image": false, + "statuses_count": 5704, + "friends_count": 580, + "show_all_inline_media": false, + "screen_name": "saskboy", + "following": true + }, + "source": "\u003Ca href=\"http:\/\/twitter.com\/tweetbutton\" rel=\"nofollow\"\u003ETweet Button\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 21:04:43 +0000 2011", + "truncated": false, + "id_str": "60811152803897345", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "Hudson CI 2.0.0 release candidate http:\/\/bit.ly\/eqJlZF (link to 37mb war file) <- available for early testing and feedback", + "id": 60811152803897345, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Stuart McCulloch", + "profile_sidebar_border_color": "C0DEED", + "profile_background_tile": false, + "profile_sidebar_fill_color": "DDEEF6", + "created_at": "Sun Sep 07 05:22:51 +0000 2008", + "location": "Kenilworth", + "profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/59578326\/hmm_normal.png", + "profile_link_color": "0084B4", + "is_translator": false, + "follow_request_sent": false, + "id_str": "16166414", + "favourites_count": 0, + "contributors_enabled": false, + "default_profile": true, + "url": "http:\/\/mcculls.blogspot.com", + "utc_offset": 0, + "id": 16166414, + "profile_use_background_image": true, + "listed_count": 16, + "lang": "en", + "protected": false, + "followers_count": 227, + "profile_text_color": "333333", + "profile_background_color": "C0DEED", + "geo_enabled": false, + "time_zone": "London", + "description": "Three computers, two cats, one adorable wife", + "notifications": false, + "verified": false, + "profile_background_image_url": "http:\/\/a3.twimg.com\/a\/1303316982\/images\/themes\/theme1\/bg.png", + "default_profile_image": false, + "friends_count": 76, + "statuses_count": 1922, + "show_all_inline_media": false, + "screen_name": "mcculls", + "following": true + }, + "source": "web", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 21:02:04 +0000 2011", + "truncated": false, + "id_str": "60810484760322048", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "Sure are a lot of tablets coming", + "id": 60810484760322048, + "retweet_count": 0, + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Tim Bray", + "profile_sidebar_border_color": "87bc44", + "profile_background_tile": false, + "profile_sidebar_fill_color": "e0ff92", + "created_at": "Thu Mar 15 17:24:22 +0000 2007", + "location": "Vancouver", + "profile_image_url": "http:\/\/a3.twimg.com\/profile_images\/421637246\/Tim_normal.jpg", + "is_translator": false, + "follow_request_sent": false, + "profile_link_color": "AA0000", + "id_str": "1235521", + "favourites_count": 415, + "default_profile": false, + "contributors_enabled": false, + "url": "http:\/\/www.tbray.org\/ongoing\/", + "utc_offset": -28800, + "id": 1235521, + "profile_use_background_image": false, + "listed_count": 1634, + "lang": "en", + "protected": false, + "followers_count": 17072, + "profile_text_color": "000000", + "profile_background_color": "FFFFFF", + "time_zone": "Pacific Time (US & Canada)", + "description": "Web geek with a camera, currently doing Android stuff at Google.", + "notifications": false, + "geo_enabled": false, + "verified": false, + "friends_count": 669, + "profile_background_image_url": "http:\/\/a0.twimg.com\/profile_background_images\/1980852\/IMGP1686.jpg", + "default_profile_image": false, + "statuses_count": 8766, + "screen_name": "timbray", + "following": true, + "show_all_inline_media": false + }, + "source": "\u003Ca href=\"http:\/\/twitter.com\" rel=\"nofollow\"\u003ETweetie for Mac\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "truncated": true, + "created_at": "Wed Apr 20 20:53:32 +0000 2011", + "favorited": false, + "retweeted_status": { + "coordinates": null, + "truncated": false, + "created_at": "Wed Apr 20 14:23:18 +0000 2011", + "favorited": false, + "id_str": "60710135722549249", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "How plugins can support @hudsonci & @jenkinsci http:\/\/bit.ly\/haAe4L http:\/\/bit.ly\/heXINi thanks to @henriklynggaard for blogging this", + "id": 60710135722549249, + "in_reply_to_status_id_str": null, + "retweet_count": 2, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "place": null, + "user": { + "profile_sidebar_border_color": "C0DEED", + "name": "Hudson CI", + "profile_background_tile": true, + "profile_sidebar_fill_color": "DDEEF6", + "created_at": "Mon Jan 31 22:35:58 +0000 2011", + "location": "Everywhere!", + "profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/1231444178\/hudson-twitter_normal.png", + "profile_link_color": "0084B4", + "is_translator": false, + "id_str": "245535216", + "follow_request_sent": false, + "contributors_enabled": false, + "default_profile": false, + "url": "http:\/\/hudson-ci.org", + "favourites_count": 0, + "utc_offset": -28800, + "id": 245535216, + "listed_count": 18, + "profile_use_background_image": true, + "followers_count": 334, + "lang": "en", + "protected": false, + "profile_text_color": "333333", + "time_zone": "Pacific Time (US & Canada)", + "geo_enabled": false, + "verified": false, + "profile_background_color": "C0DEED", + "description": "", + "notifications": false, + "friends_count": 14, + "statuses_count": 39, + "profile_background_image_url": "http:\/\/a1.twimg.com\/profile_background_images\/200487835\/hudson-twitter-background-logo.png", + "default_profile_image": false, + "screen_name": "hudsonci", + "show_all_inline_media": false, + "following": false + }, + "source": "web", + "in_reply_to_screen_name": null, + "in_reply_to_status_id": null + }, + "id_str": "60808339977797632", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "RT @hudsonci: How plugins can support @hudsonci & @jenkinsci http:\/\/bit.ly\/haAe4L http:\/\/bit.ly\/heXINi thanks to @henriklynggaard for b ...", + "id": 60808339977797632, + "in_reply_to_status_id_str": null, + "retweet_count": 2, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "place": null, + "user": { + "profile_sidebar_border_color": "C0DEED", + "name": "Stuart McCulloch", + "profile_background_tile": false, + "profile_sidebar_fill_color": "DDEEF6", + "created_at": "Sun Sep 07 05:22:51 +0000 2008", + "location": "Kenilworth", + "profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/59578326\/hmm_normal.png", + "profile_link_color": "0084B4", + "is_translator": false, + "id_str": "16166414", + "follow_request_sent": false, + "contributors_enabled": false, + "default_profile": true, + "url": "http:\/\/mcculls.blogspot.com", + "favourites_count": 0, + "utc_offset": 0, + "id": 16166414, + "listed_count": 16, + "profile_use_background_image": true, + "followers_count": 227, + "lang": "en", + "protected": false, + "profile_text_color": "333333", + "time_zone": "London", + "geo_enabled": false, + "verified": false, + "profile_background_color": "C0DEED", + "description": "Three computers, two cats, one adorable wife", + "notifications": false, + "friends_count": 76, + "statuses_count": 1921, + "profile_background_image_url": "http:\/\/a3.twimg.com\/a\/1303316982\/images\/themes\/theme1\/bg.png", + "default_profile_image": false, + "screen_name": "mcculls", + "show_all_inline_media": false, + "following": false + }, + "source": "web", + "in_reply_to_screen_name": null, + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 20 20:52:15 +0000 2011", + "truncated": true, + "retweeted_status": { + "coordinates": null, + "favorited": false, + "created_at": "Wed Apr 06 18:47:01 +0000 2011", + "truncated": false, + "id_str": "55703069324873728", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "Everybody thinks \"The Social Network\" is the best movie about forming a new startup, but they are wrong. The best movie is \"Ghostbusters\".", + "id": 55703069324873728, + "retweet_count": "100+", + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Patrick Ewing", + "profile_sidebar_border_color": "a1b44f", + "profile_background_tile": false, + "profile_sidebar_fill_color": "a0b34a", + "expanded_url": "http:\/\/patrickewing.info", + "created_at": "Sat Feb 24 18:13:15 +0000 2007", + "location": "Sane Francisco", + "profile_image_url": "http:\/\/a2.twimg.com\/profile_images\/1268256803\/cropped_normal.jpg", + "follow_request_sent": false, + "profile_link_color": "61351f", + "id_str": "792690", + "is_translator": false, + "contributors_enabled": false, + "default_profile": false, + "favourites_count": 1481, + "url": "http:\/\/t.co\/QXois9Q", + "utc_offset": -28800, + "id": 792690, + "profile_use_background_image": true, + "listed_count": 350, + "lang": "en", + "protected": false, + "followers_count": 51875, + "profile_text_color": "29230d", + "profile_background_color": "b2be63", + "time_zone": "Pacific Time (US & Canada)", + "geo_enabled": true, + "description": "Vector of enthusiasm. Tech lead on Twitter's Web Client team. I follow the code of Ruby, JavaScript & http:\/\/t.co\/vTmgS7L", + "notifications": false, + "verified": false, + "profile_background_image_url": "http:\/\/a2.twimg.com\/profile_background_images\/113507697\/cheerfulchirp_36_12284.jpg", + "statuses_count": 3791, + "default_profile_image": false, + "display_url": "patrickewing.info", + "friends_count": 765, + "screen_name": "hoverbird", + "show_all_inline_media": true, + "following": false + }, + "source": "\u003Ca href=\"http:\/\/itunes.apple.com\/us\/app\/twitter\/id409789998?mt=12\" rel=\"nofollow\"\u003ETwitter for Mac\u003C\/a\u003E", + "in_reply_to_status_id": null + }, + "id_str": "60808017754603520", + "in_reply_to_user_id_str": null, + "contributors": null, + "text": "RT @hoverbird: Everybody thinks \"The Social Network\" is the best movie about forming a new startup, but they are wrong. The best movie i ...", + "id": 60808017754603520, + "retweet_count": "100+", + "in_reply_to_status_id_str": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "place": null, + "user": { + "name": "Sarah Tidy", + "profile_sidebar_border_color": "C0DEED", + "profile_background_tile": false, + "profile_sidebar_fill_color": "DDEEF6", + "created_at": "Sun Nov 15 15:54:32 +0000 2009", + "location": "", + "profile_image_url": "http:\/\/a3.twimg.com\/profile_images\/1165642248\/jQa13bf7_normal", + "follow_request_sent": false, + "profile_link_color": "0084B4", + "id_str": "90187149", + "is_translator": false, + "contributors_enabled": false, + "default_profile": true, + "favourites_count": 7, + "url": null, + "utc_offset": null, + "id": 90187149, + "profile_use_background_image": true, + "listed_count": 0, + "lang": "en", + "protected": false, + "followers_count": 33, + "profile_text_color": "333333", + "profile_background_color": "C0DEED", + "time_zone": null, + "geo_enabled": false, + "description": "", + "notifications": false, + "verified": false, + "profile_background_image_url": "http:\/\/a3.twimg.com\/a\/1302646548\/images\/themes\/theme1\/bg.png", + "statuses_count": 112, + "default_profile_image": false, + "friends_count": 80, + "screen_name": "bu77er7ar7", + "show_all_inline_media": false, + "following": false + }, + "source": "\u003Ca href=\"http:\/\/mobile.twitter.com\" rel=\"nofollow\"\u003ETwitter for Android\u003C\/a\u003E", + "in_reply_to_status_id": null + } +] \ No newline at end of file