Merge remote-tracking branch 'origin/master'
# Conflicts: # gson/src/main/java/com/google/gson/stream/JsonReader.java # gson/src/test/java/com/google/gson/functional/ReflectionAccessFilterTest.java # gson/src/test/java/com/google/gson/functional/ReflectionAccessTest.java # gson/src/test/java/com/google/gson/internal/bind/DefaultDateTypeAdapterTest.java # pom.xml
This commit is contained in:
commit
9409197165
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
@ -4,16 +4,20 @@ on: [push, pull_request]
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
name: "Build on JDK ${{ matrix.java }}"
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
java: [ 11, 17 ]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Set up JDK 11
|
- name: "Set up JDK ${{ matrix.java }}"
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: '11'
|
java-version: ${{ matrix.java }}
|
||||||
cache: 'maven'
|
cache: 'maven'
|
||||||
- name: Build with Maven
|
- name: Build with Maven
|
||||||
# This also runs javadoc:jar to detect any issues with the Javadoc generated during release
|
# This also runs javadoc:jar to detect any issues with the Javadoc generated during release
|
||||||
run: mvn --batch-mode --update-snapshots verify javadoc:jar
|
run: mvn --batch-mode --update-snapshots --no-transfer-progress verify javadoc:jar
|
||||||
|
@ -24,9 +24,9 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cd gson-old-japicmp
|
cd gson-old-japicmp
|
||||||
# Set dummy version
|
# Set dummy version
|
||||||
mvn --batch-mode org.codehaus.mojo:versions-maven-plugin:2.11.0:set -DnewVersion=JAPICMP-OLD
|
mvn --batch-mode --no-transfer-progress org.codehaus.mojo:versions-maven-plugin:2.11.0:set -DnewVersion=JAPICMP-OLD
|
||||||
# Install artifacts with dummy version in local repository; used later by Maven plugin for comparison
|
# Install artifacts with dummy version in local repository; used later by Maven plugin for comparison
|
||||||
mvn --batch-mode install -DskipTests
|
mvn --batch-mode --no-transfer-progress install -DskipTests
|
||||||
|
|
||||||
- name: Checkout new version
|
- name: Checkout new version
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@ -34,7 +34,7 @@ jobs:
|
|||||||
- name: Check API compatibility
|
- name: Check API compatibility
|
||||||
id: check-compatibility
|
id: check-compatibility
|
||||||
run: |
|
run: |
|
||||||
mvn --batch-mode --fail-at-end package japicmp:cmp -DskipTests
|
mvn --batch-mode --fail-at-end --no-transfer-progress package japicmp:cmp -DskipTests
|
||||||
|
|
||||||
- name: Upload API differences artifacts
|
- name: Upload API differences artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@ -48,7 +48,7 @@ jobs:
|
|||||||
# Can replace this with github/codeql-action/autobuild action to run complete build
|
# Can replace this with github/codeql-action/autobuild action to run complete build
|
||||||
- name: Compile sources
|
- name: Compile sources
|
||||||
run: |
|
run: |
|
||||||
mvn compile --batch-mode
|
mvn compile --batch-mode --no-transfer-progress
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v2
|
||||||
|
105
CHANGELOG.md
105
CHANGELOG.md
@ -1,17 +1,38 @@
|
|||||||
Change Log
|
Change Log
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
## Version 2.10
|
||||||
|
|
||||||
|
* Support for serializing and deserializing Java records, on Java ≥ 16. (https://github.com/google/gson/pull/2201)
|
||||||
|
* Add `JsonArray.asList` and `JsonObject.asMap` view methods (https://github.com/google/gson/pull/2225)
|
||||||
|
* Fix `TypeAdapterRuntimeTypeWrapper` not detecting reflective `TreeTypeAdapter` and `FutureTypeAdapter` (https://github.com/google/gson/pull/1787)
|
||||||
|
* Improve `JsonReader.skipValue()` (https://github.com/google/gson/pull/2062)
|
||||||
|
* Perform numeric conversion for primitive numeric type adapters (https://github.com/google/gson/pull/2158)
|
||||||
|
* Add `Gson.fromJson(..., TypeToken)` overloads (https://github.com/google/gson/pull/1700)
|
||||||
|
* Fix changes to `GsonBuilder` affecting existing `Gson` instances (https://github.com/google/gson/pull/1815)
|
||||||
|
* Make `JsonElement` conversion methods more consistent and fix javadoc (https://github.com/google/gson/pull/2178)
|
||||||
|
* Throw `UnsupportedOperationException` when `JsonWriter.jsonValue` is not supported (https://github.com/google/gson/pull/1651)
|
||||||
|
* Disallow `JsonObject` `Entry.setValue(null)` (https://github.com/google/gson/pull/2167)
|
||||||
|
* Fix `TypeAdapter.toJson` throwing AssertionError for custom IOException (https://github.com/google/gson/pull/2172)
|
||||||
|
* Convert null to JsonNull for `JsonArray.set` (https://github.com/google/gson/pull/2170)
|
||||||
|
* Fixed nullSafe usage. (https://github.com/google/gson/pull/1555)
|
||||||
|
* Validate `TypeToken.getParameterized` arguments (https://github.com/google/gson/pull/2166)
|
||||||
|
* Fix #1702: Gson.toJson creates CharSequence which does not implement toString (https://github.com/google/gson/pull/1703)
|
||||||
|
* Prefer existing adapter for concurrent `Gson.getAdapter` calls (https://github.com/google/gson/pull/2153)
|
||||||
|
* Improve `ArrayTypeAdapter` for `Object[]` (https://github.com/google/gson/pull/1716)
|
||||||
|
* Improve `AppendableWriter` performance (https://github.com/google/gson/pull/1706)
|
||||||
|
|
||||||
## Version 2.9.1
|
## Version 2.9.1
|
||||||
|
|
||||||
* Make `Object` and `JsonElement` deserialization iterative rather than
|
* Make `Object` and `JsonElement` deserialization iterative rather than
|
||||||
recursive (#1912)
|
recursive (https://github.com/google/gson/pull/1912)
|
||||||
* Added parsing support for enum that has overridden toString() method (#1950)
|
* Added parsing support for enum that has overridden toString() method (https://github.com/google/gson/pull/1950)
|
||||||
* Removed support for building Gson with Gradle (#2081)
|
* Removed support for building Gson with Gradle (https://github.com/google/gson/pull/2081)
|
||||||
* Removed obsolete `codegen` hierarchy (#2099)
|
* Removed obsolete `codegen` hierarchy (https://github.com/google/gson/pull/2099)
|
||||||
* Add support for reflection access filter (#1905)
|
* Add support for reflection access filter (https://github.com/google/gson/pull/1905)
|
||||||
* Improve `TypeToken` creation validation (#2072)
|
* Improve `TypeToken` creation validation (https://github.com/google/gson/pull/2072)
|
||||||
* Add explicit support for `float` in `JsonWriter` (#2130, #2132)
|
* Add explicit support for `float` in `JsonWriter` (https://github.com/google/gson/pull/2130, https://github.com/google/gson/pull/2132)
|
||||||
* Fail when parsing invalid local date (#2134)
|
* Fail when parsing invalid local date (https://github.com/google/gson/pull/2134)
|
||||||
|
|
||||||
Also many small improvements to javadoc.
|
Also many small improvements to javadoc.
|
||||||
|
|
||||||
@ -19,52 +40,52 @@ Also many small improvements to javadoc.
|
|||||||
|
|
||||||
**The minimum supported Java version changes from 6 to 7.**
|
**The minimum supported Java version changes from 6 to 7.**
|
||||||
|
|
||||||
* Change target Java version to 7 (#2043)
|
* Change target Java version to 7 (https://github.com/google/gson/pull/2043)
|
||||||
* Put `module-info.class` into Multi-Release JAR folder (#2013)
|
* Put `module-info.class` into Multi-Release JAR folder (https://github.com/google/gson/pull/2013)
|
||||||
* Improve error message when abstract class cannot be constructed (#1814)
|
* Improve error message when abstract class cannot be constructed (https://github.com/google/gson/pull/1814)
|
||||||
* Support EnumMap deserialization (#2071)
|
* Support EnumMap deserialization (https://github.com/google/gson/pull/2071)
|
||||||
* Add LazilyParsedNumber default adapter (#2060)
|
* Add LazilyParsedNumber default adapter (https://github.com/google/gson/pull/2060)
|
||||||
* Fix JsonReader.hasNext() returning true at end of document (#2061)
|
* Fix JsonReader.hasNext() returning true at end of document (https://github.com/google/gson/pull/2061)
|
||||||
* Remove Gradle build support. Build script was outdated and not actively
|
* Remove Gradle build support. Build script was outdated and not actively
|
||||||
maintained anymore (#2063)
|
maintained anymore (https://github.com/google/gson/pull/2063)
|
||||||
* Add `GsonBuilder.disableJdkUnsafe()` (#1904)
|
* Add `GsonBuilder.disableJdkUnsafe()` (https://github.com/google/gson/pull/1904)
|
||||||
* Add `UPPER_CASE_WITH_UNDERSCORES` in FieldNamingPolicy (#2024)
|
* Add `UPPER_CASE_WITH_UNDERSCORES` in FieldNamingPolicy (https://github.com/google/gson/pull/2024)
|
||||||
* Fix failing to serialize Collection or Map with inaccessible constructor (#1902)
|
* Fix failing to serialize Collection or Map with inaccessible constructor (https://github.com/google/gson/pull/1902)
|
||||||
* Improve TreeTypeAdapter thread-safety (#1976)
|
* Improve TreeTypeAdapter thread-safety (https://github.com/google/gson/pull/1976)
|
||||||
* Fix `Gson.newJsonWriter` ignoring lenient and HTML-safe setting (#1989)
|
* Fix `Gson.newJsonWriter` ignoring lenient and HTML-safe setting (https://github.com/google/gson/pull/1989)
|
||||||
* Delete unused LinkedHashTreeMap (#1992)
|
* Delete unused LinkedHashTreeMap (https://github.com/google/gson/pull/1992)
|
||||||
* Make default adapters stricter; improve exception messages (#2000)
|
* Make default adapters stricter; improve exception messages (https://github.com/google/gson/pull/2000)
|
||||||
* Fix `FieldNamingPolicy.upperCaseFirstLetter` uppercasing non-letter (#2004)
|
* Fix `FieldNamingPolicy.upperCaseFirstLetter` uppercasing non-letter (https://github.com/google/gson/pull/2004)
|
||||||
|
|
||||||
## Version 2.8.9
|
## Version 2.8.9
|
||||||
|
|
||||||
* Make OSGi bundle's dependency on `sun.misc` optional (#1993).
|
* Make OSGi bundle's dependency on `sun.misc` optional (https://github.com/google/gson/pull/1993).
|
||||||
* Deprecate `Gson.excluder()` exposing internal `Excluder` class (#1986).
|
* Deprecate `Gson.excluder()` exposing internal `Excluder` class (https://github.com/google/gson/pull/1986).
|
||||||
* Prevent Java deserialization of internal classes (#1991).
|
* Prevent Java deserialization of internal classes (https://github.com/google/gson/pull/1991).
|
||||||
* Improve number strategy implementation (#1987).
|
* Improve number strategy implementation (https://github.com/google/gson/pull/1987).
|
||||||
* Fix LongSerializationPolicy null handling being inconsistent with Gson (#1990).
|
* Fix LongSerializationPolicy null handling being inconsistent with Gson (https://github.com/google/gson/pull/1990).
|
||||||
* Support arbitrary Number implementation for Object and Number deserialization (#1290).
|
* Support arbitrary Number implementation for Object and Number deserialization (https://github.com/google/gson/pull/1290).
|
||||||
* Bump proguard-maven-plugin from 2.4.0 to 2.5.1 (#1980).
|
* Bump proguard-maven-plugin from 2.4.0 to 2.5.1 (https://github.com/google/gson/pull/1980).
|
||||||
* Don't exclude static local classes (#1969).
|
* Don't exclude static local classes (https://github.com/google/gson/pull/1969).
|
||||||
* Fix `RuntimeTypeAdapterFactory` depending on internal `Streams` class (#1959).
|
* Fix `RuntimeTypeAdapterFactory` depending on internal `Streams` class (https://github.com/google/gson/pull/1959).
|
||||||
* Improve Maven build (#1964).
|
* Improve Maven build (https://github.com/google/gson/pull/1964).
|
||||||
* Make dependency on `java.sql` optional (#1707).
|
* Make dependency on `java.sql` optional (https://github.com/google/gson/pull/1707).
|
||||||
|
|
||||||
## Version 2.8.8
|
## Version 2.8.8
|
||||||
|
|
||||||
* Fixed issue with recursive types (#1390).
|
* Fixed issue with recursive types (https://github.com/google/gson/issues/1390).
|
||||||
* Better behaviour with Java 9+ and `Unsafe` if there is a security manager (#1712).
|
* Better behaviour with Java 9+ and `Unsafe` if there is a security manager (https://github.com/google/gson/pull/1712).
|
||||||
* `EnumTypeAdapter` now works better when ProGuard has obfuscated enum fields (#1495).
|
* `EnumTypeAdapter` now works better when ProGuard has obfuscated enum fields (https://github.com/google/gson/pull/1495).
|
||||||
|
|
||||||
## Version 2.8.7
|
## Version 2.8.7
|
||||||
|
|
||||||
* Fixed `ISO8601UtilsTest` failing on systems with UTC+X.
|
* Fixed `ISO8601UtilsTest` failing on systems with UTC+X.
|
||||||
* Improved javadoc for `JsonStreamParser`.
|
* Improved javadoc for `JsonStreamParser`.
|
||||||
* Updated proguard.cfg (#1693).
|
* Updated proguard.cfg (https://github.com/google/gson/pull/1693).
|
||||||
* Fixed `IllegalStateException` in `JsonTreeWriter` (#1592).
|
* Fixed `IllegalStateException` in `JsonTreeWriter` (https://github.com/google/gson/issues/1592).
|
||||||
* Added `JsonArray.isEmpty()` (#1640).
|
* Added `JsonArray.isEmpty()` (https://github.com/google/gson/pull/1640).
|
||||||
* Added new test cases (#1638).
|
* Added new test cases (https://github.com/google/gson/pull/1638).
|
||||||
* Fixed OSGi metadata generation to work on JavaSE < 9 (#1603).
|
* Fixed OSGi metadata generation to work on JavaSE < 9 (https://github.com/google/gson/pull/1603).
|
||||||
|
|
||||||
## Version 2.8.6
|
## Version 2.8.6
|
||||||
_2019-10-04_ [GitHub Diff](https://github.com/google/gson/compare/gson-parent-2.8.5...gson-parent-2.8.6)
|
_2019-10-04_ [GitHub Diff](https://github.com/google/gson/compare/gson-parent-2.8.5...gson-parent-2.8.6)
|
||||||
|
@ -45,7 +45,7 @@ There are a few open-source projects that can convert Java objects to JSON. Howe
|
|||||||
Gradle:
|
Gradle:
|
||||||
```gradle
|
```gradle
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.google.code.gson:gson:2.9.1'
|
implementation 'com.google.code.gson:gson:2.10'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ Maven:
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.code.gson</groupId>
|
<groupId>com.google.code.gson</groupId>
|
||||||
<artifactId>gson</artifactId>
|
<artifactId>gson</artifactId>
|
||||||
<version>2.9.1</version>
|
<version>2.10</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ The following is a step-by-step procedure for releasing a new version of Google-
|
|||||||
1. Ensure all changelists are code-reviewed and have +1
|
1. Ensure all changelists are code-reviewed and have +1
|
||||||
1. `cd gson` to the parent directory; ensure there are no open files and all changes are committed.
|
1. `cd gson` to the parent directory; ensure there are no open files and all changes are committed.
|
||||||
1. Run `mvn release:clean`
|
1. Run `mvn release:clean`
|
||||||
1. Do a dry run: `mvn release:prepare -DdryRun=true`
|
|
||||||
1. Start the release: `mvn release:prepare`
|
1. Start the release: `mvn release:prepare`
|
||||||
- Answer questions: usually the defaults are fine. Try to follow [Semantic Versioning](https://semver.org/) when choosing the release version number.
|
- Answer questions: usually the defaults are fine. Try to follow [Semantic Versioning](https://semver.org/) when choosing the release version number.
|
||||||
- This will do a full build, change version from `-SNAPSHOT` to the released version, commit and create the tags. It will then change the version to `-SNAPSHOT` for the next release.
|
- This will do a full build, change version from `-SNAPSHOT` to the released version, commit and create the tags. It will then change the version to `-SNAPSHOT` for the next release.
|
||||||
@ -18,9 +17,13 @@ The following is a step-by-step procedure for releasing a new version of Google-
|
|||||||
1. Update version references in (version might be referenced multiple times):
|
1. Update version references in (version might be referenced multiple times):
|
||||||
- [`README.md`](README.md)
|
- [`README.md`](README.md)
|
||||||
- [`UserGuide.md`](UserGuide.md)
|
- [`UserGuide.md`](UserGuide.md)
|
||||||
|
|
||||||
|
Note: When using the Maven Release Plugin as described above, these version references should have been replaced automatically, but verify this manually nonetheless to be on the safe side.
|
||||||
1. Optional: Create a post on the [Gson Discussion Forum](https://groups.google.com/group/google-gson).
|
1. Optional: Create a post on the [Gson Discussion Forum](https://groups.google.com/group/google-gson).
|
||||||
1. Optional: Update the release version in [Wikipedia](https://en.wikipedia.org/wiki/Gson) and update the current "stable" release.
|
1. Optional: Update the release version in [Wikipedia](https://en.wikipedia.org/wiki/Gson) and update the current "stable" release.
|
||||||
|
|
||||||
|
Important: When aborting a release / rolling back release preparations, make sure to also revert all changes to files which were done during the release (e.g. automatic replacement of version references).
|
||||||
|
|
||||||
## Configuring a machine for deployment to Sonatype Repository
|
## Configuring a machine for deployment to Sonatype Repository
|
||||||
|
|
||||||
This section was borrowed heavily from [Doclava release process](https://code.google.com/archive/p/doclava/wikis/ProcessRelease.wiki).
|
This section was borrowed heavily from [Doclava release process](https://code.google.com/archive/p/doclava/wikis/ProcessRelease.wiki).
|
||||||
|
12
UserGuide.md
12
UserGuide.md
@ -76,7 +76,7 @@ The Gson instance does not maintain any state while invoking JSON operations. So
|
|||||||
|
|
||||||
```gradle
|
```gradle
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.google.code.gson:gson:2.9.1'
|
implementation 'com.google.code.gson:gson:2.10'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ To use Gson with Maven2/3, you can use the Gson version available in Maven Centr
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.code.gson</groupId>
|
<groupId>com.google.code.gson</groupId>
|
||||||
<artifactId>gson</artifactId>
|
<artifactId>gson</artifactId>
|
||||||
<version>2.9.1</version>
|
<version>2.10</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
@ -225,7 +225,9 @@ Collection<Integer> ints = Arrays.asList(1,2,3,4,5);
|
|||||||
String json = gson.toJson(ints); // ==> json is [1,2,3,4,5]
|
String json = gson.toJson(ints); // ==> json is [1,2,3,4,5]
|
||||||
|
|
||||||
// Deserialization
|
// Deserialization
|
||||||
Type collectionType = new TypeToken<Collection<Integer>>(){}.getType();
|
TypeToken<Collection<Integer>> collectionType = new TypeToken<Collection<Integer>>(){};
|
||||||
|
// Note: For older Gson versions it is necessary to use `collectionType.getType()` as argument below,
|
||||||
|
// this is however not type-safe and care must be taken to specify the correct type for the local variable
|
||||||
Collection<Integer> ints2 = gson.fromJson(json, collectionType);
|
Collection<Integer> ints2 = gson.fromJson(json, collectionType);
|
||||||
// ==> ints2 is same as ints
|
// ==> ints2 is same as ints
|
||||||
```
|
```
|
||||||
@ -263,10 +265,12 @@ For deserialization Gson uses the `read` method of the `TypeAdapter` registered
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
Gson gson = new Gson();
|
Gson gson = new Gson();
|
||||||
Type mapType = new TypeToken<Map<String, String>>(){}.getType();
|
TypeToken<Map<String, String>> mapType = new TypeToken<Map<String, String>>(){};
|
||||||
String json = "{\"key\": \"value\"}";
|
String json = "{\"key\": \"value\"}";
|
||||||
|
|
||||||
// Deserialization
|
// Deserialization
|
||||||
|
// Note: For older Gson versions it is necessary to use `mapType.getType()` as argument below,
|
||||||
|
// this is however not type-safe and care must be taken to specify the correct type for the local variable
|
||||||
Map<String, String> stringMap = gson.fromJson(json, mapType);
|
Map<String, String> stringMap = gson.fromJson(json, mapType);
|
||||||
// ==> stringMap is {key=value}
|
// ==> stringMap is {key=value}
|
||||||
```
|
```
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>io.gitlab.jfronny</groupId>
|
<groupId>io.gitlab.jfronny</groupId>
|
||||||
<artifactId>gson-parent</artifactId>
|
<artifactId>gson-parent</artifactId>
|
||||||
<version>2.9.2-SNAPSHOT</version>
|
<version>2.11-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>gson-extras</artifactId>
|
<artifactId>gson-extras</artifactId>
|
||||||
|
@ -91,7 +91,7 @@ import java.util.Map;
|
|||||||
* Both the type field name ({@code "type"}) and the type labels ({@code
|
* Both the type field name ({@code "type"}) and the type labels ({@code
|
||||||
* "Rectangle"}) are configurable.
|
* "Rectangle"}) are configurable.
|
||||||
*
|
*
|
||||||
* <h3>Registering Types</h3>
|
* <h2>Registering Types</h2>
|
||||||
* Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field
|
* Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field
|
||||||
* name to the {@link #of} factory method. If you don't supply an explicit type
|
* name to the {@link #of} factory method. If you don't supply an explicit type
|
||||||
* field name, {@code "type"} will be used. <pre> {@code
|
* field name, {@code "type"} will be used. <pre> {@code
|
||||||
@ -119,7 +119,7 @@ import java.util.Map;
|
|||||||
* .registerSubtype(Diamond.class);
|
* .registerSubtype(Diamond.class);
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <h3>Serialization and deserialization</h3>
|
* <h2>Serialization and deserialization</h2>
|
||||||
* In order to serialize and deserialize a polymorphic object,
|
* In order to serialize and deserialize a polymorphic object,
|
||||||
* you must specify the base type explicitly.
|
* you must specify the base type explicitly.
|
||||||
* <pre> {@code
|
* <pre> {@code
|
||||||
|
34
gson/pom.xml
34
gson/pom.xml
@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>io.gitlab.jfronny</groupId>
|
<groupId>io.gitlab.jfronny</groupId>
|
||||||
<artifactId>gson-parent</artifactId>
|
<artifactId>gson-parent</artifactId>
|
||||||
<version>2.9.2-SNAPSHOT</version>
|
<version>2.11-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>gson</artifactId>
|
<artifactId>gson</artifactId>
|
||||||
@ -17,6 +17,10 @@
|
|||||||
</license>
|
</license>
|
||||||
</licenses>
|
</licenses>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<excludeTestCompilation>**/Java17*</excludeTestCompilation>
|
||||||
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
@ -60,7 +64,23 @@
|
|||||||
</excludes>
|
</excludes>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>default-testCompile</id>
|
||||||
|
<phase>test-compile</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>testCompile</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<testExcludes>
|
||||||
|
<exclude>${excludeTestCompilation}</exclude>
|
||||||
|
</testExcludes>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<source>8</source>
|
||||||
|
<target>8</target>
|
||||||
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>biz.aQute.bnd</groupId>
|
<groupId>biz.aQute.bnd</groupId>
|
||||||
@ -119,4 +139,16 @@
|
|||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>JDK17</id>
|
||||||
|
<activation>
|
||||||
|
<jdk>[17,)</jdk>
|
||||||
|
</activation>
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.testRelease>17</maven.compiler.testRelease>
|
||||||
|
<excludeTestCompilation />
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
</project>
|
</project>
|
||||||
|
@ -86,6 +86,8 @@ public enum FieldNamingPolicy implements FieldNamingStrategy {
|
|||||||
* <li>aStringField ---> A_STRING_FIELD</li>
|
* <li>aStringField ---> A_STRING_FIELD</li>
|
||||||
* <li>aURL ---> A_U_R_L</li>
|
* <li>aURL ---> A_U_R_L</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
|
*
|
||||||
|
* @since 2.9.0
|
||||||
*/
|
*/
|
||||||
UPPER_CASE_WITH_UNDERSCORES() {
|
UPPER_CASE_WITH_UNDERSCORES() {
|
||||||
@Override public String translateName(Field f) {
|
@Override public String translateName(Field f) {
|
||||||
@ -125,7 +127,8 @@ public enum FieldNamingPolicy implements FieldNamingStrategy {
|
|||||||
* Using dashes in JavaScript is not recommended since dash is also used for a minus sign in
|
* Using dashes in JavaScript is not recommended since dash is also used for a minus sign in
|
||||||
* expressions. This requires that a field named with dashes is always accessed as a quoted
|
* expressions. This requires that a field named with dashes is always accessed as a quoted
|
||||||
* property like {@code myobject['my-field']}. Accessing it as an object field
|
* property like {@code myobject['my-field']}. Accessing it as an object field
|
||||||
* {@code myobject.my-field} will result in an unintended javascript expression.
|
* {@code myobject.my-field} will result in an unintended JavaScript expression.
|
||||||
|
*
|
||||||
* @since 1.4
|
* @since 1.4
|
||||||
*/
|
*/
|
||||||
LOWER_CASE_WITH_DASHES() {
|
LOWER_CASE_WITH_DASHES() {
|
||||||
@ -148,8 +151,9 @@ public enum FieldNamingPolicy implements FieldNamingStrategy {
|
|||||||
* Using dots in JavaScript is not recommended since dot is also used for a member sign in
|
* Using dots in JavaScript is not recommended since dot is also used for a member sign in
|
||||||
* expressions. This requires that a field named with dots is always accessed as a quoted
|
* expressions. This requires that a field named with dots is always accessed as a quoted
|
||||||
* property like {@code myobject['my.field']}. Accessing it as an object field
|
* property like {@code myobject['my.field']}. Accessing it as an object field
|
||||||
* {@code myobject.my.field} will result in an unintended javascript expression.
|
* {@code myobject.my.field} will result in an unintended JavaScript expression.
|
||||||
* @since 2.8
|
*
|
||||||
|
* @since 2.8.4
|
||||||
*/
|
*/
|
||||||
LOWER_CASE_WITH_DOTS() {
|
LOWER_CASE_WITH_DOTS() {
|
||||||
@Override public String translateName(Field f) {
|
@Override public String translateName(Field f) {
|
||||||
|
@ -27,6 +27,7 @@ import com.google.gson.internal.bind.MapTypeAdapterFactory;
|
|||||||
import com.google.gson.internal.bind.NumberTypeAdapter;
|
import com.google.gson.internal.bind.NumberTypeAdapter;
|
||||||
import com.google.gson.internal.bind.ObjectTypeAdapter;
|
import com.google.gson.internal.bind.ObjectTypeAdapter;
|
||||||
import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory;
|
import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory;
|
||||||
|
import com.google.gson.internal.bind.SerializationDelegatingTypeAdapter;
|
||||||
import com.google.gson.internal.bind.TypeAdapters;
|
import com.google.gson.internal.bind.TypeAdapters;
|
||||||
import com.google.gson.internal.sql.SqlTypesSupport;
|
import com.google.gson.internal.sql.SqlTypesSupport;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
@ -71,26 +72,32 @@ import java.util.concurrent.atomic.AtomicLongArray;
|
|||||||
* <pre>
|
* <pre>
|
||||||
* Gson gson = new Gson(); // Or use new GsonBuilder().create();
|
* Gson gson = new Gson(); // Or use new GsonBuilder().create();
|
||||||
* MyType target = new MyType();
|
* MyType target = new MyType();
|
||||||
* String json = gson.toJson(target); // serializes target to Json
|
* String json = gson.toJson(target); // serializes target to JSON
|
||||||
* MyType target2 = gson.fromJson(json, MyType.class); // deserializes json into target2
|
* MyType target2 = gson.fromJson(json, MyType.class); // deserializes json into target2
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* <p>If the object that your are serializing/deserializing is a {@code ParameterizedType}
|
* <p>If the type of the object that you are converting is a {@code ParameterizedType}
|
||||||
* (i.e. contains at least one type parameter and may be an array) then you must use the
|
* (i.e. has at least one type argument, for example {@code List<MyType>}) then for
|
||||||
* {@link #toJson(Object, Type)} or {@link #fromJson(String, Type)} method. Here is an
|
* deserialization you must use a {@code fromJson} method with {@link Type} or {@link TypeToken}
|
||||||
* example for serializing and deserializing a {@code ParameterizedType}:
|
* parameter to specify the parameterized type. For serialization specifying a {@code Type}
|
||||||
*
|
* or {@code TypeToken} is optional, otherwise Gson will use the runtime type of the object.
|
||||||
|
* {@link TypeToken} is a class provided by Gson which helps creating parameterized types.
|
||||||
|
* Here is an example showing how this can be done:
|
||||||
* <pre>
|
* <pre>
|
||||||
* Type listType = new TypeToken<List<String>>() {}.getType();
|
* TypeToken<List<MyType>> listType = new TypeToken<List<MyType>>() {};
|
||||||
* List<String> target = new LinkedList<String>();
|
* List<MyType> target = new LinkedList<MyType>();
|
||||||
* target.add("blah");
|
* target.add(new MyType(1, "abc"));
|
||||||
*
|
*
|
||||||
* Gson gson = new Gson();
|
* Gson gson = new Gson();
|
||||||
* String json = gson.toJson(target, listType);
|
* // For serialization you normally do not have to specify the type, Gson will use
|
||||||
* List<String> target2 = gson.fromJson(json, listType);
|
* // the runtime type of the objects, however you can also specify it explicitly
|
||||||
|
* String json = gson.toJson(target, listType.getType());
|
||||||
|
*
|
||||||
|
* // But for deserialization you have to specify the type
|
||||||
|
* List<MyType> target2 = gson.fromJson(json, listType);
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* <p>See the <a href="https://sites.google.com/site/gson/gson-user-guide">Gson User Guide</a>
|
* <p>See the <a href="https://github.com/google/gson/blob/master/UserGuide.md">Gson User Guide</a>
|
||||||
* for a more complete set of examples.</p>
|
* for a more complete set of examples.</p>
|
||||||
*
|
*
|
||||||
* <h2>Lenient JSON handling</h2>
|
* <h2>Lenient JSON handling</h2>
|
||||||
@ -120,7 +127,7 @@ import java.util.concurrent.atomic.AtomicLongArray;
|
|||||||
* to make sure there is no trailing data
|
* to make sure there is no trailing data
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
*
|
||||||
* @see com.google.gson.reflect.TypeToken
|
* @see TypeToken
|
||||||
*
|
*
|
||||||
* @author Inderjeet Singh
|
* @author Inderjeet Singh
|
||||||
* @author Joel Leitch
|
* @author Joel Leitch
|
||||||
@ -192,9 +199,9 @@ public final class Gson {
|
|||||||
* through {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()}. </li>
|
* through {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()}. </li>
|
||||||
* <li>By default, Gson ignores the {@link com.google.gson.annotations.Since} annotation. You
|
* <li>By default, Gson ignores the {@link com.google.gson.annotations.Since} annotation. You
|
||||||
* can enable Gson to use this annotation through {@link GsonBuilder#setVersion(double)}.</li>
|
* can enable Gson to use this annotation through {@link GsonBuilder#setVersion(double)}.</li>
|
||||||
* <li>The default field naming policy for the output Json is same as in Java. So, a Java class
|
* <li>The default field naming policy for the output JSON is same as in Java. So, a Java class
|
||||||
* field <code>versionNumber</code> will be output as <code>"versionNumber"</code> in
|
* field <code>versionNumber</code> will be output as <code>"versionNumber"</code> in
|
||||||
* Json. The same rules are applied for mapping incoming Json to the Java classes. You can
|
* JSON. The same rules are applied for mapping incoming JSON to the Java classes. You can
|
||||||
* change this policy through {@link GsonBuilder#setFieldNamingPolicy(FieldNamingPolicy)}.</li>
|
* change this policy through {@link GsonBuilder#setFieldNamingPolicy(FieldNamingPolicy)}.</li>
|
||||||
* <li>By default, Gson excludes <code>transient</code> or <code>static</code> fields from
|
* <li>By default, Gson excludes <code>transient</code> or <code>static</code> fields from
|
||||||
* consideration for serialization and deserialization. You can change this behavior through
|
* consideration for serialization and deserialization. You can change this behavior through
|
||||||
@ -321,6 +328,7 @@ public final class Gson {
|
|||||||
* instance.
|
* instance.
|
||||||
*
|
*
|
||||||
* @return a GsonBuilder instance.
|
* @return a GsonBuilder instance.
|
||||||
|
* @since 2.8.3
|
||||||
*/
|
*/
|
||||||
public GsonBuilder newBuilder() {
|
public GsonBuilder newBuilder() {
|
||||||
return new GsonBuilder(this);
|
return new GsonBuilder(this);
|
||||||
@ -374,7 +382,7 @@ public final class Gson {
|
|||||||
}
|
}
|
||||||
double doubleValue = value.doubleValue();
|
double doubleValue = value.doubleValue();
|
||||||
checkValidFloatingPoint(doubleValue);
|
checkValidFloatingPoint(doubleValue);
|
||||||
out.value(value);
|
out.value(doubleValue);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -398,7 +406,10 @@ public final class Gson {
|
|||||||
}
|
}
|
||||||
float floatValue = value.floatValue();
|
float floatValue = value.floatValue();
|
||||||
checkValidFloatingPoint(floatValue);
|
checkValidFloatingPoint(floatValue);
|
||||||
out.value(value);
|
// For backward compatibility don't call `JsonWriter.value(float)` because that method has
|
||||||
|
// been newly added and not all custom JsonWriter implementations might override it yet
|
||||||
|
Number floatNumber = value instanceof Float ? value : floatValue;
|
||||||
|
out.value(floatNumber);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -619,13 +630,15 @@ public final class Gson {
|
|||||||
* {@link JsonElement}s. This method should be used when the specified object is not a generic
|
* {@link JsonElement}s. This method should be used when the specified object is not a generic
|
||||||
* type. This method uses {@link Class#getClass()} to get the type for the specified object, but
|
* type. This method uses {@link Class#getClass()} to get the type for the specified object, but
|
||||||
* the {@code getClass()} loses the generic type information because of the Type Erasure feature
|
* the {@code getClass()} loses the generic type information because of the Type Erasure feature
|
||||||
* of Java. Note that this method works fine if the any of the object fields are of generic type,
|
* of Java. Note that this method works fine if any of the object fields are of generic type,
|
||||||
* just the object itself should not be of a generic type. If the object is of generic type, use
|
* just the object itself should not be of a generic type. If the object is of generic type, use
|
||||||
* {@link #toJsonTree(Object, Type)} instead.
|
* {@link #toJsonTree(Object, Type)} instead.
|
||||||
*
|
*
|
||||||
* @param src the object for which Json representation is to be created setting for Gson
|
* @param src the object for which JSON representation is to be created
|
||||||
* @return Json representation of {@code src}.
|
* @return JSON representation of {@code src}.
|
||||||
* @since 1.4
|
* @since 1.4
|
||||||
|
*
|
||||||
|
* @see #toJsonTree(Object, Type)
|
||||||
*/
|
*/
|
||||||
public JsonElement toJsonTree(Object src) {
|
public JsonElement toJsonTree(Object src) {
|
||||||
if (src == null) {
|
if (src == null) {
|
||||||
@ -649,6 +662,8 @@ public final class Gson {
|
|||||||
* </pre>
|
* </pre>
|
||||||
* @return Json representation of {@code src}
|
* @return Json representation of {@code src}
|
||||||
* @since 1.4
|
* @since 1.4
|
||||||
|
*
|
||||||
|
* @see #toJsonTree(Object)
|
||||||
*/
|
*/
|
||||||
public JsonElement toJsonTree(Object src, Type typeOfSrc) {
|
public JsonElement toJsonTree(Object src, Type typeOfSrc) {
|
||||||
JsonTreeWriter writer = new JsonTreeWriter();
|
JsonTreeWriter writer = new JsonTreeWriter();
|
||||||
@ -657,17 +672,20 @@ public final class Gson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method serializes the specified object into its equivalent Json representation.
|
* This method serializes the specified object into its equivalent JSON representation.
|
||||||
* This method should be used when the specified object is not a generic type. This method uses
|
* This method should be used when the specified object is not a generic type. This method uses
|
||||||
* {@link Class#getClass()} to get the type for the specified object, but the
|
* {@link Class#getClass()} to get the type for the specified object, but the
|
||||||
* {@code getClass()} loses the generic type information because of the Type Erasure feature
|
* {@code getClass()} loses the generic type information because of the Type Erasure feature
|
||||||
* of Java. Note that this method works fine if the any of the object fields are of generic type,
|
* of Java. Note that this method works fine if any of the object fields are of generic type,
|
||||||
* just the object itself should not be of a generic type. If the object is of generic type, use
|
* just the object itself should not be of a generic type. If the object is of generic type, use
|
||||||
* {@link #toJson(Object, Type)} instead. If you want to write out the object to a
|
* {@link #toJson(Object, Type)} instead. If you want to write out the object to a
|
||||||
* {@link Writer}, use {@link #toJson(Object, Appendable)} instead.
|
* {@link Writer}, use {@link #toJson(Object, Appendable)} instead.
|
||||||
*
|
*
|
||||||
* @param src the object for which Json representation is to be created setting for Gson
|
* @param src the object for which JSON representation is to be created
|
||||||
* @return Json representation of {@code src}.
|
* @return Json representation of {@code src}.
|
||||||
|
*
|
||||||
|
* @see #toJson(Object, Appendable)
|
||||||
|
* @see #toJson(Object, Type)
|
||||||
*/
|
*/
|
||||||
public String toJson(Object src) {
|
public String toJson(Object src) {
|
||||||
if (src == null) {
|
if (src == null) {
|
||||||
@ -678,7 +696,7 @@ public final class Gson {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This method serializes the specified object, including those of generic types, into its
|
* This method serializes the specified object, including those of generic types, into its
|
||||||
* equivalent Json representation. This method must be used if the specified object is a generic
|
* equivalent JSON representation. This method must be used if the specified object is a generic
|
||||||
* type. For non-generic objects, use {@link #toJson(Object)} instead. If you want to write out
|
* type. For non-generic objects, use {@link #toJson(Object)} instead. If you want to write out
|
||||||
* the object to a {@link Appendable}, use {@link #toJson(Object, Type, Appendable)} instead.
|
* the object to a {@link Appendable}, use {@link #toJson(Object, Type, Appendable)} instead.
|
||||||
*
|
*
|
||||||
@ -689,7 +707,10 @@ public final class Gson {
|
|||||||
* <pre>
|
* <pre>
|
||||||
* Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
|
* Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
|
||||||
* </pre>
|
* </pre>
|
||||||
* @return Json representation of {@code src}
|
* @return JSON representation of {@code src}
|
||||||
|
*
|
||||||
|
* @see #toJson(Object, Type, Appendable)
|
||||||
|
* @see #toJson(Object)
|
||||||
*/
|
*/
|
||||||
public String toJson(Object src, Type typeOfSrc) {
|
public String toJson(Object src, Type typeOfSrc) {
|
||||||
StringWriter writer = new StringWriter();
|
StringWriter writer = new StringWriter();
|
||||||
@ -698,18 +719,22 @@ public final class Gson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method serializes the specified object into its equivalent Json representation.
|
* This method serializes the specified object into its equivalent JSON representation and
|
||||||
|
* writes it to the writer.
|
||||||
* This method should be used when the specified object is not a generic type. This method uses
|
* This method should be used when the specified object is not a generic type. This method uses
|
||||||
* {@link Class#getClass()} to get the type for the specified object, but the
|
* {@link Class#getClass()} to get the type for the specified object, but the
|
||||||
* {@code getClass()} loses the generic type information because of the Type Erasure feature
|
* {@code getClass()} loses the generic type information because of the Type Erasure feature
|
||||||
* of Java. Note that this method works fine if the any of the object fields are of generic type,
|
* of Java. Note that this method works fine if any of the object fields are of generic type,
|
||||||
* just the object itself should not be of a generic type. If the object is of generic type, use
|
* just the object itself should not be of a generic type. If the object is of generic type, use
|
||||||
* {@link #toJson(Object, Type, Appendable)} instead.
|
* {@link #toJson(Object, Type, Appendable)} instead.
|
||||||
*
|
*
|
||||||
* @param src the object for which Json representation is to be created setting for Gson
|
* @param src the object for which JSON representation is to be created
|
||||||
* @param writer Writer to which the Json representation needs to be written
|
* @param writer Writer to which the JSON representation needs to be written
|
||||||
* @throws JsonIOException if there was a problem writing to the writer
|
* @throws JsonIOException if there was a problem writing to the writer
|
||||||
* @since 1.2
|
* @since 1.2
|
||||||
|
*
|
||||||
|
* @see #toJson(Object)
|
||||||
|
* @see #toJson(Object, Type, Appendable)
|
||||||
*/
|
*/
|
||||||
public void toJson(Object src, Appendable writer) throws JsonIOException {
|
public void toJson(Object src, Appendable writer) throws JsonIOException {
|
||||||
if (src != null) {
|
if (src != null) {
|
||||||
@ -721,8 +746,9 @@ public final class Gson {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This method serializes the specified object, including those of generic types, into its
|
* This method serializes the specified object, including those of generic types, into its
|
||||||
* equivalent Json representation. This method must be used if the specified object is a generic
|
* equivalent JSON representation and writes it to the writer.
|
||||||
* type. For non-generic objects, use {@link #toJson(Object, Appendable)} instead.
|
* This method must be used if the specified object is a generic type. For non-generic objects,
|
||||||
|
* use {@link #toJson(Object, Appendable)} instead.
|
||||||
*
|
*
|
||||||
* @param src the object for which JSON representation is to be created
|
* @param src the object for which JSON representation is to be created
|
||||||
* @param typeOfSrc The specific genericized type of src. You can obtain
|
* @param typeOfSrc The specific genericized type of src. You can obtain
|
||||||
@ -731,9 +757,12 @@ public final class Gson {
|
|||||||
* <pre>
|
* <pre>
|
||||||
* Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
|
* Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
|
||||||
* </pre>
|
* </pre>
|
||||||
* @param writer Writer to which the Json representation of src needs to be written.
|
* @param writer Writer to which the JSON representation of src needs to be written.
|
||||||
* @throws JsonIOException if there was a problem writing to the writer
|
* @throws JsonIOException if there was a problem writing to the writer
|
||||||
* @since 1.2
|
* @since 1.2
|
||||||
|
*
|
||||||
|
* @see #toJson(Object, Type)
|
||||||
|
* @see #toJson(Object, Appendable)
|
||||||
*/
|
*/
|
||||||
public void toJson(Object src, Type typeOfSrc, Appendable writer) throws JsonIOException {
|
public void toJson(Object src, Type typeOfSrc, Appendable writer) throws JsonIOException {
|
||||||
try {
|
try {
|
||||||
@ -802,7 +831,7 @@ public final class Gson {
|
|||||||
* Writes out the equivalent JSON for a tree of {@link JsonElement}s.
|
* Writes out the equivalent JSON for a tree of {@link JsonElement}s.
|
||||||
*
|
*
|
||||||
* @param jsonElement root of a tree of {@link JsonElement}s
|
* @param jsonElement root of a tree of {@link JsonElement}s
|
||||||
* @param writer Writer to which the Json representation needs to be written
|
* @param writer Writer to which the JSON representation needs to be written
|
||||||
* @throws JsonIOException if there was a problem writing to the writer
|
* @throws JsonIOException if there was a problem writing to the writer
|
||||||
* @since 1.4
|
* @since 1.4
|
||||||
*/
|
*/
|
||||||
@ -840,6 +869,7 @@ public final class Gson {
|
|||||||
jsonWriter.setLenient(lenient);
|
jsonWriter.setLenient(lenient);
|
||||||
jsonWriter.setOmitQuotes(omitQuotes);
|
jsonWriter.setOmitQuotes(omitQuotes);
|
||||||
jsonWriter.setSerializeNulls(serializeNulls);
|
jsonWriter.setSerializeNulls(serializeNulls);
|
||||||
|
jsonWriter.setSerializeSpecialFloatingPointValues(serializeSpecialFloatingPointValues);
|
||||||
return jsonWriter;
|
return jsonWriter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -854,6 +884,7 @@ public final class Gson {
|
|||||||
public JsonReader newJsonReader(Reader reader) {
|
public JsonReader newJsonReader(Reader reader) {
|
||||||
JsonReader jsonReader = new JsonReader(reader);
|
JsonReader jsonReader = new JsonReader(reader);
|
||||||
jsonReader.setLenient(lenient);
|
jsonReader.setLenient(lenient);
|
||||||
|
jsonReader.setSerializeSpecialFloatingPointValues(serializeSpecialFloatingPointValues);
|
||||||
return jsonReader;
|
return jsonReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -896,17 +927,17 @@ public final class Gson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method deserializes the specified Json into an object of the specified class. It is not
|
* This method deserializes the specified JSON into an object of the specified class. It is not
|
||||||
* suitable to use if the specified class is a generic type since it will not have the generic
|
* suitable to use if the specified class is a generic type since it will not have the generic
|
||||||
* type information because of the Type Erasure feature of Java. Therefore, this method should not
|
* type information because of the Type Erasure feature of Java. Therefore, this method should not
|
||||||
* be used if the desired type is a generic type. Note that this method works fine if the any of
|
* be used if the desired type is a generic type. Note that this method works fine if the any of
|
||||||
* the fields of the specified object are generics, just the object itself should not be a
|
* the fields of the specified object are generics, just the object itself should not be a
|
||||||
* generic type. For the cases when the object is of generic type, invoke
|
* generic type. For the cases when the object is of generic type, invoke
|
||||||
* {@link #fromJson(String, Type)}. If you have the Json in a {@link Reader} instead of
|
* {@link #fromJson(String, TypeToken)}. If you have the JSON in a {@link Reader} instead of
|
||||||
* a String, use {@link #fromJson(Reader, Class)} instead.
|
* a String, use {@link #fromJson(Reader, Class)} instead.
|
||||||
*
|
*
|
||||||
* <p>An exception is thrown if the JSON string has multiple top-level JSON elements,
|
* <p>An exception is thrown if the JSON string has multiple top-level JSON elements, or if there
|
||||||
* or if there is trailing data.
|
* is trailing data. Use {@link #fromJson(JsonReader, Type)} if this behavior is not desired.
|
||||||
*
|
*
|
||||||
* @param <T> the type of the desired object
|
* @param <T> the type of the desired object
|
||||||
* @param json the string from which the object is to be deserialized
|
* @param json the string from which the object is to be deserialized
|
||||||
@ -915,98 +946,167 @@ public final class Gson {
|
|||||||
* or if {@code json} is empty.
|
* or if {@code json} is empty.
|
||||||
* @throws JsonSyntaxException if json is not a valid representation for an object of type
|
* @throws JsonSyntaxException if json is not a valid representation for an object of type
|
||||||
* classOfT
|
* classOfT
|
||||||
|
*
|
||||||
|
* @see #fromJson(Reader, Class)
|
||||||
|
* @see #fromJson(String, TypeToken)
|
||||||
*/
|
*/
|
||||||
public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
|
public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
|
||||||
Object object = fromJson(json, (Type) classOfT);
|
T object = fromJson(json, TypeToken.get(classOfT));
|
||||||
return Primitives.wrap(classOfT).cast(object);
|
return Primitives.wrap(classOfT).cast(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method deserializes the specified Json into an object of the specified type. This method
|
* This method deserializes the specified JSON into an object of the specified type. This method
|
||||||
* is useful if the specified object is a generic type. For non-generic objects, use
|
* is useful if the specified object is a generic type. For non-generic objects, use
|
||||||
* {@link #fromJson(String, Class)} instead. If you have the Json in a {@link Reader} instead of
|
* {@link #fromJson(String, Class)} instead. If you have the JSON in a {@link Reader} instead of
|
||||||
* a String, use {@link #fromJson(Reader, Type)} instead.
|
* a String, use {@link #fromJson(Reader, Type)} instead.
|
||||||
*
|
*
|
||||||
|
* <p>Since {@code Type} is not parameterized by T, this method is not type-safe and
|
||||||
|
* should be used carefully. If you are creating the {@code Type} from a {@link TypeToken},
|
||||||
|
* prefer using {@link #fromJson(String, TypeToken)} instead since its return type is based
|
||||||
|
* on the {@code TypeToken} and is therefore more type-safe.
|
||||||
|
*
|
||||||
* <p>An exception is thrown if the JSON string has multiple top-level JSON elements,
|
* <p>An exception is thrown if the JSON string has multiple top-level JSON elements,
|
||||||
* or if there is trailing data.
|
* or if there is trailing data. Use {@link #fromJson(JsonReader, Type)} if this behavior is
|
||||||
|
* not desired.
|
||||||
*
|
*
|
||||||
* @param <T> the type of the desired object
|
* @param <T> the type of the desired object
|
||||||
* @param json the string from which the object is to be deserialized
|
* @param json the string from which the object is to be deserialized
|
||||||
* @param typeOfT The specific genericized type of src. You can obtain this type by using the
|
* @param typeOfT The specific genericized type of src
|
||||||
* {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for
|
* @return an object of type T from the string. Returns {@code null} if {@code json} is {@code null}
|
||||||
|
* or if {@code json} is empty.
|
||||||
|
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
|
||||||
|
*
|
||||||
|
* @see #fromJson(Reader, Type)
|
||||||
|
* @see #fromJson(String, Class)
|
||||||
|
* @see #fromJson(String, TypeToken)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
|
||||||
|
return (T) fromJson(json, TypeToken.get(typeOfT));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method deserializes the specified JSON into an object of the specified type. This method
|
||||||
|
* is useful if the specified object is a generic type. For non-generic objects, use
|
||||||
|
* {@link #fromJson(String, Class)} instead. If you have the JSON in a {@link Reader} instead of
|
||||||
|
* a String, use {@link #fromJson(Reader, TypeToken)} instead.
|
||||||
|
*
|
||||||
|
* <p>An exception is thrown if the JSON string has multiple top-level JSON elements, or if there
|
||||||
|
* is trailing data. Use {@link #fromJson(JsonReader, TypeToken)} if this behavior is not desired.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the desired object
|
||||||
|
* @param json the string from which the object is to be deserialized
|
||||||
|
* @param typeOfT The specific genericized type of src. You should create an anonymous subclass of
|
||||||
|
* {@code TypeToken} with the specific generic type arguments. For example, to get the type for
|
||||||
* {@code Collection<Foo>}, you should use:
|
* {@code Collection<Foo>}, you should use:
|
||||||
* <pre>
|
* <pre>
|
||||||
* Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
|
* new TypeToken<Collection<Foo>>(){}
|
||||||
* </pre>
|
* </pre>
|
||||||
* @return an object of type T from the string. Returns {@code null} if {@code json} is {@code null}
|
* @return an object of type T from the string. Returns {@code null} if {@code json} is {@code null}
|
||||||
* or if {@code json} is empty.
|
* or if {@code json} is empty.
|
||||||
* @throws JsonParseException if json is not a valid representation for an object of type typeOfT
|
* @throws JsonSyntaxException if json is not a valid representation for an object of the type typeOfT
|
||||||
* @throws JsonSyntaxException if json is not a valid representation for an object of type
|
*
|
||||||
|
* @see #fromJson(Reader, TypeToken)
|
||||||
|
* @see #fromJson(String, Class)
|
||||||
|
* @since 2.10
|
||||||
*/
|
*/
|
||||||
public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
|
public <T> T fromJson(String json, TypeToken<T> typeOfT) throws JsonSyntaxException {
|
||||||
if (json == null) {
|
if (json == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
StringReader reader = new StringReader(json);
|
StringReader reader = new StringReader(json);
|
||||||
@SuppressWarnings("unchecked")
|
return fromJson(reader, typeOfT);
|
||||||
T target = (T) fromJson(reader, typeOfT);
|
|
||||||
return target;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method deserializes the Json read from the specified reader into an object of the
|
* This method deserializes the JSON read from the specified reader into an object of the
|
||||||
* specified class. It is not suitable to use if the specified class is a generic type since it
|
* specified class. It is not suitable to use if the specified class is a generic type since it
|
||||||
* will not have the generic type information because of the Type Erasure feature of Java.
|
* will not have the generic type information because of the Type Erasure feature of Java.
|
||||||
* Therefore, this method should not be used if the desired type is a generic type. Note that
|
* Therefore, this method should not be used if the desired type is a generic type. Note that
|
||||||
* this method works fine if the any of the fields of the specified object are generics, just the
|
* this method works fine if any of the fields of the specified object are generics, just the
|
||||||
* object itself should not be a generic type. For the cases when the object is of generic type,
|
* object itself should not be a generic type. For the cases when the object is of generic type,
|
||||||
* invoke {@link #fromJson(Reader, Type)}. If you have the Json in a String form instead of a
|
* invoke {@link #fromJson(Reader, TypeToken)}. If you have the JSON in a String form instead of a
|
||||||
* {@link Reader}, use {@link #fromJson(String, Class)} instead.
|
* {@link Reader}, use {@link #fromJson(String, Class)} instead.
|
||||||
*
|
*
|
||||||
* <p>An exception is thrown if the JSON data has multiple top-level JSON elements,
|
* <p>An exception is thrown if the JSON data has multiple top-level JSON elements, or if there
|
||||||
* or if there is trailing data.
|
* is trailing data. Use {@link #fromJson(JsonReader, Type)} if this behavior is not desired.
|
||||||
*
|
*
|
||||||
* @param <T> the type of the desired object
|
* @param <T> the type of the desired object
|
||||||
* @param json the reader producing the Json from which the object is to be deserialized.
|
* @param json the reader producing the JSON from which the object is to be deserialized.
|
||||||
* @param classOfT the class of T
|
* @param classOfT the class of T
|
||||||
* @return an object of type T from the string. Returns {@code null} if {@code json} is at EOF.
|
* @return an object of type T from the Reader. Returns {@code null} if {@code json} is at EOF.
|
||||||
* @throws JsonIOException if there was a problem reading from the Reader
|
* @throws JsonIOException if there was a problem reading from the Reader
|
||||||
* @throws JsonSyntaxException if json is not a valid representation for an object of type
|
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
|
||||||
* @since 1.2
|
* @since 1.2
|
||||||
|
*
|
||||||
|
* @see #fromJson(String, Class)
|
||||||
|
* @see #fromJson(Reader, TypeToken)
|
||||||
*/
|
*/
|
||||||
public <T> T fromJson(Reader json, Class<T> classOfT) throws JsonSyntaxException, JsonIOException {
|
public <T> T fromJson(Reader json, Class<T> classOfT) throws JsonSyntaxException, JsonIOException {
|
||||||
JsonReader jsonReader = newJsonReader(json);
|
T object = fromJson(json, TypeToken.get(classOfT));
|
||||||
Object object = fromJson(jsonReader, classOfT);
|
|
||||||
assertFullConsumption(object, jsonReader);
|
|
||||||
return Primitives.wrap(classOfT).cast(object);
|
return Primitives.wrap(classOfT).cast(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method deserializes the Json read from the specified reader into an object of the
|
* This method deserializes the JSON read from the specified reader into an object of the
|
||||||
* specified type. This method is useful if the specified object is a generic type. For
|
* specified type. This method is useful if the specified object is a generic type. For
|
||||||
* non-generic objects, use {@link #fromJson(Reader, Class)} instead. If you have the Json in a
|
* non-generic objects, use {@link #fromJson(Reader, Class)} instead. If you have the JSON in a
|
||||||
* String form instead of a {@link Reader}, use {@link #fromJson(String, Type)} instead.
|
* String form instead of a {@link Reader}, use {@link #fromJson(String, Type)} instead.
|
||||||
*
|
*
|
||||||
* <p>An exception is thrown if the JSON data has multiple top-level JSON elements,
|
* <p>Since {@code Type} is not parameterized by T, this method is not type-safe and
|
||||||
* or if there is trailing data.
|
* should be used carefully. If you are creating the {@code Type} from a {@link TypeToken},
|
||||||
|
* prefer using {@link #fromJson(Reader, TypeToken)} instead since its return type is based
|
||||||
|
* on the {@code TypeToken} and is therefore more type-safe.
|
||||||
|
*
|
||||||
|
* <p>An exception is thrown if the JSON data has multiple top-level JSON elements, or if there
|
||||||
|
* is trailing data. Use {@link #fromJson(JsonReader, Type)} if this behavior is not desired.
|
||||||
*
|
*
|
||||||
* @param <T> the type of the desired object
|
* @param <T> the type of the desired object
|
||||||
* @param json the reader producing Json from which the object is to be deserialized
|
* @param json the reader producing JSON from which the object is to be deserialized
|
||||||
* @param typeOfT The specific genericized type of src. You can obtain this type by using the
|
* @param typeOfT The specific genericized type of src
|
||||||
* {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for
|
* @return an object of type T from the Reader. Returns {@code null} if {@code json} is at EOF.
|
||||||
|
* @throws JsonIOException if there was a problem reading from the Reader
|
||||||
|
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
|
||||||
|
* @since 1.2
|
||||||
|
*
|
||||||
|
* @see #fromJson(String, Type)
|
||||||
|
* @see #fromJson(Reader, Class)
|
||||||
|
* @see #fromJson(Reader, TypeToken)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
|
||||||
|
return (T) fromJson(json, TypeToken.get(typeOfT));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method deserializes the JSON read from the specified reader into an object of the
|
||||||
|
* specified type. This method is useful if the specified object is a generic type. For
|
||||||
|
* non-generic objects, use {@link #fromJson(Reader, Class)} instead. If you have the JSON in a
|
||||||
|
* String form instead of a {@link Reader}, use {@link #fromJson(String, TypeToken)} instead.
|
||||||
|
*
|
||||||
|
* <p>An exception is thrown if the JSON data has multiple top-level JSON elements, or if there
|
||||||
|
* is trailing data. Use {@link #fromJson(JsonReader, TypeToken)} if this behavior is not desired.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the desired object
|
||||||
|
* @param json the reader producing JSON from which the object is to be deserialized
|
||||||
|
* @param typeOfT The specific genericized type of src. You should create an anonymous subclass of
|
||||||
|
* {@code TypeToken} with the specific generic type arguments. For example, to get the type for
|
||||||
* {@code Collection<Foo>}, you should use:
|
* {@code Collection<Foo>}, you should use:
|
||||||
* <pre>
|
* <pre>
|
||||||
* Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
|
* new TypeToken<Collection<Foo>>(){}
|
||||||
* </pre>
|
* </pre>
|
||||||
* @return an object of type T from the json. Returns {@code null} if {@code json} is at EOF.
|
* @return an object of type T from the Reader. Returns {@code null} if {@code json} is at EOF.
|
||||||
* @throws JsonIOException if there was a problem reading from the Reader
|
* @throws JsonIOException if there was a problem reading from the Reader
|
||||||
* @throws JsonSyntaxException if json is not a valid representation for an object of type
|
* @throws JsonSyntaxException if json is not a valid representation for an object of type of typeOfT
|
||||||
* @since 1.2
|
*
|
||||||
|
* @see #fromJson(String, TypeToken)
|
||||||
|
* @see #fromJson(Reader, Class)
|
||||||
|
* @since 2.10
|
||||||
*/
|
*/
|
||||||
public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
|
public <T> T fromJson(Reader json, TypeToken<T> typeOfT) throws JsonIOException, JsonSyntaxException {
|
||||||
JsonReader jsonReader = newJsonReader(json);
|
JsonReader jsonReader = newJsonReader(json);
|
||||||
@SuppressWarnings("unchecked")
|
T object = fromJson(jsonReader, typeOfT);
|
||||||
T object = (T) fromJson(jsonReader, typeOfT);
|
|
||||||
assertFullConsumption(object, jsonReader);
|
assertFullConsumption(object, jsonReader);
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
@ -1023,10 +1123,18 @@ public final class Gson {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fromJson(JsonReader, Class) is unfortunately missing and cannot be added now without breaking
|
||||||
|
// source compatibility in certain cases, see https://github.com/google/gson/pull/1700#discussion_r973764414
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the next JSON value from {@code reader} and convert it to an object
|
* Reads the next JSON value from {@code reader} and converts it to an object
|
||||||
* of type {@code typeOfT}. Returns {@code null}, if the {@code reader} is at EOF.
|
* of type {@code typeOfT}. Returns {@code null}, if the {@code reader} is at EOF.
|
||||||
* Since Type is not parameterized by T, this method is type unsafe and should be used carefully.
|
*
|
||||||
|
* <p>Since {@code Type} is not parameterized by T, this method is not type-safe and
|
||||||
|
* should be used carefully. If you are creating the {@code Type} from a {@link TypeToken},
|
||||||
|
* prefer using {@link #fromJson(JsonReader, TypeToken)} instead since its return type is based
|
||||||
|
* on the {@code TypeToken} and is therefore more type-safe. If the provided type is a
|
||||||
|
* {@code Class} the {@code TypeToken} can be created with {@link TypeToken#get(Class)}.
|
||||||
*
|
*
|
||||||
* <p>Unlike the other {@code fromJson} methods, no exception is thrown if the JSON data has
|
* <p>Unlike the other {@code fromJson} methods, no exception is thrown if the JSON data has
|
||||||
* multiple top-level JSON elements, or if there is trailing data.
|
* multiple top-level JSON elements, or if there is trailing data.
|
||||||
@ -1035,19 +1143,59 @@ public final class Gson {
|
|||||||
* regardless of the lenient mode setting of the provided reader. The lenient mode setting
|
* regardless of the lenient mode setting of the provided reader. The lenient mode setting
|
||||||
* of the reader is restored once this method returns.
|
* of the reader is restored once this method returns.
|
||||||
*
|
*
|
||||||
* @throws JsonIOException if there was a problem writing to the Reader
|
* @param <T> the type of the desired object
|
||||||
* @throws JsonSyntaxException if json is not a valid representation for an object of type
|
* @param reader the reader whose next JSON value should be deserialized
|
||||||
|
* @param typeOfT The specific genericized type of src
|
||||||
|
* @return an object of type T from the JsonReader. Returns {@code null} if {@code reader} is at EOF.
|
||||||
|
* @throws JsonIOException if there was a problem reading from the JsonReader
|
||||||
|
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
|
||||||
|
*
|
||||||
|
* @see #fromJson(Reader, Type)
|
||||||
|
* @see #fromJson(JsonReader, TypeToken)
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
|
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
|
||||||
|
return (T) fromJson(reader, TypeToken.get(typeOfT));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next JSON value from {@code reader} and converts it to an object
|
||||||
|
* of type {@code typeOfT}. Returns {@code null}, if the {@code reader} is at EOF.
|
||||||
|
* This method is useful if the specified object is a generic type. For non-generic objects,
|
||||||
|
* {@link #fromJson(JsonReader, Type)} can be called, or {@link TypeToken#get(Class)} can
|
||||||
|
* be used to create the type token.
|
||||||
|
*
|
||||||
|
* <p>Unlike the other {@code fromJson} methods, no exception is thrown if the JSON data has
|
||||||
|
* multiple top-level JSON elements, or if there is trailing data.
|
||||||
|
*
|
||||||
|
* <p>The JSON data is parsed in {@linkplain JsonReader#setLenient(boolean) lenient mode},
|
||||||
|
* regardless of the lenient mode setting of the provided reader. The lenient mode setting
|
||||||
|
* of the reader is restored once this method returns.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the desired object
|
||||||
|
* @param reader the reader whose next JSON value should be deserialized
|
||||||
|
* @param typeOfT The specific genericized type of src. You should create an anonymous subclass of
|
||||||
|
* {@code TypeToken} with the specific generic type arguments. For example, to get the type for
|
||||||
|
* {@code Collection<Foo>}, you should use:
|
||||||
|
* <pre>
|
||||||
|
* new TypeToken<Collection<Foo>>(){}
|
||||||
|
* </pre>
|
||||||
|
* @return an object of type T from the JsonReader. Returns {@code null} if {@code reader} is at EOF.
|
||||||
|
* @throws JsonIOException if there was a problem reading from the JsonReader
|
||||||
|
* @throws JsonSyntaxException if json is not a valid representation for an object of the type typeOfT
|
||||||
|
*
|
||||||
|
* @see #fromJson(Reader, TypeToken)
|
||||||
|
* @see #fromJson(JsonReader, Type)
|
||||||
|
* @since 2.10
|
||||||
|
*/
|
||||||
|
public <T> T fromJson(JsonReader reader, TypeToken<T> typeOfT) throws JsonIOException, JsonSyntaxException {
|
||||||
boolean isEmpty = true;
|
boolean isEmpty = true;
|
||||||
boolean oldLenient = reader.isLenient();
|
boolean oldLenient = reader.isLenient();
|
||||||
reader.setLenient(lenient);
|
reader.setLenient(lenient);
|
||||||
try {
|
try {
|
||||||
reader.peek();
|
reader.peek();
|
||||||
isEmpty = false;
|
isEmpty = false;
|
||||||
@SuppressWarnings("unchecked")
|
TypeAdapter<T> typeAdapter = getAdapter(typeOfT);
|
||||||
TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
|
|
||||||
TypeAdapter<T> typeAdapter = getAdapter(typeToken);
|
|
||||||
T object = typeAdapter.read(reader);
|
T object = typeAdapter.read(reader);
|
||||||
return object;
|
return object;
|
||||||
} catch (EOFException e) {
|
} catch (EOFException e) {
|
||||||
@ -1074,55 +1222,89 @@ public final class Gson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method deserializes the Json read from the specified parse tree into an object of the
|
* This method deserializes the JSON read from the specified parse tree into an object of the
|
||||||
* specified type. It is not suitable to use if the specified class is a generic type since it
|
* specified type. It is not suitable to use if the specified class is a generic type since it
|
||||||
* will not have the generic type information because of the Type Erasure feature of Java.
|
* will not have the generic type information because of the Type Erasure feature of Java.
|
||||||
* Therefore, this method should not be used if the desired type is a generic type. Note that
|
* Therefore, this method should not be used if the desired type is a generic type. Note that
|
||||||
* this method works fine if the any of the fields of the specified object are generics, just the
|
* this method works fine if any of the fields of the specified object are generics, just the
|
||||||
* object itself should not be a generic type. For the cases when the object is of generic type,
|
* object itself should not be a generic type. For the cases when the object is of generic type,
|
||||||
* invoke {@link #fromJson(JsonElement, Type)}.
|
* invoke {@link #fromJson(JsonElement, TypeToken)}.
|
||||||
|
*
|
||||||
* @param <T> the type of the desired object
|
* @param <T> the type of the desired object
|
||||||
* @param json the root of the parse tree of {@link JsonElement}s from which the object is to
|
* @param json the root of the parse tree of {@link JsonElement}s from which the object is to
|
||||||
* be deserialized
|
* be deserialized
|
||||||
* @param classOfT The class of T
|
* @param classOfT The class of T
|
||||||
* @return an object of type T from the json. Returns {@code null} if {@code json} is {@code null}
|
* @return an object of type T from the JSON. Returns {@code null} if {@code json} is {@code null}
|
||||||
* or if {@code json} is empty.
|
* or if {@code json} is empty.
|
||||||
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
|
* @throws JsonSyntaxException if json is not a valid representation for an object of type classOfT
|
||||||
* @since 1.3
|
* @since 1.3
|
||||||
|
*
|
||||||
|
* @see #fromJson(Reader, Class)
|
||||||
|
* @see #fromJson(JsonElement, TypeToken)
|
||||||
*/
|
*/
|
||||||
public <T> T fromJson(JsonElement json, Class<T> classOfT) throws JsonSyntaxException {
|
public <T> T fromJson(JsonElement json, Class<T> classOfT) throws JsonSyntaxException {
|
||||||
Object object = fromJson(json, (Type) classOfT);
|
T object = fromJson(json, TypeToken.get(classOfT));
|
||||||
return Primitives.wrap(classOfT).cast(object);
|
return Primitives.wrap(classOfT).cast(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method deserializes the Json read from the specified parse tree into an object of the
|
* This method deserializes the JSON read from the specified parse tree into an object of the
|
||||||
|
* specified type. This method is useful if the specified object is a generic type. For
|
||||||
|
* non-generic objects, use {@link #fromJson(JsonElement, Class)} instead.
|
||||||
|
*
|
||||||
|
* <p>Since {@code Type} is not parameterized by T, this method is not type-safe and
|
||||||
|
* should be used carefully. If you are creating the {@code Type} from a {@link TypeToken},
|
||||||
|
* prefer using {@link #fromJson(JsonElement, TypeToken)} instead since its return type is based
|
||||||
|
* on the {@code TypeToken} and is therefore more type-safe.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the desired object
|
||||||
|
* @param json the root of the parse tree of {@link JsonElement}s from which the object is to
|
||||||
|
* be deserialized
|
||||||
|
* @param typeOfT The specific genericized type of src
|
||||||
|
* @return an object of type T from the JSON. Returns {@code null} if {@code json} is {@code null}
|
||||||
|
* or if {@code json} is empty.
|
||||||
|
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
|
||||||
|
* @since 1.3
|
||||||
|
*
|
||||||
|
* @see #fromJson(Reader, Type)
|
||||||
|
* @see #fromJson(JsonElement, Class)
|
||||||
|
* @see #fromJson(JsonElement, TypeToken)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException {
|
||||||
|
return (T) fromJson(json, TypeToken.get(typeOfT));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method deserializes the JSON read from the specified parse tree into an object of the
|
||||||
* specified type. This method is useful if the specified object is a generic type. For
|
* specified type. This method is useful if the specified object is a generic type. For
|
||||||
* non-generic objects, use {@link #fromJson(JsonElement, Class)} instead.
|
* non-generic objects, use {@link #fromJson(JsonElement, Class)} instead.
|
||||||
*
|
*
|
||||||
* @param <T> the type of the desired object
|
* @param <T> the type of the desired object
|
||||||
* @param json the root of the parse tree of {@link JsonElement}s from which the object is to
|
* @param json the root of the parse tree of {@link JsonElement}s from which the object is to
|
||||||
* be deserialized
|
* be deserialized
|
||||||
* @param typeOfT The specific genericized type of src. You can obtain this type by using the
|
* @param typeOfT The specific genericized type of src. You should create an anonymous subclass of
|
||||||
* {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for
|
* {@code TypeToken} with the specific generic type arguments. For example, to get the type for
|
||||||
* {@code Collection<Foo>}, you should use:
|
* {@code Collection<Foo>}, you should use:
|
||||||
* <pre>
|
* <pre>
|
||||||
* Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
|
* new TypeToken<Collection<Foo>>(){}
|
||||||
* </pre>
|
* </pre>
|
||||||
* @return an object of type T from the json. Returns {@code null} if {@code json} is {@code null}
|
* @return an object of type T from the JSON. Returns {@code null} if {@code json} is {@code null}
|
||||||
* or if {@code json} is empty.
|
* or if {@code json} is empty.
|
||||||
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
|
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
|
||||||
* @since 1.3
|
*
|
||||||
|
* @see #fromJson(Reader, TypeToken)
|
||||||
|
* @see #fromJson(JsonElement, Class)
|
||||||
|
* @since 2.10
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
public <T> T fromJson(JsonElement json, TypeToken<T> typeOfT) throws JsonSyntaxException {
|
||||||
public <T> T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException {
|
|
||||||
if (json == null) {
|
if (json == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (T) fromJson(new JsonTreeReader(json), typeOfT);
|
return fromJson(new JsonTreeReader(json), typeOfT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static class FutureTypeAdapter<T> extends TypeAdapter<T> {
|
static class FutureTypeAdapter<T> extends SerializationDelegatingTypeAdapter<T> {
|
||||||
private TypeAdapter<T> delegate;
|
private TypeAdapter<T> delegate;
|
||||||
|
|
||||||
public void setDelegate(TypeAdapter<T> typeAdapter) {
|
public void setDelegate(TypeAdapter<T> typeAdapter) {
|
||||||
@ -1132,18 +1314,23 @@ public final class Gson {
|
|||||||
delegate = typeAdapter;
|
delegate = typeAdapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public T read(JsonReader in) throws IOException {
|
private TypeAdapter<T> delegate() {
|
||||||
if (delegate == null) {
|
if (delegate == null) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException("Delegate has not been set yet");
|
||||||
}
|
}
|
||||||
return delegate.read(in);
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public TypeAdapter<T> getSerializationDelegate() {
|
||||||
|
return delegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public T read(JsonReader in) throws IOException {
|
||||||
|
return delegate().read(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void write(JsonWriter out, T value) throws IOException {
|
@Override public void write(JsonWriter out, T value) throws IOException {
|
||||||
if (delegate == null) {
|
delegate().write(out, value);
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
delegate.write(out, value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ package com.google.gson;
|
|||||||
|
|
||||||
import static com.google.gson.internal.DefaultConfig.*;
|
import static com.google.gson.internal.DefaultConfig.*;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Since;
|
||||||
|
import com.google.gson.annotations.Until;
|
||||||
import com.google.gson.internal.$Gson$Preconditions;
|
import com.google.gson.internal.$Gson$Preconditions;
|
||||||
import com.google.gson.internal.Excluder;
|
import com.google.gson.internal.Excluder;
|
||||||
import com.google.gson.internal.bind.DefaultDateTypeAdapter;
|
import com.google.gson.internal.bind.DefaultDateTypeAdapter;
|
||||||
@ -137,14 +139,25 @@ public final class GsonBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures Gson to enable versioning support.
|
* Configures Gson to enable versioning support. Versioning support works based on the
|
||||||
|
* annotation types {@link Since} and {@link Until}. It allows including or excluding fields
|
||||||
|
* and classes based on the specified version. See the documentation of these annotation
|
||||||
|
* types for more information.
|
||||||
*
|
*
|
||||||
* @param ignoreVersionsAfter any field or type marked with a version higher than this value
|
* <p>By default versioning support is disabled and usage of {@code @Since} and {@code @Until}
|
||||||
* are ignored during serialization or deserialization.
|
* has no effect.
|
||||||
|
*
|
||||||
|
* @param version the version number to use.
|
||||||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
* @throws IllegalArgumentException if the version number is NaN or negative
|
||||||
|
* @see Since
|
||||||
|
* @see Until
|
||||||
*/
|
*/
|
||||||
public GsonBuilder setVersion(double ignoreVersionsAfter) {
|
public GsonBuilder setVersion(double version) {
|
||||||
excluder = excluder.withVersion(ignoreVersionsAfter);
|
if (Double.isNaN(version) || version < 0.0) {
|
||||||
|
throw new IllegalArgumentException("Invalid version: " + version);
|
||||||
|
}
|
||||||
|
excluder = excluder.withVersion(version);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,8 +228,9 @@ public final class GsonBuilder {
|
|||||||
* on the key; however, when this is called then one of the following cases
|
* on the key; however, when this is called then one of the following cases
|
||||||
* apply:
|
* apply:
|
||||||
*
|
*
|
||||||
* <h3>Maps as JSON objects</h3>
|
* <p><b>Maps as JSON objects</b>
|
||||||
* For this case, assume that a type adapter is registered to serialize and
|
*
|
||||||
|
* <p>For this case, assume that a type adapter is registered to serialize and
|
||||||
* deserialize some {@code Point} class, which contains an x and y coordinate,
|
* deserialize some {@code Point} class, which contains an x and y coordinate,
|
||||||
* to/from the JSON Primitive string value {@code "(x,y)"}. The Java map would
|
* to/from the JSON Primitive string value {@code "(x,y)"}. The Java map would
|
||||||
* then be serialized as a {@link JsonObject}.
|
* then be serialized as a {@link JsonObject}.
|
||||||
@ -240,11 +254,12 @@ public final class GsonBuilder {
|
|||||||
* }
|
* }
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <h3>Maps as JSON arrays</h3>
|
* <p><b>Maps as JSON arrays</b>
|
||||||
* For this case, assume that a type adapter was NOT registered for some
|
*
|
||||||
|
* <p>For this case, assume that a type adapter was NOT registered for some
|
||||||
* {@code Point} class, but rather the default Gson serialization is applied.
|
* {@code Point} class, but rather the default Gson serialization is applied.
|
||||||
* In this case, some {@code new Point(2,3)} would serialize as {@code
|
* In this case, some {@code new Point(2,3)} would serialize as {@code
|
||||||
* {"x":2,"y":5}}.
|
* {"x":2,"y":3}}.
|
||||||
*
|
*
|
||||||
* <p>Given the assumption above, a {@code Map<Point, String>} will be
|
* <p>Given the assumption above, a {@code Map<Point, String>} will be
|
||||||
* serialize as an array of arrays (can be viewed as an entry set of pairs).
|
* serialize as an array of arrays (can be viewed as an entry set of pairs).
|
||||||
@ -357,6 +372,10 @@ public final class GsonBuilder {
|
|||||||
* Configures Gson to apply a specific naming strategy to an object's fields during
|
* Configures Gson to apply a specific naming strategy to an object's fields during
|
||||||
* serialization and deserialization.
|
* serialization and deserialization.
|
||||||
*
|
*
|
||||||
|
* <p>The created Gson instance might only use the field naming strategy once for a
|
||||||
|
* field and cache the result. It is not guaranteed that the strategy will be used
|
||||||
|
* again every time the value of a field is serialized or deserialized.
|
||||||
|
*
|
||||||
* @param fieldNamingStrategy the naming strategy to apply to the fields
|
* @param fieldNamingStrategy the naming strategy to apply to the fields
|
||||||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
||||||
* @since 1.3
|
* @since 1.3
|
||||||
@ -372,6 +391,7 @@ public final class GsonBuilder {
|
|||||||
* @param objectToNumberStrategy the actual object-to-number strategy
|
* @param objectToNumberStrategy the actual object-to-number strategy
|
||||||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
||||||
* @see ToNumberPolicy#DOUBLE The default object-to-number strategy
|
* @see ToNumberPolicy#DOUBLE The default object-to-number strategy
|
||||||
|
* @since 2.8.9
|
||||||
*/
|
*/
|
||||||
public GsonBuilder setObjectToNumberStrategy(ToNumberStrategy objectToNumberStrategy) {
|
public GsonBuilder setObjectToNumberStrategy(ToNumberStrategy objectToNumberStrategy) {
|
||||||
this.objectToNumberStrategy = Objects.requireNonNull(objectToNumberStrategy);
|
this.objectToNumberStrategy = Objects.requireNonNull(objectToNumberStrategy);
|
||||||
@ -384,6 +404,7 @@ public final class GsonBuilder {
|
|||||||
* @param numberToNumberStrategy the actual number-to-number strategy
|
* @param numberToNumberStrategy the actual number-to-number strategy
|
||||||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
||||||
* @see ToNumberPolicy#LAZILY_PARSED_NUMBER The default number-to-number strategy
|
* @see ToNumberPolicy#LAZILY_PARSED_NUMBER The default number-to-number strategy
|
||||||
|
* @since 2.8.9
|
||||||
*/
|
*/
|
||||||
public GsonBuilder setNumberToNumberStrategy(ToNumberStrategy numberToNumberStrategy) {
|
public GsonBuilder setNumberToNumberStrategy(ToNumberStrategy numberToNumberStrategy) {
|
||||||
this.numberToNumberStrategy = Objects.requireNonNull(numberToNumberStrategy);
|
this.numberToNumberStrategy = Objects.requireNonNull(numberToNumberStrategy);
|
||||||
@ -408,6 +429,10 @@ public final class GsonBuilder {
|
|||||||
* JSON null is written to output, and when deserialized the JSON value is skipped and
|
* JSON null is written to output, and when deserialized the JSON value is skipped and
|
||||||
* {@code null} is returned.
|
* {@code null} is returned.
|
||||||
*
|
*
|
||||||
|
* <p>The created Gson instance might only use an exclusion strategy once for a field or
|
||||||
|
* class and cache the result. It is not guaranteed that the strategy will be used again
|
||||||
|
* every time the value of a field or a class is serialized or deserialized.
|
||||||
|
*
|
||||||
* @param strategies the set of strategy object to apply during object (de)serialization.
|
* @param strategies the set of strategy object to apply during object (de)serialization.
|
||||||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
||||||
* @since 1.4
|
* @since 1.4
|
||||||
@ -622,6 +647,10 @@ public final class GsonBuilder {
|
|||||||
* is designed to handle a large number of factories, so you should consider registering
|
* is designed to handle a large number of factories, so you should consider registering
|
||||||
* them to be at par with registering an individual type adapter.
|
* them to be at par with registering an individual type adapter.
|
||||||
*
|
*
|
||||||
|
* <p>The created Gson instance might only use the factory once to create an adapter for
|
||||||
|
* a specific type and cache the result. It is not guaranteed that the factory will be used
|
||||||
|
* again every time the type is serialized or deserialized.
|
||||||
|
*
|
||||||
* @since 2.1
|
* @since 2.1
|
||||||
*/
|
*/
|
||||||
public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) {
|
public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) {
|
||||||
@ -699,6 +728,7 @@ public final class GsonBuilder {
|
|||||||
* disabling usage of {@code Unsafe}.
|
* disabling usage of {@code Unsafe}.
|
||||||
*
|
*
|
||||||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
* @since 2.9.0
|
||||||
*/
|
*/
|
||||||
public GsonBuilder disableJdkUnsafe() {
|
public GsonBuilder disableJdkUnsafe() {
|
||||||
this.useJdkUnsafe = false;
|
this.useJdkUnsafe = false;
|
||||||
@ -717,8 +747,13 @@ public final class GsonBuilder {
|
|||||||
* all classes for which no {@link TypeAdapter} has been registered, and for which no
|
* all classes for which no {@link TypeAdapter} has been registered, and for which no
|
||||||
* built-in Gson {@code TypeAdapter} exists.
|
* built-in Gson {@code TypeAdapter} exists.
|
||||||
*
|
*
|
||||||
|
* <p>The created Gson instance might only use an access filter once for a class or its
|
||||||
|
* members and cache the result. It is not guaranteed that the filter will be used again
|
||||||
|
* every time a class or its members are accessed during serialization or deserialization.
|
||||||
|
*
|
||||||
* @param filter filter to add
|
* @param filter filter to add
|
||||||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
* @since 2.9.1
|
||||||
*/
|
*/
|
||||||
public GsonBuilder addReflectionAccessFilter(ReflectionAccessFilter filter) {
|
public GsonBuilder addReflectionAccessFilter(ReflectionAccessFilter filter) {
|
||||||
Objects.requireNonNull(filter);
|
Objects.requireNonNull(filter);
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.google.gson;
|
package com.google.gson;
|
||||||
|
|
||||||
|
import com.google.gson.internal.NonNullElementWrapperList;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -28,11 +29,14 @@ import java.util.List;
|
|||||||
* elements are added is preserved. This class does not support {@code null} values. If {@code null}
|
* elements are added is preserved. This class does not support {@code null} values. If {@code null}
|
||||||
* is provided as element argument to any of the methods, it is converted to a {@link JsonNull}.
|
* is provided as element argument to any of the methods, it is converted to a {@link JsonNull}.
|
||||||
*
|
*
|
||||||
|
* <p>{@code JsonArray} only implements the {@link Iterable} interface but not the {@link List}
|
||||||
|
* interface. A {@code List} view of it can be obtained with {@link #asList()}.
|
||||||
|
*
|
||||||
* @author Inderjeet Singh
|
* @author Inderjeet Singh
|
||||||
* @author Joel Leitch
|
* @author Joel Leitch
|
||||||
*/
|
*/
|
||||||
public final class JsonArray extends JsonElement implements Iterable<JsonElement> {
|
public final class JsonArray extends JsonElement implements Iterable<JsonElement> {
|
||||||
private final List<JsonElement> elements;
|
private final ArrayList<JsonElement> elements;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an empty JsonArray.
|
* Creates an empty JsonArray.
|
||||||
@ -48,6 +52,7 @@ public final class JsonArray extends JsonElement implements Iterable<JsonElement
|
|||||||
* @param capacity initial capacity.
|
* @param capacity initial capacity.
|
||||||
* @throws IllegalArgumentException if the {@code capacity} is
|
* @throws IllegalArgumentException if the {@code capacity} is
|
||||||
* negative
|
* negative
|
||||||
|
* @since 2.8.1
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation") // superclass constructor
|
@SuppressWarnings("deprecation") // superclass constructor
|
||||||
public JsonArray(int capacity) {
|
public JsonArray(int capacity) {
|
||||||
@ -75,6 +80,7 @@ public final class JsonArray extends JsonElement implements Iterable<JsonElement
|
|||||||
* Adds the specified boolean to self.
|
* Adds the specified boolean to self.
|
||||||
*
|
*
|
||||||
* @param bool the boolean that needs to be added to the array.
|
* @param bool the boolean that needs to be added to the array.
|
||||||
|
* @since 2.4
|
||||||
*/
|
*/
|
||||||
public void add(Boolean bool) {
|
public void add(Boolean bool) {
|
||||||
elements.add(bool == null ? JsonNull.INSTANCE : new JsonPrimitive(bool));
|
elements.add(bool == null ? JsonNull.INSTANCE : new JsonPrimitive(bool));
|
||||||
@ -84,6 +90,7 @@ public final class JsonArray extends JsonElement implements Iterable<JsonElement
|
|||||||
* Adds the specified character to self.
|
* Adds the specified character to self.
|
||||||
*
|
*
|
||||||
* @param character the character that needs to be added to the array.
|
* @param character the character that needs to be added to the array.
|
||||||
|
* @since 2.4
|
||||||
*/
|
*/
|
||||||
public void add(Character character) {
|
public void add(Character character) {
|
||||||
elements.add(character == null ? JsonNull.INSTANCE : new JsonPrimitive(character));
|
elements.add(character == null ? JsonNull.INSTANCE : new JsonPrimitive(character));
|
||||||
@ -93,6 +100,7 @@ public final class JsonArray extends JsonElement implements Iterable<JsonElement
|
|||||||
* Adds the specified number to self.
|
* Adds the specified number to self.
|
||||||
*
|
*
|
||||||
* @param number the number that needs to be added to the array.
|
* @param number the number that needs to be added to the array.
|
||||||
|
* @since 2.4
|
||||||
*/
|
*/
|
||||||
public void add(Number number) {
|
public void add(Number number) {
|
||||||
elements.add(number == null ? JsonNull.INSTANCE : new JsonPrimitive(number));
|
elements.add(number == null ? JsonNull.INSTANCE : new JsonPrimitive(number));
|
||||||
@ -102,6 +110,7 @@ public final class JsonArray extends JsonElement implements Iterable<JsonElement
|
|||||||
* Adds the specified string to self.
|
* Adds the specified string to self.
|
||||||
*
|
*
|
||||||
* @param string the string that needs to be added to the array.
|
* @param string the string that needs to be added to the array.
|
||||||
|
* @since 2.4
|
||||||
*/
|
*/
|
||||||
public void add(String string) {
|
public void add(String string) {
|
||||||
elements.add(string == null ? JsonNull.INSTANCE : new JsonPrimitive(string));
|
elements.add(string == null ? JsonNull.INSTANCE : new JsonPrimitive(string));
|
||||||
@ -190,6 +199,7 @@ public final class JsonArray extends JsonElement implements Iterable<JsonElement
|
|||||||
* Returns true if the array is empty.
|
* Returns true if the array is empty.
|
||||||
*
|
*
|
||||||
* @return true if the array is empty.
|
* @return true if the array is empty.
|
||||||
|
* @since 2.8.7
|
||||||
*/
|
*/
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return elements.isEmpty();
|
return elements.isEmpty();
|
||||||
@ -387,6 +397,21 @@ public final class JsonArray extends JsonElement implements Iterable<JsonElement
|
|||||||
return getAsSingleElement().getAsBoolean();
|
return getAsSingleElement().getAsBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a mutable {@link List} view of this {@code JsonArray}. Changes to the {@code List}
|
||||||
|
* are visible in this {@code JsonArray} and the other way around.
|
||||||
|
*
|
||||||
|
* <p>The {@code List} does not permit {@code null} elements. Unlike {@code JsonArray}'s
|
||||||
|
* {@code null} handling, a {@link NullPointerException} is thrown when trying to add {@code null}.
|
||||||
|
* Use {@link JsonNull} for JSON null values.
|
||||||
|
*
|
||||||
|
* @return mutable {@code List} view
|
||||||
|
* @since 2.10
|
||||||
|
*/
|
||||||
|
public List<JsonElement> asList() {
|
||||||
|
return new NonNullElementWrapperList<>(elements);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the other object is equal to this. This method only considers
|
* Returns whether the other object is equal to this. This method only considers
|
||||||
* the other object to be equal if it is an instance of {@code JsonArray} and has
|
* the other object to be equal if it is an instance of {@code JsonArray} and has
|
||||||
|
@ -27,6 +27,9 @@ import java.util.Set;
|
|||||||
* This class does not support {@code null} values. If {@code null} is provided as value argument
|
* This class does not support {@code null} values. If {@code null} is provided as value argument
|
||||||
* to any of the methods, it is converted to a {@link JsonNull}.
|
* to any of the methods, it is converted to a {@link JsonNull}.
|
||||||
*
|
*
|
||||||
|
* <p>{@code JsonObject} does not implement the {@link Map} interface, but a {@code Map} view
|
||||||
|
* of it can be obtained with {@link #asMap()}.
|
||||||
|
*
|
||||||
* @author Inderjeet Singh
|
* @author Inderjeet Singh
|
||||||
* @author Joel Leitch
|
* @author Joel Leitch
|
||||||
*/
|
*/
|
||||||
@ -146,6 +149,7 @@ public final class JsonObject extends JsonElement {
|
|||||||
* Returns the number of key/value pairs in the object.
|
* Returns the number of key/value pairs in the object.
|
||||||
*
|
*
|
||||||
* @return the number of key/value pairs in the object.
|
* @return the number of key/value pairs in the object.
|
||||||
|
* @since 2.7
|
||||||
*/
|
*/
|
||||||
public int size() {
|
public int size() {
|
||||||
return members.size();
|
return members.size();
|
||||||
@ -207,6 +211,22 @@ public final class JsonObject extends JsonElement {
|
|||||||
return (JsonObject) members.get(memberName);
|
return (JsonObject) members.get(memberName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a mutable {@link Map} view of this {@code JsonObject}. Changes to the {@code Map}
|
||||||
|
* are visible in this {@code JsonObject} and the other way around.
|
||||||
|
*
|
||||||
|
* <p>The {@code Map} does not permit {@code null} keys or values. Unlike {@code JsonObject}'s
|
||||||
|
* {@code null} handling, a {@link NullPointerException} is thrown when trying to add {@code null}.
|
||||||
|
* Use {@link JsonNull} for JSON null values.
|
||||||
|
*
|
||||||
|
* @return mutable {@code Map} view
|
||||||
|
* @since 2.10
|
||||||
|
*/
|
||||||
|
public Map<String, JsonElement> asMap() {
|
||||||
|
// It is safe to expose the underlying map because it disallows null keys and values
|
||||||
|
return members;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the other object is equal to this. This method only considers
|
* Returns whether the other object is equal to this. This method only considers
|
||||||
* the other object to be equal if it is an instance of {@code JsonObject} and has
|
* the other object to be equal if it is an instance of {@code JsonObject} and has
|
||||||
|
@ -45,6 +45,7 @@ public final class JsonParser {
|
|||||||
* @param json JSON text
|
* @param json JSON text
|
||||||
* @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
|
* @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
|
||||||
* @throws JsonParseException if the specified text is not valid JSON
|
* @throws JsonParseException if the specified text is not valid JSON
|
||||||
|
* @since 2.8.6
|
||||||
*/
|
*/
|
||||||
public static JsonElement parseString(String json) throws JsonSyntaxException {
|
public static JsonElement parseString(String json) throws JsonSyntaxException {
|
||||||
return parseReader(new StringReader(json));
|
return parseReader(new StringReader(json));
|
||||||
@ -61,6 +62,7 @@ public final class JsonParser {
|
|||||||
* @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
|
* @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
|
||||||
* @throws JsonParseException if there is an IOException or if the specified
|
* @throws JsonParseException if there is an IOException or if the specified
|
||||||
* text is not valid JSON
|
* text is not valid JSON
|
||||||
|
* @since 2.8.6
|
||||||
*/
|
*/
|
||||||
public static JsonElement parseReader(Reader reader) throws JsonIOException, JsonSyntaxException {
|
public static JsonElement parseReader(Reader reader) throws JsonIOException, JsonSyntaxException {
|
||||||
try {
|
try {
|
||||||
@ -90,6 +92,7 @@ public final class JsonParser {
|
|||||||
*
|
*
|
||||||
* @throws JsonParseException if there is an IOException or if the specified
|
* @throws JsonParseException if there is an IOException or if the specified
|
||||||
* text is not valid JSON
|
* text is not valid JSON
|
||||||
|
* @since 2.8.6
|
||||||
*/
|
*/
|
||||||
public static JsonElement parseReader(JsonReader reader)
|
public static JsonElement parseReader(JsonReader reader)
|
||||||
throws JsonIOException, JsonSyntaxException {
|
throws JsonIOException, JsonSyntaxException {
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package com.google.gson;
|
package com.google.gson;
|
||||||
|
|
||||||
import java.lang.reflect.AccessibleObject;
|
|
||||||
|
|
||||||
import com.google.gson.internal.ReflectionAccessFilterHelper;
|
import com.google.gson.internal.ReflectionAccessFilterHelper;
|
||||||
|
import java.lang.reflect.AccessibleObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter for determining whether reflection based serialization and
|
* Filter for determining whether reflection based serialization and
|
||||||
@ -28,10 +27,13 @@ import com.google.gson.internal.ReflectionAccessFilterHelper;
|
|||||||
* fields and classes.
|
* fields and classes.
|
||||||
*
|
*
|
||||||
* @see GsonBuilder#addReflectionAccessFilter(ReflectionAccessFilter)
|
* @see GsonBuilder#addReflectionAccessFilter(ReflectionAccessFilter)
|
||||||
|
* @since 2.9.1
|
||||||
*/
|
*/
|
||||||
public interface ReflectionAccessFilter {
|
public interface ReflectionAccessFilter {
|
||||||
/**
|
/**
|
||||||
* Result of a filter check.
|
* Result of a filter check.
|
||||||
|
*
|
||||||
|
* @since 2.9.1
|
||||||
*/
|
*/
|
||||||
enum FilterResult {
|
enum FilterResult {
|
||||||
/**
|
/**
|
||||||
|
@ -16,12 +16,11 @@
|
|||||||
|
|
||||||
package com.google.gson;
|
package com.google.gson;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import com.google.gson.internal.LazilyParsedNumber;
|
import com.google.gson.internal.LazilyParsedNumber;
|
||||||
import com.google.gson.stream.JsonReader;
|
import com.google.gson.stream.JsonReader;
|
||||||
import com.google.gson.stream.MalformedJsonException;
|
import com.google.gson.stream.MalformedJsonException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An enumeration that defines two standard number reading strategies and a couple of
|
* An enumeration that defines two standard number reading strategies and a couple of
|
||||||
@ -29,6 +28,7 @@ import com.google.gson.stream.MalformedJsonException;
|
|||||||
* {@link Object} and {@link Number}.
|
* {@link Object} and {@link Number}.
|
||||||
*
|
*
|
||||||
* @see ToNumberStrategy
|
* @see ToNumberStrategy
|
||||||
|
* @since 2.8.9
|
||||||
*/
|
*/
|
||||||
public enum ToNumberPolicy implements ToNumberStrategy {
|
public enum ToNumberPolicy implements ToNumberStrategy {
|
||||||
|
|
||||||
|
@ -16,9 +16,8 @@
|
|||||||
|
|
||||||
package com.google.gson;
|
package com.google.gson;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.google.gson.stream.JsonReader;
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A strategy that is used to control how numbers should be deserialized for {@link Object} and {@link Number}
|
* A strategy that is used to control how numbers should be deserialized for {@link Object} and {@link Number}
|
||||||
@ -56,6 +55,7 @@ import com.google.gson.stream.JsonReader;
|
|||||||
* @see ToNumberPolicy
|
* @see ToNumberPolicy
|
||||||
* @see GsonBuilder#setObjectToNumberStrategy(ToNumberStrategy)
|
* @see GsonBuilder#setObjectToNumberStrategy(ToNumberStrategy)
|
||||||
* @see GsonBuilder#setNumberToNumberStrategy(ToNumberStrategy)
|
* @see GsonBuilder#setNumberToNumberStrategy(ToNumberStrategy)
|
||||||
|
* @since 2.8.9
|
||||||
*/
|
*/
|
||||||
public interface ToNumberStrategy {
|
public interface ToNumberStrategy {
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ import java.io.Writer;
|
|||||||
/**
|
/**
|
||||||
* Converts Java objects to and from JSON.
|
* Converts Java objects to and from JSON.
|
||||||
*
|
*
|
||||||
* <h3>Defining a type's JSON form</h3>
|
* <h2>Defining a type's JSON form</h2>
|
||||||
* By default Gson converts application classes to JSON using its built-in type
|
* By default Gson converts application classes to JSON using its built-in type
|
||||||
* adapters. If Gson's default JSON conversion isn't appropriate for a type,
|
* adapters. If Gson's default JSON conversion isn't appropriate for a type,
|
||||||
* extend this class to customize the conversion. Here's an example of a type
|
* extend this class to customize the conversion. Here's an example of a type
|
||||||
@ -96,7 +96,7 @@ import java.io.Writer;
|
|||||||
*/
|
*/
|
||||||
// non-Javadoc:
|
// non-Javadoc:
|
||||||
//
|
//
|
||||||
// <h3>JSON Conversion</h3>
|
// <h2>JSON Conversion</h2>
|
||||||
// <p>A type adapter registered with Gson is automatically invoked while serializing
|
// <p>A type adapter registered with Gson is automatically invoked while serializing
|
||||||
// or deserializing JSON. However, you can also use type adapters directly to serialize
|
// or deserializing JSON. However, you can also use type adapters directly to serialize
|
||||||
// and deserialize JSON. Here is an example for deserialization: <pre> {@code
|
// and deserialize JSON. Here is an example for deserialization: <pre> {@code
|
||||||
@ -118,6 +118,9 @@ import java.io.Writer;
|
|||||||
//
|
//
|
||||||
public abstract class TypeAdapter<T> {
|
public abstract class TypeAdapter<T> {
|
||||||
|
|
||||||
|
public TypeAdapter() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes one JSON value (an array, object, string, number, boolean or null)
|
* Writes one JSON value (an array, object, string, number, boolean or null)
|
||||||
* for {@code value}.
|
* for {@code value}.
|
||||||
|
@ -22,6 +22,7 @@ import com.google.gson.reflect.TypeToken;
|
|||||||
* Creates type adapters for set of related types. Type adapter factories are
|
* Creates type adapters for set of related types. Type adapter factories are
|
||||||
* most useful when several types share similar structure in their JSON form.
|
* most useful when several types share similar structure in their JSON form.
|
||||||
*
|
*
|
||||||
|
* <h2>Examples</h2>
|
||||||
* <h3>Example: Converting enums to lowercase</h3>
|
* <h3>Example: Converting enums to lowercase</h3>
|
||||||
* In this example, we implement a factory that creates type adapters for all
|
* In this example, we implement a factory that creates type adapters for all
|
||||||
* enums. The type adapters will write enums in lowercase, despite the fact
|
* enums. The type adapters will write enums in lowercase, despite the fact
|
||||||
@ -90,7 +91,7 @@ import com.google.gson.reflect.TypeToken;
|
|||||||
* If multiple factories support the same type, the factory registered earlier
|
* If multiple factories support the same type, the factory registered earlier
|
||||||
* takes precedence.
|
* takes precedence.
|
||||||
*
|
*
|
||||||
* <h3>Example: composing other type adapters</h3>
|
* <h3>Example: Composing other type adapters</h3>
|
||||||
* In this example we implement a factory for Guava's {@code Multiset}
|
* In this example we implement a factory for Guava's {@code Multiset}
|
||||||
* collection type. The factory can be used to create type adapters for
|
* collection type. The factory can be used to create type adapters for
|
||||||
* multisets of any element type: the type adapter for {@code
|
* multisets of any element type: the type adapter for {@code
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.google.gson.annotations;
|
package com.google.gson.annotations;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonDeserializer;
|
import com.google.gson.JsonDeserializer;
|
||||||
import com.google.gson.JsonSerializer;
|
import com.google.gson.JsonSerializer;
|
||||||
import com.google.gson.TypeAdapter;
|
import com.google.gson.TypeAdapter;
|
||||||
@ -60,7 +61,7 @@ import java.lang.annotation.Target;
|
|||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* Since User class specified UserJsonAdapter.class in @JsonAdapter annotation, it
|
* Since User class specified UserJsonAdapter.class in @JsonAdapter annotation, it
|
||||||
* will automatically be invoked to serialize/deserialize User instances. <br>
|
* will automatically be invoked to serialize/deserialize User instances.
|
||||||
*
|
*
|
||||||
* <p> Here is an example of how to apply this annotation to a field.
|
* <p> Here is an example of how to apply this annotation to a field.
|
||||||
* <pre>
|
* <pre>
|
||||||
@ -82,7 +83,12 @@ import java.lang.annotation.Target;
|
|||||||
* TypeAdapter} or a {@link TypeAdapterFactory}, or must implement one
|
* TypeAdapter} or a {@link TypeAdapterFactory}, or must implement one
|
||||||
* or both of {@link JsonDeserializer} or {@link JsonSerializer}.
|
* or both of {@link JsonDeserializer} or {@link JsonSerializer}.
|
||||||
* Using {@link TypeAdapterFactory} makes it possible to delegate
|
* Using {@link TypeAdapterFactory} makes it possible to delegate
|
||||||
* to the enclosing {@code Gson} instance.
|
* to the enclosing {@link Gson} instance.
|
||||||
|
*
|
||||||
|
* <p>{@code Gson} instances might cache the adapter they create for
|
||||||
|
* a {@code @JsonAdapter} annotation. It is not guaranteed that a new
|
||||||
|
* adapter is created every time the annotated class or field is serialized
|
||||||
|
* or deserialized.
|
||||||
*
|
*
|
||||||
* @since 2.3
|
* @since 2.3
|
||||||
*
|
*
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.google.gson.annotations;
|
package com.google.gson.annotations;
|
||||||
|
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
@ -24,12 +25,11 @@ import java.lang.annotation.Target;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An annotation that indicates the version number since a member or a type has been present.
|
* An annotation that indicates the version number since a member or a type has been present.
|
||||||
* This annotation is useful to manage versioning of your Json classes for a web-service.
|
* This annotation is useful to manage versioning of your JSON classes for a web-service.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This annotation has no effect unless you build {@link com.google.gson.Gson} with a
|
* This annotation has no effect unless you build {@link com.google.gson.Gson} with a
|
||||||
* {@link com.google.gson.GsonBuilder} and invoke
|
* {@code GsonBuilder} and invoke the {@link GsonBuilder#setVersion(double)} method.
|
||||||
* {@link com.google.gson.GsonBuilder#setVersion(double)} method.
|
|
||||||
*
|
*
|
||||||
* <p>Here is an example of how this annotation is meant to be used:</p>
|
* <p>Here is an example of how this annotation is meant to be used:</p>
|
||||||
* <pre>
|
* <pre>
|
||||||
@ -50,14 +50,16 @@ import java.lang.annotation.Target;
|
|||||||
*
|
*
|
||||||
* @author Inderjeet Singh
|
* @author Inderjeet Singh
|
||||||
* @author Joel Leitch
|
* @author Joel Leitch
|
||||||
|
* @see GsonBuilder#setVersion(double)
|
||||||
|
* @see Until
|
||||||
*/
|
*/
|
||||||
@Documented
|
@Documented
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target({ElementType.FIELD, ElementType.TYPE})
|
@Target({ElementType.FIELD, ElementType.TYPE})
|
||||||
public @interface Since {
|
public @interface Since {
|
||||||
/**
|
/**
|
||||||
* the value indicating a version number since this member
|
* The value indicating a version number since this member or type has been present.
|
||||||
* or type has been present.
|
* The number is inclusive; annotated elements will be included if {@code gsonVersion >= value}.
|
||||||
*/
|
*/
|
||||||
double value();
|
double value();
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.google.gson.annotations;
|
package com.google.gson.annotations;
|
||||||
|
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
@ -24,14 +25,13 @@ import java.lang.annotation.Target;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An annotation that indicates the version number until a member or a type should be present.
|
* An annotation that indicates the version number until a member or a type should be present.
|
||||||
* Basically, if Gson is created with a version number that exceeds the value stored in the
|
* Basically, if Gson is created with a version number that is equal to or exceeds the value
|
||||||
* {@code Until} annotation then the field will be ignored from the JSON output. This annotation
|
* stored in the {@code Until} annotation then the field will be ignored from the JSON output.
|
||||||
* is useful to manage versioning of your JSON classes for a web-service.
|
* This annotation is useful to manage versioning of your JSON classes for a web-service.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This annotation has no effect unless you build {@link com.google.gson.Gson} with a
|
* This annotation has no effect unless you build {@link com.google.gson.Gson} with a
|
||||||
* {@link com.google.gson.GsonBuilder} and invoke
|
* {@code GsonBuilder} and invoke the {@link GsonBuilder#setVersion(double)} method.
|
||||||
* {@link com.google.gson.GsonBuilder#setVersion(double)} method.
|
|
||||||
*
|
*
|
||||||
* <p>Here is an example of how this annotation is meant to be used:</p>
|
* <p>Here is an example of how this annotation is meant to be used:</p>
|
||||||
* <pre>
|
* <pre>
|
||||||
@ -53,6 +53,8 @@ import java.lang.annotation.Target;
|
|||||||
*
|
*
|
||||||
* @author Inderjeet Singh
|
* @author Inderjeet Singh
|
||||||
* @author Joel Leitch
|
* @author Joel Leitch
|
||||||
|
* @see GsonBuilder#setVersion(double)
|
||||||
|
* @see Since
|
||||||
* @since 1.3
|
* @since 1.3
|
||||||
*/
|
*/
|
||||||
@Documented
|
@Documented
|
||||||
@ -61,8 +63,8 @@ import java.lang.annotation.Target;
|
|||||||
public @interface Until {
|
public @interface Until {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the value indicating a version number until this member
|
* The value indicating a version number until this member or type should be be included.
|
||||||
* or type should be ignored.
|
* The number is exclusive; annotated elements will be included if {@code gsonVersion < value}.
|
||||||
*/
|
*/
|
||||||
double value();
|
double value();
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,25 @@ public final class ConstructorConstructor {
|
|||||||
this.reflectionFilters = reflectionFilters;
|
this.reflectionFilters = reflectionFilters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the class can be instantiated by Unsafe allocator. If the instance has interface or abstract modifiers
|
||||||
|
* return an exception message.
|
||||||
|
* @param c instance of the class to be checked
|
||||||
|
* @return if instantiable {@code null}, else a non-{@code null} exception message
|
||||||
|
*/
|
||||||
|
static String checkInstantiable(Class<?> c) {
|
||||||
|
int modifiers = c.getModifiers();
|
||||||
|
if (Modifier.isInterface(modifiers)) {
|
||||||
|
return "Interfaces can't be instantiated! Register an InstanceCreator "
|
||||||
|
+ "or a TypeAdapter for this type. Interface name: " + c.getName();
|
||||||
|
}
|
||||||
|
if (Modifier.isAbstract(modifiers)) {
|
||||||
|
return "Abstract classes can't be instantiated! Register an InstanceCreator "
|
||||||
|
+ "or a TypeAdapter for this type. Class name: " + c.getName();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
|
public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
|
||||||
final Type type = typeToken.getType();
|
final Type type = typeToken.getType();
|
||||||
final Class<? super T> rawType = typeToken.getRawType();
|
final Class<? super T> rawType = typeToken.getRawType();
|
||||||
@ -110,7 +129,7 @@ public final class ConstructorConstructor {
|
|||||||
|
|
||||||
// Check whether type is instantiable; otherwise ReflectionAccessFilter recommendation
|
// Check whether type is instantiable; otherwise ReflectionAccessFilter recommendation
|
||||||
// of adjusting filter suggested below is irrelevant since it would not solve the problem
|
// of adjusting filter suggested below is irrelevant since it would not solve the problem
|
||||||
final String exceptionMessage = UnsafeAllocator.checkInstantiable(rawType);
|
final String exceptionMessage = checkInstantiable(rawType);
|
||||||
if (exceptionMessage != null) {
|
if (exceptionMessage != null) {
|
||||||
return new ObjectConstructor<T>() {
|
return new ObjectConstructor<T>() {
|
||||||
@Override public T construct() {
|
@Override public T construct() {
|
||||||
@ -242,14 +261,17 @@ public final class ConstructorConstructor {
|
|||||||
@SuppressWarnings("unchecked") // T is the same raw type as is requested
|
@SuppressWarnings("unchecked") // T is the same raw type as is requested
|
||||||
T newInstance = (T) constructor.newInstance();
|
T newInstance = (T) constructor.newInstance();
|
||||||
return newInstance;
|
return newInstance;
|
||||||
} catch (InstantiationException e) {
|
}
|
||||||
// TODO: JsonParseException ?
|
// Note: InstantiationException should be impossible because check at start of method made sure
|
||||||
throw new RuntimeException("Failed to invoke " + constructor + " with no args", e);
|
// that class is not abstract
|
||||||
|
catch (InstantiationException e) {
|
||||||
|
throw new RuntimeException("Failed to invoke constructor '" + ReflectionHelper.constructorToString(constructor) + "'"
|
||||||
|
+ " with no args", e);
|
||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException e) {
|
||||||
// TODO: don't wrap if cause is unchecked!
|
// TODO: don't wrap if cause is unchecked?
|
||||||
// TODO: JsonParseException ?
|
// TODO: JsonParseException ?
|
||||||
throw new RuntimeException("Failed to invoke " + constructor + " with no args",
|
throw new RuntimeException("Failed to invoke constructor '" + ReflectionHelper.constructorToString(constructor) + "'"
|
||||||
e.getTargetException());
|
+ " with no args", e.getCause());
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw ReflectionHelper.createExceptionForUnexpectedIllegalAccess(e);
|
throw ReflectionHelper.createExceptionForUnexpectedIllegalAccess(e);
|
||||||
}
|
}
|
||||||
@ -342,11 +364,10 @@ public final class ConstructorConstructor {
|
|||||||
private <T> ObjectConstructor<T> newUnsafeAllocator(final Class<? super T> rawType) {
|
private <T> ObjectConstructor<T> newUnsafeAllocator(final Class<? super T> rawType) {
|
||||||
if (useJdkUnsafe) {
|
if (useJdkUnsafe) {
|
||||||
return new ObjectConstructor<T>() {
|
return new ObjectConstructor<T>() {
|
||||||
private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
|
|
||||||
@Override public T construct() {
|
@Override public T construct() {
|
||||||
try {
|
try {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
T newInstance = (T) unsafeAllocator.newInstance(rawType);
|
T newInstance = (T) UnsafeAllocator.INSTANCE.newInstance(rawType);
|
||||||
return newInstance;
|
return newInstance;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(("Unable to create instance of " + rawType + ". "
|
throw new RuntimeException(("Unable to create instance of " + rawType + ". "
|
||||||
|
@ -240,9 +240,7 @@ public final class Excluder implements TypeAdapterFactory, Cloneable {
|
|||||||
private boolean isValidSince(Since annotation) {
|
private boolean isValidSince(Since annotation) {
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
double annotationVersion = annotation.value();
|
double annotationVersion = annotation.value();
|
||||||
if (annotationVersion > version) {
|
return version >= annotationVersion;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -250,9 +248,7 @@ public final class Excluder implements TypeAdapterFactory, Cloneable {
|
|||||||
private boolean isValidUntil(Until annotation) {
|
private boolean isValidUntil(Until annotation) {
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
double annotationVersion = annotation.value();
|
double annotationVersion = annotation.value();
|
||||||
if (annotationVersion <= version) {
|
return version < annotationVersion;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,98 @@
|
|||||||
|
package com.google.gson.internal;
|
||||||
|
|
||||||
|
import java.util.AbstractList;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.RandomAccess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link List} which wraps another {@code List} but prevents insertion of
|
||||||
|
* {@code null} elements. Methods which only perform checks with the element
|
||||||
|
* argument (e.g. {@link #contains(Object)}) do not throw exceptions for
|
||||||
|
* {@code null} arguments.
|
||||||
|
*/
|
||||||
|
public class NonNullElementWrapperList<E> extends AbstractList<E> implements RandomAccess {
|
||||||
|
// Explicitly specify ArrayList as type to guarantee that delegate implements RandomAccess
|
||||||
|
private final ArrayList<E> delegate;
|
||||||
|
|
||||||
|
public NonNullElementWrapperList(ArrayList<E> delegate) {
|
||||||
|
this.delegate = Objects.requireNonNull(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public E get(int index) {
|
||||||
|
return delegate.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public int size() {
|
||||||
|
return delegate.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private E nonNull(E element) {
|
||||||
|
if (element == null) {
|
||||||
|
throw new NullPointerException("Element must be non-null");
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public E set(int index, E element) {
|
||||||
|
return delegate.set(index, nonNull(element));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void add(int index, E element) {
|
||||||
|
delegate.add(index, nonNull(element));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public E remove(int index) {
|
||||||
|
return delegate.remove(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The following methods are overridden because their default implementation is inefficient */
|
||||||
|
|
||||||
|
@Override public void clear() {
|
||||||
|
delegate.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public boolean remove(Object o) {
|
||||||
|
return delegate.remove(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public boolean removeAll(Collection<?> c) {
|
||||||
|
return delegate.removeAll(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public boolean retainAll(Collection<?> c) {
|
||||||
|
return delegate.retainAll(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public boolean contains(Object o) {
|
||||||
|
return delegate.contains(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public int indexOf(Object o) {
|
||||||
|
return delegate.indexOf(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public int lastIndexOf(Object o) {
|
||||||
|
return delegate.lastIndexOf(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public Object[] toArray() {
|
||||||
|
return delegate.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public <T> T[] toArray(T[] a) {
|
||||||
|
return delegate.toArray(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public boolean equals(Object o) {
|
||||||
|
return delegate.equals(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public int hashCode() {
|
||||||
|
return delegate.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Once Gson targets Java 8 also override List.sort
|
||||||
|
}
|
@ -20,7 +20,6 @@ import java.io.ObjectInputStream;
|
|||||||
import java.io.ObjectStreamClass;
|
import java.io.ObjectStreamClass;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do sneaky things to allocate objects without invoking their constructors.
|
* Do sneaky things to allocate objects without invoking their constructors.
|
||||||
@ -31,38 +30,21 @@ import java.lang.reflect.Modifier;
|
|||||||
public abstract class UnsafeAllocator {
|
public abstract class UnsafeAllocator {
|
||||||
public abstract <T> T newInstance(Class<T> c) throws Exception;
|
public abstract <T> T newInstance(Class<T> c) throws Exception;
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the class can be instantiated by Unsafe allocator. If the instance has interface or abstract modifiers
|
|
||||||
* return an exception message.
|
|
||||||
* @param c instance of the class to be checked
|
|
||||||
* @return if instantiable {@code null}, else a non-{@code null} exception message
|
|
||||||
*/
|
|
||||||
static String checkInstantiable(Class<?> c) {
|
|
||||||
int modifiers = c.getModifiers();
|
|
||||||
if (Modifier.isInterface(modifiers)) {
|
|
||||||
return "Interfaces can't be instantiated! Register an InstanceCreator "
|
|
||||||
+ "or a TypeAdapter for this type. Interface name: " + c.getName();
|
|
||||||
}
|
|
||||||
if (Modifier.isAbstract(modifiers)) {
|
|
||||||
return "Abstract classes can't be instantiated! Register an InstanceCreator "
|
|
||||||
+ "or a TypeAdapter for this type. Class name: " + c.getName();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts that the class is instantiable. This check should have already occurred
|
* Asserts that the class is instantiable. This check should have already occurred
|
||||||
* in {@link ConstructorConstructor}; this check here acts as safeguard since trying
|
* in {@link ConstructorConstructor}; this check here acts as safeguard since trying
|
||||||
* to use Unsafe for non-instantiable classes might crash the JVM on some devices.
|
* to use Unsafe for non-instantiable classes might crash the JVM on some devices.
|
||||||
*/
|
*/
|
||||||
private static void assertInstantiable(Class<?> c) {
|
private static void assertInstantiable(Class<?> c) {
|
||||||
String exceptionMessage = checkInstantiable(c);
|
String exceptionMessage = ConstructorConstructor.checkInstantiable(c);
|
||||||
if (exceptionMessage != null) {
|
if (exceptionMessage != null) {
|
||||||
throw new AssertionError("UnsafeAllocator is used for non-instantiable type: " + exceptionMessage);
|
throw new AssertionError("UnsafeAllocator is used for non-instantiable type: " + exceptionMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UnsafeAllocator create() {
|
public static final UnsafeAllocator INSTANCE = create();
|
||||||
|
|
||||||
|
private static UnsafeAllocator create() {
|
||||||
// try JVM
|
// try JVM
|
||||||
// public class Unsafe {
|
// public class Unsafe {
|
||||||
// public Object allocateInstance(Class<?> type);
|
// public Object allocateInstance(Class<?> type);
|
||||||
|
@ -93,6 +93,7 @@ public final class JsonTreeReader extends JsonReader {
|
|||||||
|
|
||||||
@Override public void endObject() throws IOException {
|
@Override public void endObject() throws IOException {
|
||||||
expect(JsonToken.END_OBJECT);
|
expect(JsonToken.END_OBJECT);
|
||||||
|
pathNames[stackSize - 1] = null; // Free the last path name so that it can be garbage collected
|
||||||
popStack(); // empty iterator
|
popStack(); // empty iterator
|
||||||
popStack(); // object
|
popStack(); // object
|
||||||
if (stackSize > 0) {
|
if (stackSize > 0) {
|
||||||
@ -165,16 +166,20 @@ public final class JsonTreeReader extends JsonReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public String nextName() throws IOException {
|
private String nextName(boolean skipName) throws IOException {
|
||||||
expect(JsonToken.NAME);
|
expect(JsonToken.NAME);
|
||||||
Iterator<?> i = (Iterator<?>) peekStack();
|
Iterator<?> i = (Iterator<?>) peekStack();
|
||||||
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
|
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
|
||||||
String result = (String) entry.getKey();
|
String result = (String) entry.getKey();
|
||||||
pathNames[stackSize - 1] = result;
|
pathNames[stackSize - 1] = skipName ? "<skipped>" : result;
|
||||||
push(entry.getValue());
|
push(entry.getValue());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public String nextName() throws IOException {
|
||||||
|
return nextName(false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override public String nextString() throws IOException {
|
@Override public String nextString() throws IOException {
|
||||||
JsonToken token = peek();
|
JsonToken token = peek();
|
||||||
if (token != JsonToken.STRING && token != JsonToken.NUMBER) {
|
if (token != JsonToken.STRING && token != JsonToken.NUMBER) {
|
||||||
@ -269,18 +274,27 @@ public final class JsonTreeReader extends JsonReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override public void skipValue() throws IOException {
|
@Override public void skipValue() throws IOException {
|
||||||
if (peek() == JsonToken.NAME) {
|
JsonToken peeked = peek();
|
||||||
nextName();
|
switch (peeked) {
|
||||||
pathNames[stackSize - 2] = "null";
|
case NAME:
|
||||||
} else {
|
@SuppressWarnings("unused")
|
||||||
|
String unused = nextName(true);
|
||||||
|
break;
|
||||||
|
case END_ARRAY:
|
||||||
|
endArray();
|
||||||
|
throw new IllegalStateException("Attempt to skip led outside its parent");
|
||||||
|
case END_OBJECT:
|
||||||
|
endObject();
|
||||||
|
throw new IllegalStateException("Attempt to skip led outside its parent");
|
||||||
|
case END_DOCUMENT:
|
||||||
|
throw new IllegalStateException("Attempt to skip led outside the document");
|
||||||
|
default:
|
||||||
popStack();
|
popStack();
|
||||||
if (stackSize > 0) {
|
|
||||||
pathNames[stackSize - 1] = "null";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (stackSize > 0) {
|
if (stackSize > 0) {
|
||||||
pathIndices[stackSize - 1]++;
|
pathIndices[stackSize - 1]++;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public String toString() {
|
@Override public String toString() {
|
||||||
|
@ -40,7 +40,7 @@ import java.util.Map;
|
|||||||
/**
|
/**
|
||||||
* Adapts maps to either JSON objects or JSON arrays.
|
* Adapts maps to either JSON objects or JSON arrays.
|
||||||
*
|
*
|
||||||
* <h3>Maps as JSON objects</h3>
|
* <h2>Maps as JSON objects</h2>
|
||||||
* For primitive keys or when complex map key serialization is not enabled, this
|
* For primitive keys or when complex map key serialization is not enabled, this
|
||||||
* converts Java {@link Map Maps} to JSON Objects. This requires that map keys
|
* converts Java {@link Map Maps} to JSON Objects. This requires that map keys
|
||||||
* can be serialized as strings; this is insufficient for some key types. For
|
* can be serialized as strings; this is insufficient for some key types. For
|
||||||
@ -65,7 +65,7 @@ import java.util.Map;
|
|||||||
* at com.google.gson.ObjectNavigator.navigateClassFields
|
* at com.google.gson.ObjectNavigator.navigateClassFields
|
||||||
* ...</pre>
|
* ...</pre>
|
||||||
*
|
*
|
||||||
* <h3>Maps as JSON arrays</h3>
|
* <h2>Maps as JSON arrays</h2>
|
||||||
* An alternative approach taken by this type adapter when it is required and
|
* An alternative approach taken by this type adapter when it is required and
|
||||||
* complex map key serialization is enabled is to encode maps as arrays of map
|
* complex map key serialization is enabled is to encode maps as arrays of map
|
||||||
* entries. Each map entry is a two element array containing a key and a value.
|
* entries. Each map entry is a two element array containing a key and a value.
|
||||||
|
@ -19,6 +19,7 @@ package com.google.gson.internal.bind;
|
|||||||
import com.google.gson.FieldNamingStrategy;
|
import com.google.gson.FieldNamingStrategy;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonIOException;
|
import com.google.gson.JsonIOException;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
import com.google.gson.JsonSyntaxException;
|
import com.google.gson.JsonSyntaxException;
|
||||||
import com.google.gson.ReflectionAccessFilter;
|
import com.google.gson.ReflectionAccessFilter;
|
||||||
import com.google.gson.ReflectionAccessFilter.FilterResult;
|
import com.google.gson.ReflectionAccessFilter.FilterResult;
|
||||||
@ -38,11 +39,18 @@ import com.google.gson.stream.JsonReader;
|
|||||||
import com.google.gson.stream.JsonToken;
|
import com.google.gson.stream.JsonToken;
|
||||||
import com.google.gson.stream.JsonWriter;
|
import com.google.gson.stream.JsonWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.AccessibleObject;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Member;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -88,44 +96,59 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
|||||||
|
|
||||||
List<String> fieldNames = new ArrayList<>(alternates.length + 1);
|
List<String> fieldNames = new ArrayList<>(alternates.length + 1);
|
||||||
fieldNames.add(serializedName);
|
fieldNames.add(serializedName);
|
||||||
for (String alternate : alternates) {
|
Collections.addAll(fieldNames, alternates);
|
||||||
fieldNames.add(alternate);
|
|
||||||
}
|
|
||||||
return fieldNames;
|
return fieldNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
|
@Override
|
||||||
|
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
|
||||||
Class<? super T> raw = type.getRawType();
|
Class<? super T> raw = type.getRawType();
|
||||||
|
|
||||||
if (!Object.class.isAssignableFrom(raw)) {
|
if (!Object.class.isAssignableFrom(raw)) {
|
||||||
return null; // it's a primitive!
|
return null; // it's a primitive!
|
||||||
}
|
}
|
||||||
|
|
||||||
FilterResult filterResult = ReflectionAccessFilterHelper.getFilterResult(reflectionFilters, raw);
|
FilterResult filterResult =
|
||||||
|
ReflectionAccessFilterHelper.getFilterResult(reflectionFilters, raw);
|
||||||
if (filterResult == FilterResult.BLOCK_ALL) {
|
if (filterResult == FilterResult.BLOCK_ALL) {
|
||||||
throw new JsonIOException("ReflectionAccessFilter does not permit using reflection for "
|
throw new JsonIOException(
|
||||||
+ raw + ". Register a TypeAdapter for this type or adjust the access filter.");
|
"ReflectionAccessFilter does not permit using reflection for " + raw
|
||||||
|
+ ". Register a TypeAdapter for this type or adjust the access filter.");
|
||||||
}
|
}
|
||||||
boolean blockInaccessible = filterResult == FilterResult.BLOCK_INACCESSIBLE;
|
boolean blockInaccessible = filterResult == FilterResult.BLOCK_INACCESSIBLE;
|
||||||
|
|
||||||
ObjectConstructor<T> constructor = constructorConstructor.get(type);
|
// If the type is actually a Java Record, we need to use the RecordAdapter instead. This will always be false
|
||||||
return new Adapter<>(constructor, getBoundFields(gson, type, raw, blockInaccessible));
|
// on JVMs that do not support records.
|
||||||
|
if (ReflectionHelper.isRecord(raw)) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
TypeAdapter<T> adapter = (TypeAdapter<T>) new RecordAdapter<>(raw,
|
||||||
|
getBoundFields(gson, type, raw, blockInaccessible, true), blockInaccessible);
|
||||||
|
return adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkAccessible(Object object, Field field) {
|
ObjectConstructor<T> constructor = constructorConstructor.get(type);
|
||||||
if (!ReflectionAccessFilterHelper.canAccess(field, Modifier.isStatic(field.getModifiers()) ? null : object)) {
|
return new FieldReflectionAdapter<>(constructor, getBoundFields(gson, type, raw, blockInaccessible, false));
|
||||||
throw new JsonIOException("Field '" + field.getDeclaringClass().getName() + "#"
|
}
|
||||||
+ field.getName() + "' is not accessible and ReflectionAccessFilter does not "
|
|
||||||
+ "permit making it accessible. Register a TypeAdapter for the declaring type "
|
private static <M extends AccessibleObject & Member> void checkAccessible(Object object, M member) {
|
||||||
+ "or adjust the access filter.");
|
if (!ReflectionAccessFilterHelper.canAccess(member, Modifier.isStatic(member.getModifiers()) ? null : object)) {
|
||||||
|
String memberDescription = ReflectionHelper.getAccessibleObjectDescription(member, true);
|
||||||
|
throw new JsonIOException(memberDescription + " is not accessible and ReflectionAccessFilter does not"
|
||||||
|
+ " permit making it accessible. Register a TypeAdapter for the declaring type, adjust the"
|
||||||
|
+ " access filter or increase the visibility of the element and its declaring type.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReflectiveTypeAdapterFactory.BoundField createBoundField(
|
private ReflectiveTypeAdapterFactory.BoundField createBoundField(
|
||||||
final Gson context, final Field field, final String name,
|
final Gson context, final Field field, final Method accessor, final String name,
|
||||||
final TypeToken<?> fieldType, boolean serialize, boolean deserialize,
|
final TypeToken<?> fieldType, boolean serialize, boolean deserialize,
|
||||||
final boolean blockInaccessible) {
|
final boolean blockInaccessible) {
|
||||||
|
|
||||||
final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
|
final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
|
||||||
|
|
||||||
|
int modifiers = field.getModifiers();
|
||||||
|
final boolean isStaticFinalField = Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers);
|
||||||
|
|
||||||
JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
|
JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
|
||||||
TypeAdapter<?> mapped = null;
|
TypeAdapter<?> mapped = null;
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
@ -138,16 +161,32 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) mapped;
|
final TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) mapped;
|
||||||
return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
|
return new ReflectiveTypeAdapterFactory.BoundField(name, field.getName(), serialize, deserialize) {
|
||||||
@Override void write(JsonWriter writer, Object value)
|
@Override void write(JsonWriter writer, Object source)
|
||||||
throws IOException, IllegalAccessException {
|
throws IOException, IllegalAccessException {
|
||||||
if (!serialized) return;
|
if (!serialized) return;
|
||||||
if (blockInaccessible) {
|
if (blockInaccessible) {
|
||||||
checkAccessible(value, field);
|
if (accessor == null) {
|
||||||
|
checkAccessible(source, field);
|
||||||
|
} else {
|
||||||
|
// Note: This check might actually be redundant because access check for canonical
|
||||||
|
// constructor should have failed already
|
||||||
|
checkAccessible(source, accessor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Object fieldValue = field.get(value);
|
Object fieldValue;
|
||||||
if (fieldValue == value) {
|
if (accessor != null) {
|
||||||
|
try {
|
||||||
|
fieldValue = accessor.invoke(source);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
String accessorDescription = ReflectionHelper.getAccessibleObjectDescription(accessor, false);
|
||||||
|
throw new JsonIOException("Accessor " + accessorDescription + " threw exception", e.getCause());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fieldValue = field.get(source);
|
||||||
|
}
|
||||||
|
if (fieldValue == source) {
|
||||||
// avoid direct recursion
|
// avoid direct recursion
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -156,20 +195,38 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
|||||||
: new TypeAdapterRuntimeTypeWrapper<>(context, typeAdapter, fieldType.getType());
|
: new TypeAdapterRuntimeTypeWrapper<>(context, typeAdapter, fieldType.getType());
|
||||||
t.write(writer, fieldValue);
|
t.write(writer, fieldValue);
|
||||||
}
|
}
|
||||||
@Override void read(JsonReader reader, Object value)
|
|
||||||
|
@Override
|
||||||
|
void readIntoArray(JsonReader reader, int index, Object[] target) throws IOException, JsonParseException {
|
||||||
|
Object fieldValue = typeAdapter.read(reader);
|
||||||
|
if (fieldValue == null && isPrimitive) {
|
||||||
|
throw new JsonParseException("null is not allowed as value for record component '" + fieldName + "'"
|
||||||
|
+ " of primitive type; at path " + reader.getPath());
|
||||||
|
}
|
||||||
|
target[index] = fieldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void readIntoField(JsonReader reader, Object target)
|
||||||
throws IOException, IllegalAccessException {
|
throws IOException, IllegalAccessException {
|
||||||
Object fieldValue = typeAdapter.read(reader);
|
Object fieldValue = typeAdapter.read(reader);
|
||||||
if (fieldValue != null || !isPrimitive) {
|
if (fieldValue != null || !isPrimitive) {
|
||||||
if (blockInaccessible) {
|
if (blockInaccessible) {
|
||||||
checkAccessible(value, field);
|
checkAccessible(target, field);
|
||||||
|
} else if (isStaticFinalField) {
|
||||||
|
// Reflection does not permit setting value of `static final` field, even after calling `setAccessible`
|
||||||
|
// Handle this here to avoid causing IllegalAccessException when calling `Field.set`
|
||||||
|
String fieldDescription = ReflectionHelper.getAccessibleObjectDescription(field, false);
|
||||||
|
throw new JsonIOException("Cannot set value of 'static final' " + fieldDescription);
|
||||||
}
|
}
|
||||||
field.set(value, fieldValue);
|
field.set(target, fieldValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw, boolean blockInaccessible) {
|
private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw,
|
||||||
|
boolean blockInaccessible, boolean isRecord) {
|
||||||
Map<String, BoundField> result = new LinkedHashMap<>();
|
Map<String, BoundField> result = new LinkedHashMap<>();
|
||||||
if (raw.isInterface()) {
|
if (raw.isInterface()) {
|
||||||
return result;
|
return result;
|
||||||
@ -184,9 +241,9 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
|||||||
if (raw != originalRaw && fields.length > 0) {
|
if (raw != originalRaw && fields.length > 0) {
|
||||||
FilterResult filterResult = ReflectionAccessFilterHelper.getFilterResult(reflectionFilters, raw);
|
FilterResult filterResult = ReflectionAccessFilterHelper.getFilterResult(reflectionFilters, raw);
|
||||||
if (filterResult == FilterResult.BLOCK_ALL) {
|
if (filterResult == FilterResult.BLOCK_ALL) {
|
||||||
throw new JsonIOException("ReflectionAccessFilter does not permit using reflection for "
|
throw new JsonIOException("ReflectionAccessFilter does not permit using reflection for " + raw
|
||||||
+ raw + " (supertype of " + originalRaw + "). Register a TypeAdapter for this type "
|
+ " (supertype of " + originalRaw + "). Register a TypeAdapter for this type"
|
||||||
+ "or adjust the access filter.");
|
+ " or adjust the access filter.");
|
||||||
}
|
}
|
||||||
blockInaccessible = filterResult == FilterResult.BLOCK_INACCESSIBLE;
|
blockInaccessible = filterResult == FilterResult.BLOCK_INACCESSIBLE;
|
||||||
}
|
}
|
||||||
@ -197,9 +254,36 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
|||||||
if (!serialize && !deserialize) {
|
if (!serialize && !deserialize) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// The accessor method is only used for records. If the type is a record, we will read out values
|
||||||
|
// via its accessor method instead of via reflection. This way we will bypass the accessible restrictions
|
||||||
|
Method accessor = null;
|
||||||
|
if (isRecord) {
|
||||||
|
// If there is a static field on a record, there will not be an accessor. Instead we will use the default
|
||||||
|
// field serialization logic, but for deserialization the field is excluded for simplicity. Note that Gson
|
||||||
|
// ignores static fields by default, but GsonBuilder.excludeFieldsWithModifiers can overwrite this.
|
||||||
|
if (Modifier.isStatic(field.getModifiers())) {
|
||||||
|
deserialize = false;
|
||||||
|
} else {
|
||||||
|
accessor = ReflectionHelper.getAccessor(raw, field);
|
||||||
// If blockInaccessible, skip and perform access check later
|
// If blockInaccessible, skip and perform access check later
|
||||||
if (!blockInaccessible) {
|
if (!blockInaccessible) {
|
||||||
|
ReflectionHelper.makeAccessible(accessor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @SerializedName can be placed on accessor method, but it is not supported there
|
||||||
|
// If field and method have annotation it is not easily possible to determine if accessor method
|
||||||
|
// is implicit and has inherited annotation, or if it is explicitly declared with custom annotation
|
||||||
|
if (accessor.getAnnotation(SerializedName.class) != null
|
||||||
|
&& field.getAnnotation(SerializedName.class) == null) {
|
||||||
|
String methodDescription = ReflectionHelper.getAccessibleObjectDescription(accessor, false);
|
||||||
|
throw new JsonIOException("@SerializedName on " + methodDescription + " is not supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If blockInaccessible, skip and perform access check later
|
||||||
|
// For Records if the accessor method is used the field does not have to be made accessible
|
||||||
|
if (!blockInaccessible && accessor == null) {
|
||||||
ReflectionHelper.makeAccessible(field);
|
ReflectionHelper.makeAccessible(field);
|
||||||
}
|
}
|
||||||
Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
|
Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
|
||||||
@ -208,7 +292,7 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
|||||||
for (int i = 0, size = fieldNames.size(); i < size; ++i) {
|
for (int i = 0, size = fieldNames.size(); i < size; ++i) {
|
||||||
String name = fieldNames.get(i);
|
String name = fieldNames.get(i);
|
||||||
if (i != 0) serialize = false; // only serialize the default name
|
if (i != 0) serialize = false; // only serialize the default name
|
||||||
BoundField boundField = createBoundField(context, field, name,
|
BoundField boundField = createBoundField(context, field, accessor, name,
|
||||||
TypeToken.get(fieldType), serialize, deserialize, blockInaccessible);
|
TypeToken.get(fieldType), serialize, deserialize, blockInaccessible);
|
||||||
BoundField replaced = result.put(name, boundField);
|
BoundField replaced = result.put(name, boundField);
|
||||||
if (previous == null) previous = replaced;
|
if (previous == null) previous = replaced;
|
||||||
@ -226,56 +310,51 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
|||||||
|
|
||||||
static abstract class BoundField {
|
static abstract class BoundField {
|
||||||
final String name;
|
final String name;
|
||||||
|
/** Name of the underlying field */
|
||||||
|
final String fieldName;
|
||||||
final boolean serialized;
|
final boolean serialized;
|
||||||
final boolean deserialized;
|
final boolean deserialized;
|
||||||
|
|
||||||
protected BoundField(String name, boolean serialized, boolean deserialized) {
|
protected BoundField(String name, String fieldName, boolean serialized, boolean deserialized) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.fieldName = fieldName;
|
||||||
this.serialized = serialized;
|
this.serialized = serialized;
|
||||||
this.deserialized = deserialized;
|
this.deserialized = deserialized;
|
||||||
}
|
}
|
||||||
abstract void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException;
|
|
||||||
abstract void read(JsonReader reader, Object value) throws IOException, IllegalAccessException;
|
/** Read this field value from the source, and append its JSON value to the writer */
|
||||||
|
abstract void write(JsonWriter writer, Object source) throws IOException, IllegalAccessException;
|
||||||
|
|
||||||
|
/** Read the value into the target array, used to provide constructor arguments for records */
|
||||||
|
abstract void readIntoArray(JsonReader reader, int index, Object[] target) throws IOException, JsonParseException;
|
||||||
|
|
||||||
|
/** Read the value from the reader, and set it on the corresponding field on target via reflection */
|
||||||
|
abstract void readIntoField(JsonReader reader, Object target) throws IOException, IllegalAccessException;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Adapter<T> extends TypeAdapter<T> {
|
/**
|
||||||
private final ObjectConstructor<T> constructor;
|
* Base class for Adapters produced by this factory.
|
||||||
private final Map<String, BoundField> boundFields;
|
*
|
||||||
|
* <p>The {@link RecordAdapter} is a special case to handle records for JVMs that support it, for
|
||||||
|
* all other types we use the {@link FieldReflectionAdapter}. This class encapsulates the common
|
||||||
|
* logic for serialization and deserialization. During deserialization, we construct an
|
||||||
|
* accumulator A, which we use to accumulate values from the source JSON. After the object has been read in
|
||||||
|
* full, the {@link #finalize(Object)} method is used to convert the accumulator to an instance
|
||||||
|
* of T.
|
||||||
|
*
|
||||||
|
* @param <T> type of objects that this Adapter creates.
|
||||||
|
* @param <A> type of accumulator used to build the deserialization result.
|
||||||
|
*/
|
||||||
|
// This class is public because external projects check for this class with `instanceof` (even though it is internal)
|
||||||
|
public static abstract class Adapter<T, A> extends TypeAdapter<T> {
|
||||||
|
final Map<String, BoundField> boundFields;
|
||||||
|
|
||||||
Adapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) {
|
Adapter(Map<String, BoundField> boundFields) {
|
||||||
this.constructor = constructor;
|
|
||||||
this.boundFields = boundFields;
|
this.boundFields = boundFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public T read(JsonReader in) throws IOException {
|
@Override
|
||||||
if (in.peek() == JsonToken.NULL) {
|
public void write(JsonWriter out, T value) throws IOException {
|
||||||
in.nextNull();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
T instance = constructor.construct();
|
|
||||||
|
|
||||||
try {
|
|
||||||
in.beginObject();
|
|
||||||
while (in.hasNext()) {
|
|
||||||
String name = in.nextName();
|
|
||||||
BoundField field = boundFields.get(name);
|
|
||||||
if (field == null || !field.deserialized) {
|
|
||||||
in.skipValue();
|
|
||||||
} else {
|
|
||||||
field.read(in, instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
throw new JsonSyntaxException(e);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw ReflectionHelper.createExceptionForUnexpectedIllegalAccess(e);
|
|
||||||
}
|
|
||||||
in.endObject();
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void write(JsonWriter out, T value) throws IOException {
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
out.nullValue();
|
out.nullValue();
|
||||||
return;
|
return;
|
||||||
@ -291,5 +370,163 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
|||||||
}
|
}
|
||||||
out.endObject();
|
out.endObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T read(JsonReader in) throws IOException {
|
||||||
|
if (in.peek() == JsonToken.NULL) {
|
||||||
|
in.nextNull();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
A accumulator = createAccumulator();
|
||||||
|
|
||||||
|
try {
|
||||||
|
in.beginObject();
|
||||||
|
while (in.hasNext()) {
|
||||||
|
String name = in.nextName();
|
||||||
|
BoundField field = boundFields.get(name);
|
||||||
|
if (field == null || !field.deserialized) {
|
||||||
|
in.skipValue();
|
||||||
|
} else {
|
||||||
|
readField(accumulator, in, field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
throw new JsonSyntaxException(e);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw ReflectionHelper.createExceptionForUnexpectedIllegalAccess(e);
|
||||||
|
}
|
||||||
|
in.endObject();
|
||||||
|
return finalize(accumulator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create the Object that will be used to collect each field value */
|
||||||
|
abstract A createAccumulator();
|
||||||
|
/**
|
||||||
|
* Read a single BoundField into the accumulator. The JsonReader will be pointed at the
|
||||||
|
* start of the value for the BoundField to read from.
|
||||||
|
*/
|
||||||
|
abstract void readField(A accumulator, JsonReader in, BoundField field)
|
||||||
|
throws IllegalAccessException, IOException;
|
||||||
|
/** Convert the accumulator to a final instance of T. */
|
||||||
|
abstract T finalize(A accumulator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class FieldReflectionAdapter<T> extends Adapter<T, T> {
|
||||||
|
private final ObjectConstructor<T> constructor;
|
||||||
|
|
||||||
|
FieldReflectionAdapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) {
|
||||||
|
super(boundFields);
|
||||||
|
this.constructor = constructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
T createAccumulator() {
|
||||||
|
return constructor.construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void readField(T accumulator, JsonReader in, BoundField field)
|
||||||
|
throws IllegalAccessException, IOException {
|
||||||
|
field.readIntoField(in, accumulator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
T finalize(T accumulator) {
|
||||||
|
return accumulator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class RecordAdapter<T> extends Adapter<T, Object[]> {
|
||||||
|
static final Map<Class<?>, Object> PRIMITIVE_DEFAULTS = primitiveDefaults();
|
||||||
|
|
||||||
|
// The canonical constructor of the record
|
||||||
|
private final Constructor<T> constructor;
|
||||||
|
// Array of arguments to the constructor, initialized with default values for primitives
|
||||||
|
private final Object[] constructorArgsDefaults;
|
||||||
|
// Map from component names to index into the constructors arguments.
|
||||||
|
private final Map<String, Integer> componentIndices = new HashMap<>();
|
||||||
|
|
||||||
|
RecordAdapter(Class<T> raw, Map<String, BoundField> boundFields, boolean blockInaccessible) {
|
||||||
|
super(boundFields);
|
||||||
|
constructor = ReflectionHelper.getCanonicalRecordConstructor(raw);
|
||||||
|
|
||||||
|
if (blockInaccessible) {
|
||||||
|
checkAccessible(null, constructor);
|
||||||
|
} else {
|
||||||
|
// Ensure the constructor is accessible
|
||||||
|
ReflectionHelper.makeAccessible(constructor);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] componentNames = ReflectionHelper.getRecordComponentNames(raw);
|
||||||
|
for (int i = 0; i < componentNames.length; i++) {
|
||||||
|
componentIndices.put(componentNames[i], i);
|
||||||
|
}
|
||||||
|
Class<?>[] parameterTypes = constructor.getParameterTypes();
|
||||||
|
|
||||||
|
// We need to ensure that we are passing non-null values to primitive fields in the constructor. To do this,
|
||||||
|
// we create an Object[] where all primitives are initialized to non-null values.
|
||||||
|
constructorArgsDefaults = new Object[parameterTypes.length];
|
||||||
|
for (int i = 0; i < parameterTypes.length; i++) {
|
||||||
|
// This will correctly be null for non-primitive types:
|
||||||
|
constructorArgsDefaults[i] = PRIMITIVE_DEFAULTS.get(parameterTypes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<Class<?>, Object> primitiveDefaults() {
|
||||||
|
Map<Class<?>, Object> zeroes = new HashMap<>();
|
||||||
|
zeroes.put(byte.class, (byte) 0);
|
||||||
|
zeroes.put(short.class, (short) 0);
|
||||||
|
zeroes.put(int.class, 0);
|
||||||
|
zeroes.put(long.class, 0L);
|
||||||
|
zeroes.put(float.class, 0F);
|
||||||
|
zeroes.put(double.class, 0D);
|
||||||
|
zeroes.put(char.class, '\0');
|
||||||
|
zeroes.put(boolean.class, false);
|
||||||
|
return zeroes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Object[] createAccumulator() {
|
||||||
|
return constructorArgsDefaults.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void readField(Object[] accumulator, JsonReader in, BoundField field) throws IOException {
|
||||||
|
// Obtain the component index from the name of the field backing it
|
||||||
|
Integer componentIndex = componentIndices.get(field.fieldName);
|
||||||
|
if (componentIndex == null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Could not find the index in the constructor '" + ReflectionHelper.constructorToString(constructor) + "'"
|
||||||
|
+ " for field with name '" + field.fieldName + "',"
|
||||||
|
+ " unable to determine which argument in the constructor the field corresponds"
|
||||||
|
+ " to. This is unexpected behavior, as we expect the RecordComponents to have the"
|
||||||
|
+ " same names as the fields in the Java class, and that the order of the"
|
||||||
|
+ " RecordComponents is the same as the order of the canonical constructor parameters.");
|
||||||
|
}
|
||||||
|
field.readIntoArray(in, componentIndex, accumulator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
T finalize(Object[] accumulator) {
|
||||||
|
try {
|
||||||
|
return constructor.newInstance(accumulator);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw ReflectionHelper.createExceptionForUnexpectedIllegalAccess(e);
|
||||||
|
}
|
||||||
|
// Note: InstantiationException should be impossible because record class is not abstract;
|
||||||
|
// IllegalArgumentException should not be possible unless a bad adapter returns objects of the wrong type
|
||||||
|
catch (InstantiationException | IllegalArgumentException e) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Failed to invoke constructor '" + ReflectionHelper.constructorToString(constructor) + "'"
|
||||||
|
+ " with args " + Arrays.toString(accumulator), e);
|
||||||
|
}
|
||||||
|
catch (InvocationTargetException e) {
|
||||||
|
// TODO: JsonParseException ?
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Failed to invoke constructor '" + ReflectionHelper.constructorToString(constructor) + "'"
|
||||||
|
+ " with args " + Arrays.toString(accumulator), e.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.google.gson.internal.bind;
|
||||||
|
|
||||||
|
import com.google.gson.TypeAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type adapter which might delegate serialization to another adapter.
|
||||||
|
*/
|
||||||
|
public abstract class SerializationDelegatingTypeAdapter<T> extends TypeAdapter<T> {
|
||||||
|
/**
|
||||||
|
* Returns the adapter used for serialization, might be {@code this} or another adapter.
|
||||||
|
* That other adapter might itself also be a {@code SerializationDelegatingTypeAdapter}.
|
||||||
|
*/
|
||||||
|
public abstract TypeAdapter<T> getSerializationDelegate();
|
||||||
|
}
|
@ -38,7 +38,7 @@ import java.lang.reflect.Type;
|
|||||||
* tree adapter may be serialization-only or deserialization-only, this class
|
* tree adapter may be serialization-only or deserialization-only, this class
|
||||||
* has a facility to lookup a delegate type adapter on demand.
|
* has a facility to lookup a delegate type adapter on demand.
|
||||||
*/
|
*/
|
||||||
public final class TreeTypeAdapter<T> extends TypeAdapter<T> {
|
public final class TreeTypeAdapter<T> extends SerializationDelegatingTypeAdapter<T> {
|
||||||
private final JsonSerializer<T> serializer;
|
private final JsonSerializer<T> serializer;
|
||||||
private final JsonDeserializer<T> deserializer;
|
private final JsonDeserializer<T> deserializer;
|
||||||
final Gson gson;
|
final Gson gson;
|
||||||
@ -97,6 +97,15 @@ public final class TreeTypeAdapter<T> extends TypeAdapter<T> {
|
|||||||
: (delegate = gson.getDelegateAdapter(skipPast, typeToken));
|
: (delegate = gson.getDelegateAdapter(skipPast, typeToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the type adapter which is used for serialization. Returns {@code this}
|
||||||
|
* if this {@code TreeTypeAdapter} has a {@link #serializer}; otherwise returns
|
||||||
|
* the delegate.
|
||||||
|
*/
|
||||||
|
@Override public TypeAdapter<T> getSerializationDelegate() {
|
||||||
|
return serializer != null ? this : delegate();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new factory that will match each type against {@code exactType}.
|
* Returns a new factory that will match each type against {@code exactType}.
|
||||||
*/
|
*/
|
||||||
@ -169,5 +178,5 @@ public final class TreeTypeAdapter<T> extends TypeAdapter<T> {
|
|||||||
@Override public <R> R deserialize(JsonElement json, Type typeOfT) throws JsonParseException {
|
@Override public <R> R deserialize(JsonElement json, Type typeOfT) throws JsonParseException {
|
||||||
return (R) gson.fromJson(json, typeOfT);
|
return (R) gson.fromJson(json, typeOfT);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,10 +53,12 @@ final class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {
|
|||||||
if (runtimeType != type) {
|
if (runtimeType != type) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
TypeAdapter<T> runtimeTypeAdapter = (TypeAdapter<T>) context.getAdapter(TypeToken.get(runtimeType));
|
TypeAdapter<T> runtimeTypeAdapter = (TypeAdapter<T>) context.getAdapter(TypeToken.get(runtimeType));
|
||||||
|
// For backward compatibility only check ReflectiveTypeAdapterFactory.Adapter here but not any other
|
||||||
|
// wrapping adapters, see https://github.com/google/gson/pull/1787#issuecomment-1222175189
|
||||||
if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) {
|
if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) {
|
||||||
// The user registered a type adapter for the runtime type, so we will use that
|
// The user registered a type adapter for the runtime type, so we will use that
|
||||||
chosen = runtimeTypeAdapter;
|
chosen = runtimeTypeAdapter;
|
||||||
} else if (!(delegate instanceof ReflectiveTypeAdapterFactory.Adapter)) {
|
} else if (!isReflective(delegate)) {
|
||||||
// The user registered a type adapter for Base class, so we prefer it over the
|
// The user registered a type adapter for Base class, so we prefer it over the
|
||||||
// reflective type adapter for the runtime type
|
// reflective type adapter for the runtime type
|
||||||
chosen = delegate;
|
chosen = delegate;
|
||||||
@ -68,12 +70,30 @@ final class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {
|
|||||||
chosen.write(out, value);
|
chosen.write(out, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the type adapter uses reflection.
|
||||||
|
*
|
||||||
|
* @param typeAdapter the type adapter to check.
|
||||||
|
*/
|
||||||
|
private static boolean isReflective(TypeAdapter<?> typeAdapter) {
|
||||||
|
// Run this in loop in case multiple delegating adapters are nested
|
||||||
|
while (typeAdapter instanceof SerializationDelegatingTypeAdapter) {
|
||||||
|
TypeAdapter<?> delegate = ((SerializationDelegatingTypeAdapter<?>) typeAdapter).getSerializationDelegate();
|
||||||
|
// Break if adapter does not delegate serialization
|
||||||
|
if (delegate == typeAdapter) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
typeAdapter = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds a compatible runtime type if it is more specific
|
* Finds a compatible runtime type if it is more specific
|
||||||
*/
|
*/
|
||||||
private Type getRuntimeTypeIfMoreSpecific(Type type, Object value) {
|
private static Type getRuntimeTypeIfMoreSpecific(Type type, Object value) {
|
||||||
if (value != null
|
if (value != null && (type instanceof Class<?> || type instanceof TypeVariable<?>)) {
|
||||||
&& (type == Object.class || type instanceof TypeVariable<?> || type instanceof Class<?>)) {
|
|
||||||
type = value.getClass();
|
type = value.getClass();
|
||||||
}
|
}
|
||||||
return type;
|
return type;
|
||||||
|
@ -194,7 +194,11 @@ public final class TypeAdapters {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void write(JsonWriter out, Number value) throws IOException {
|
public void write(JsonWriter out, Number value) throws IOException {
|
||||||
out.value(value);
|
if (value == null) {
|
||||||
|
out.nullValue();
|
||||||
|
} else {
|
||||||
|
out.value(value.byteValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -223,7 +227,11 @@ public final class TypeAdapters {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void write(JsonWriter out, Number value) throws IOException {
|
public void write(JsonWriter out, Number value) throws IOException {
|
||||||
out.value(value);
|
if (value == null) {
|
||||||
|
out.nullValue();
|
||||||
|
} else {
|
||||||
|
out.value(value.shortValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -245,7 +253,11 @@ public final class TypeAdapters {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void write(JsonWriter out, Number value) throws IOException {
|
public void write(JsonWriter out, Number value) throws IOException {
|
||||||
out.value(value);
|
if (value == null) {
|
||||||
|
out.nullValue();
|
||||||
|
} else {
|
||||||
|
out.value(value.intValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
public static final TypeAdapterFactory INTEGER_FACTORY
|
public static final TypeAdapterFactory INTEGER_FACTORY
|
||||||
@ -323,7 +335,11 @@ public final class TypeAdapters {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void write(JsonWriter out, Number value) throws IOException {
|
public void write(JsonWriter out, Number value) throws IOException {
|
||||||
out.value(value);
|
if (value == null) {
|
||||||
|
out.nullValue();
|
||||||
|
} else {
|
||||||
|
out.value(value.longValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -338,7 +354,14 @@ public final class TypeAdapters {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void write(JsonWriter out, Number value) throws IOException {
|
public void write(JsonWriter out, Number value) throws IOException {
|
||||||
out.value(value);
|
if (value == null) {
|
||||||
|
out.nullValue();
|
||||||
|
} else {
|
||||||
|
// For backward compatibility don't call `JsonWriter.value(float)` because that method has
|
||||||
|
// been newly added and not all custom JsonWriter implementations might override it yet
|
||||||
|
Number floatNumber = value instanceof Float ? value : value.floatValue();
|
||||||
|
out.value(floatNumber);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -353,7 +376,11 @@ public final class TypeAdapters {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void write(JsonWriter out, Number value) throws IOException {
|
public void write(JsonWriter out, Number value) throws IOException {
|
||||||
out.value(value);
|
if (value == null) {
|
||||||
|
out.nullValue();
|
||||||
|
} else {
|
||||||
|
out.value(value.doubleValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,39 +2,97 @@ package com.google.gson.internal.reflect;
|
|||||||
|
|
||||||
import com.google.gson.JsonIOException;
|
import com.google.gson.JsonIOException;
|
||||||
import com.google.gson.internal.GsonBuildConfig;
|
import com.google.gson.internal.GsonBuildConfig;
|
||||||
|
import java.lang.reflect.AccessibleObject;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
public class ReflectionHelper {
|
public class ReflectionHelper {
|
||||||
private ReflectionHelper() { }
|
|
||||||
|
private static final RecordHelper RECORD_HELPER;
|
||||||
|
|
||||||
|
static {
|
||||||
|
RecordHelper instance;
|
||||||
|
try {
|
||||||
|
// Try to construct the RecordSupportedHelper, if this fails, records are not supported on this JVM.
|
||||||
|
instance = new RecordSupportedHelper();
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
instance = new RecordNotSupportedHelper();
|
||||||
|
}
|
||||||
|
RECORD_HELPER = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReflectionHelper() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries making the field accessible, wrapping any thrown exception in a
|
* Internal implementation of making an {@link AccessibleObject} accessible.
|
||||||
* {@link JsonIOException} with descriptive message.
|
|
||||||
*
|
*
|
||||||
* @param field field to make accessible
|
* @param object the object that {@link AccessibleObject#setAccessible(boolean)} should be called on.
|
||||||
* @throws JsonIOException if making the field accessible fails
|
* @throws JsonIOException if making the object accessible fails
|
||||||
*/
|
*/
|
||||||
public static void makeAccessible(Field field) throws JsonIOException {
|
public static void makeAccessible(AccessibleObject object) throws JsonIOException {
|
||||||
try {
|
try {
|
||||||
field.setAccessible(true);
|
object.setAccessible(true);
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
throw new JsonIOException("Failed making field '" + field.getDeclaringClass().getName() + "#"
|
String description = getAccessibleObjectDescription(object, false);
|
||||||
+ field.getName() + "' accessible; either change its visibility or write a custom "
|
throw new JsonIOException("Failed making " + description + " accessible; either increase its visibility"
|
||||||
+ "TypeAdapter for its declaring type", exception);
|
+ " or write a custom TypeAdapter for its declaring type.", exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a short string describing the {@link AccessibleObject} in a human-readable way.
|
||||||
|
* The result is normally shorter than {@link AccessibleObject#toString()} because it omits
|
||||||
|
* modifiers (e.g. {@code final}) and uses simple names for constructor and method parameter
|
||||||
|
* types.
|
||||||
|
*
|
||||||
|
* @param object object to describe
|
||||||
|
* @param uppercaseFirstLetter whether the first letter of the description should be uppercased
|
||||||
|
*/
|
||||||
|
public static String getAccessibleObjectDescription(AccessibleObject object, boolean uppercaseFirstLetter) {
|
||||||
|
String description;
|
||||||
|
|
||||||
|
if (object instanceof Field) {
|
||||||
|
Field field = (Field) object;
|
||||||
|
description = "field '" + field.getDeclaringClass().getName() + "#" + field.getName() + "'";
|
||||||
|
} else if (object instanceof Method) {
|
||||||
|
Method method = (Method) object;
|
||||||
|
|
||||||
|
StringBuilder methodSignatureBuilder = new StringBuilder(method.getName());
|
||||||
|
appendExecutableParameters(method, methodSignatureBuilder);
|
||||||
|
String methodSignature = methodSignatureBuilder.toString();
|
||||||
|
|
||||||
|
description = "method '" + method.getDeclaringClass().getName() + "#" + methodSignature + "'";
|
||||||
|
} else if (object instanceof Constructor) {
|
||||||
|
description = "constructor '" + constructorToString((Constructor<?>) object) + "'";
|
||||||
|
} else {
|
||||||
|
description = "<unknown AccessibleObject> " + object.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uppercaseFirstLetter && Character.isLowerCase(description.charAt(0))) {
|
||||||
|
description = Character.toUpperCase(description.charAt(0)) + description.substring(1);
|
||||||
|
}
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a string representation for a constructor.
|
* Creates a string representation for a constructor.
|
||||||
* E.g.: {@code java.lang.String#String(char[], int, int)}
|
* E.g.: {@code java.lang.String(char[], int, int)}
|
||||||
*/
|
*/
|
||||||
private static String constructorToString(Constructor<?> constructor) {
|
public static String constructorToString(Constructor<?> constructor) {
|
||||||
StringBuilder stringBuilder = new StringBuilder(constructor.getDeclaringClass().getName())
|
StringBuilder stringBuilder = new StringBuilder(constructor.getDeclaringClass().getName());
|
||||||
.append('#')
|
appendExecutableParameters(constructor, stringBuilder);
|
||||||
.append(constructor.getDeclaringClass().getSimpleName())
|
|
||||||
.append('(');
|
return stringBuilder.toString();
|
||||||
Class<?>[] parameters = constructor.getParameterTypes();
|
}
|
||||||
|
|
||||||
|
// Note: Ideally parameter type would be java.lang.reflect.Executable, but that was added in Java 8
|
||||||
|
private static void appendExecutableParameters(AccessibleObject executable, StringBuilder stringBuilder) {
|
||||||
|
stringBuilder.append('(');
|
||||||
|
|
||||||
|
Class<?>[] parameters = (executable instanceof Method)
|
||||||
|
? ((Method) executable).getParameterTypes()
|
||||||
|
: ((Constructor<?>) executable).getParameterTypes();
|
||||||
for (int i = 0; i < parameters.length; i++) {
|
for (int i = 0; i < parameters.length; i++) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
stringBuilder.append(", ");
|
stringBuilder.append(", ");
|
||||||
@ -42,7 +100,7 @@ public class ReflectionHelper {
|
|||||||
stringBuilder.append(parameters[i].getSimpleName());
|
stringBuilder.append(parameters[i].getSimpleName());
|
||||||
}
|
}
|
||||||
|
|
||||||
return stringBuilder.append(')').toString();
|
stringBuilder.append(')');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,17 +116,155 @@ public class ReflectionHelper {
|
|||||||
constructor.setAccessible(true);
|
constructor.setAccessible(true);
|
||||||
return null;
|
return null;
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
return "Failed making constructor '" + constructorToString(constructor) + "' accessible; "
|
return "Failed making constructor '" + constructorToString(constructor) + "' accessible;"
|
||||||
+ "either change its visibility or write a custom InstanceCreator or TypeAdapter for its declaring type: "
|
+ " either increase its visibility or write a custom InstanceCreator or TypeAdapter for"
|
||||||
// Include the message since it might contain more detailed information
|
// Include the message since it might contain more detailed information
|
||||||
+ exception.getMessage();
|
+ " its declaring type: " + exception.getMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RuntimeException createExceptionForUnexpectedIllegalAccess(IllegalAccessException exception) {
|
/** If records are supported on the JVM, this is equivalent to a call to Class.isRecord() */
|
||||||
throw new RuntimeException("Unexpected IllegalAccessException occurred (Gson " + GsonBuildConfig.VERSION + "). "
|
public static boolean isRecord(Class<?> raw) {
|
||||||
+ "Certain ReflectionAccessFilter features require Java >= 9 to work correctly. If you are not using "
|
return RECORD_HELPER.isRecord(raw);
|
||||||
+ "ReflectionAccessFilter, report this to the Gson maintainers.",
|
}
|
||||||
|
|
||||||
|
public static String[] getRecordComponentNames(Class<?> raw) {
|
||||||
|
return RECORD_HELPER.getRecordComponentNames(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Looks up the record accessor method that corresponds to the given record field */
|
||||||
|
public static Method getAccessor(Class<?> raw, Field field) {
|
||||||
|
return RECORD_HELPER.getAccessor(raw, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Constructor<T> getCanonicalRecordConstructor(Class<T> raw) {
|
||||||
|
return RECORD_HELPER.getCanonicalRecordConstructor(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RuntimeException createExceptionForUnexpectedIllegalAccess(
|
||||||
|
IllegalAccessException exception) {
|
||||||
|
throw new RuntimeException("Unexpected IllegalAccessException occurred (Gson " + GsonBuildConfig.VERSION + ")."
|
||||||
|
+ " Certain ReflectionAccessFilter features require Java >= 9 to work correctly. If you are not using"
|
||||||
|
+ " ReflectionAccessFilter, report this to the Gson maintainers.",
|
||||||
exception);
|
exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static RuntimeException createExceptionForRecordReflectionException(
|
||||||
|
ReflectiveOperationException exception) {
|
||||||
|
throw new RuntimeException("Unexpected ReflectiveOperationException occurred"
|
||||||
|
+ " (Gson " + GsonBuildConfig.VERSION + ")."
|
||||||
|
+ " To support Java records, reflection is utilized to read out information"
|
||||||
|
+ " about records. All these invocations happens after it is established"
|
||||||
|
+ " that records exist in the JVM. This exception is unexpected behavior.",
|
||||||
|
exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal abstraction over reflection when Records are supported.
|
||||||
|
*/
|
||||||
|
private abstract static class RecordHelper {
|
||||||
|
abstract boolean isRecord(Class<?> clazz);
|
||||||
|
|
||||||
|
abstract String[] getRecordComponentNames(Class<?> clazz);
|
||||||
|
|
||||||
|
abstract <T> Constructor<T> getCanonicalRecordConstructor(Class<T> raw);
|
||||||
|
|
||||||
|
public abstract Method getAccessor(Class<?> raw, Field field);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class RecordSupportedHelper extends RecordHelper {
|
||||||
|
private final Method isRecord;
|
||||||
|
private final Method getRecordComponents;
|
||||||
|
private final Method getName;
|
||||||
|
private final Method getType;
|
||||||
|
|
||||||
|
private RecordSupportedHelper() throws NoSuchMethodException {
|
||||||
|
isRecord = Class.class.getMethod("isRecord");
|
||||||
|
getRecordComponents = Class.class.getMethod("getRecordComponents");
|
||||||
|
// Class java.lang.reflect.RecordComponent
|
||||||
|
Class<?> classRecordComponent = getRecordComponents.getReturnType().getComponentType();
|
||||||
|
getName = classRecordComponent.getMethod("getName");
|
||||||
|
getType = classRecordComponent.getMethod("getType");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isRecord(Class<?> raw) {
|
||||||
|
try {
|
||||||
|
return (boolean) isRecord.invoke(raw);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw createExceptionForRecordReflectionException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String[] getRecordComponentNames(Class<?> raw) {
|
||||||
|
try {
|
||||||
|
Object[] recordComponents = (Object[]) getRecordComponents.invoke(raw);
|
||||||
|
String[] componentNames = new String[recordComponents.length];
|
||||||
|
for (int i = 0; i < recordComponents.length; i++) {
|
||||||
|
componentNames[i] = (String) getName.invoke(recordComponents[i]);
|
||||||
|
}
|
||||||
|
return componentNames;
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw createExceptionForRecordReflectionException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Constructor<T> getCanonicalRecordConstructor(Class<T> raw) {
|
||||||
|
try {
|
||||||
|
Object[] recordComponents = (Object[]) getRecordComponents.invoke(raw);
|
||||||
|
Class<?>[] recordComponentTypes = new Class<?>[recordComponents.length];
|
||||||
|
for (int i = 0; i < recordComponents.length; i++) {
|
||||||
|
recordComponentTypes[i] = (Class<?>) getType.invoke(recordComponents[i]);
|
||||||
|
}
|
||||||
|
// Uses getDeclaredConstructor because implicit constructor has same visibility as record and might
|
||||||
|
// therefore not be public
|
||||||
|
return raw.getDeclaredConstructor(recordComponentTypes);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw createExceptionForRecordReflectionException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Method getAccessor(Class<?> raw, Field field) {
|
||||||
|
try {
|
||||||
|
// Records consists of record components, each with a unique name, a corresponding field and accessor method
|
||||||
|
// with the same name. Ref.: https://docs.oracle.com/javase/specs/jls/se17/html/jls-8.html#jls-8.10.3
|
||||||
|
return raw.getMethod(field.getName());
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw createExceptionForRecordReflectionException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance used when records are not supported
|
||||||
|
*/
|
||||||
|
private static class RecordNotSupportedHelper extends RecordHelper {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isRecord(Class<?> clazz) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String[] getRecordComponentNames(Class<?> clazz) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Records are not supported on this JVM, this method should not be called");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<T> Constructor<T> getCanonicalRecordConstructor(Class<T> raw) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Records are not supported on this JVM, this method should not be called");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Method getAccessor(Class<?> raw, Field field) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Records are not supported on this JVM, this method should not be called");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ import java.util.Objects;
|
|||||||
* runtime.
|
* runtime.
|
||||||
*
|
*
|
||||||
* <p>For example, to create a type literal for {@code List<String>}, you can
|
* <p>For example, to create a type literal for {@code List<String>}, you can
|
||||||
* create an empty anonymous inner class:
|
* create an empty anonymous class:
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* {@code TypeToken<List<String>> list = new TypeToken<List<String>>() {};}
|
* {@code TypeToken<List<String>> list = new TypeToken<List<String>>() {};}
|
||||||
@ -43,6 +43,11 @@ import java.util.Objects;
|
|||||||
* might expect, which gives a false sense of type-safety at compilation time
|
* might expect, which gives a false sense of type-safety at compilation time
|
||||||
* and can lead to an unexpected {@code ClassCastException} at runtime.
|
* and can lead to an unexpected {@code ClassCastException} at runtime.
|
||||||
*
|
*
|
||||||
|
* <p>If the type arguments of the parameterized type are only available at
|
||||||
|
* runtime, for example when you want to create a {@code List<E>} based on
|
||||||
|
* a {@code Class<E>} representing the element type, the method
|
||||||
|
* {@link #getParameterized(Type, Type...)} can be used.
|
||||||
|
*
|
||||||
* @author Bob Lee
|
* @author Bob Lee
|
||||||
* @author Sven Mawson
|
* @author Sven Mawson
|
||||||
* @author Jesse Wilson
|
* @author Jesse Wilson
|
||||||
@ -317,8 +322,17 @@ public class TypeToken<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets type literal for the parameterized type represented by applying {@code typeArguments} to
|
* Gets a type literal for the parameterized type represented by applying {@code typeArguments} to
|
||||||
* {@code rawType}.
|
* {@code rawType}. This is mainly intended for situations where the type arguments are not
|
||||||
|
* available at compile time. The following example shows how a type token for {@code Map<K, V>}
|
||||||
|
* can be created:
|
||||||
|
* <pre>{@code
|
||||||
|
* Class<K> keyClass = ...;
|
||||||
|
* Class<V> valueClass = ...;
|
||||||
|
* TypeToken<?> mapTypeToken = TypeToken.getParameterized(Map.class, keyClass, valueClass);
|
||||||
|
* }</pre>
|
||||||
|
* As seen here the result is a {@code TypeToken<?>}; this method cannot provide any type safety,
|
||||||
|
* and care must be taken to pass in the correct number of type arguments.
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException
|
* @throws IllegalArgumentException
|
||||||
* If {@code rawType} is not of type {@code Class}, or if the type arguments are invalid for
|
* If {@code rawType} is not of type {@code Class}, or if the type arguments are invalid for
|
||||||
|
@ -33,7 +33,7 @@ import java.util.Objects;
|
|||||||
* depth-first order, the same order that they appear in the JSON document.
|
* depth-first order, the same order that they appear in the JSON document.
|
||||||
* Within JSON objects, name/value pairs are represented by a single token.
|
* Within JSON objects, name/value pairs are represented by a single token.
|
||||||
*
|
*
|
||||||
* <h3>Parsing JSON</h3>
|
* <h2>Parsing JSON</h2>
|
||||||
* To create a recursive descent parser for your own JSON streams, first create
|
* To create a recursive descent parser for your own JSON streams, first create
|
||||||
* an entry point method that creates a {@code JsonReader}.
|
* an entry point method that creates a {@code JsonReader}.
|
||||||
*
|
*
|
||||||
@ -62,7 +62,7 @@ import java.util.Objects;
|
|||||||
* Null literals can be consumed using either {@link #nextNull()} or {@link
|
* Null literals can be consumed using either {@link #nextNull()} or {@link
|
||||||
* #skipValue()}.
|
* #skipValue()}.
|
||||||
*
|
*
|
||||||
* <h3>Example</h3>
|
* <h2>Example</h2>
|
||||||
* Suppose we'd like to parse a stream of messages such as the following: <pre> {@code
|
* Suppose we'd like to parse a stream of messages such as the following: <pre> {@code
|
||||||
* [
|
* [
|
||||||
* {
|
* {
|
||||||
@ -161,7 +161,7 @@ import java.util.Objects;
|
|||||||
* return new User(username, followersCount);
|
* return new User(username, followersCount);
|
||||||
* }}</pre>
|
* }}</pre>
|
||||||
*
|
*
|
||||||
* <h3>Number Handling</h3>
|
* <h2>Number Handling</h2>
|
||||||
* This reader permits numeric values to be read as strings and string values to
|
* This reader permits numeric values to be read as strings and string values to
|
||||||
* be read as numbers. For example, both elements of the JSON array {@code
|
* be read as numbers. For example, both elements of the JSON array {@code
|
||||||
* [1, "1"]} may be read using either {@link #nextInt} or {@link #nextString}.
|
* [1, "1"]} may be read using either {@link #nextInt} or {@link #nextString}.
|
||||||
@ -171,7 +171,7 @@ import java.util.Objects;
|
|||||||
* precision loss, extremely large values should be written and read as strings
|
* precision loss, extremely large values should be written and read as strings
|
||||||
* in JSON.
|
* in JSON.
|
||||||
*
|
*
|
||||||
* <h3 id="nonexecuteprefix">Non-Execute Prefix</h3>
|
* <h2 id="nonexecuteprefix">Non-Execute Prefix</h2>
|
||||||
* Web servers that serve private data using JSON may be vulnerable to <a
|
* Web servers that serve private data using JSON may be vulnerable to <a
|
||||||
* href="http://en.wikipedia.org/wiki/JSON#Cross-site_request_forgery">Cross-site
|
* href="http://en.wikipedia.org/wiki/JSON#Cross-site_request_forgery">Cross-site
|
||||||
* request forgery</a> attacks. In such an attack, a malicious site gains access
|
* request forgery</a> attacks. In such an attack, a malicious site gains access
|
||||||
@ -229,6 +229,8 @@ public class JsonReader implements Closeable {
|
|||||||
/** True to accept non-spec compliant JSON */
|
/** True to accept non-spec compliant JSON */
|
||||||
private boolean lenient = DefaultConfig.DEFAULT_LENIENT;
|
private boolean lenient = DefaultConfig.DEFAULT_LENIENT;
|
||||||
|
|
||||||
|
private boolean serializeSpecialFloatingPointValues = DefaultConfig.DEFAULT_SPECIALIZE_FLOAT_VALUES;
|
||||||
|
|
||||||
static final int BUFFER_SIZE = 1024;
|
static final int BUFFER_SIZE = 1024;
|
||||||
/**
|
/**
|
||||||
* Use a manual buffer to easily read and unread upcoming characters, and
|
* Use a manual buffer to easily read and unread upcoming characters, and
|
||||||
@ -331,6 +333,7 @@ public class JsonReader implements Closeable {
|
|||||||
*/
|
*/
|
||||||
public final void setLenient(boolean lenient) {
|
public final void setLenient(boolean lenient) {
|
||||||
this.lenient = lenient;
|
this.lenient = lenient;
|
||||||
|
if (lenient) serializeSpecialFloatingPointValues = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -340,6 +343,14 @@ public class JsonReader implements Closeable {
|
|||||||
return lenient;
|
return lenient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSerializeSpecialFloatingPointValues(boolean serializeSpecialFloatingPointValues) {
|
||||||
|
this.serializeSpecialFloatingPointValues = serializeSpecialFloatingPointValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSerializeSpecialFloatingPointValues() {
|
||||||
|
return serializeSpecialFloatingPointValues;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consumes the next token from the JSON stream and asserts that it is the
|
* Consumes the next token from the JSON stream and asserts that it is the
|
||||||
* beginning of a new array.
|
* beginning of a new array.
|
||||||
@ -788,10 +799,9 @@ public class JsonReader implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the next token, a {@link com.google.gson.stream.JsonToken#NAME property name}, and
|
* Returns the next token, a {@link JsonToken#NAME property name}, and consumes it.
|
||||||
* consumes it.
|
|
||||||
*
|
*
|
||||||
* @throws java.io.IOException if the next token in the stream is not a property
|
* @throws IOException if the next token in the stream is not a property
|
||||||
* name.
|
* name.
|
||||||
*/
|
*/
|
||||||
public String nextName() throws IOException {
|
public String nextName() throws IOException {
|
||||||
@ -815,7 +825,7 @@ public class JsonReader implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link com.google.gson.stream.JsonToken#STRING string} value of the next token,
|
* Returns the {@link JsonToken#STRING string} value of the next token,
|
||||||
* consuming it. If the next token is a number, this method will return its
|
* consuming it. If the next token is a number, this method will return its
|
||||||
* string form.
|
* string form.
|
||||||
*
|
*
|
||||||
@ -851,7 +861,7 @@ public class JsonReader implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link com.google.gson.stream.JsonToken#BOOLEAN boolean} value of the next token,
|
* Returns the {@link JsonToken#BOOLEAN boolean} value of the next token,
|
||||||
* consuming it.
|
* consuming it.
|
||||||
*
|
*
|
||||||
* @throws IllegalStateException if the next token is not a boolean or if
|
* @throws IllegalStateException if the next token is not a boolean or if
|
||||||
@ -895,7 +905,7 @@ public class JsonReader implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link com.google.gson.stream.JsonToken#NUMBER double} value of the next token,
|
* Returns the {@link JsonToken#NUMBER double} value of the next token,
|
||||||
* consuming it. If the next token is a string, this method will attempt to
|
* consuming it. If the next token is a string, this method will attempt to
|
||||||
* parse it as a double using {@link Double#parseDouble(String)}.
|
* parse it as a double using {@link Double#parseDouble(String)}.
|
||||||
*
|
*
|
||||||
@ -930,7 +940,7 @@ public class JsonReader implements Closeable {
|
|||||||
|
|
||||||
peeked = PEEKED_BUFFERED;
|
peeked = PEEKED_BUFFERED;
|
||||||
double result = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
|
double result = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
|
||||||
if (!lenient && (Double.isNaN(result) || Double.isInfinite(result))) {
|
if (!serializeSpecialFloatingPointValues && (Double.isNaN(result) || Double.isInfinite(result))) {
|
||||||
throw new MalformedJsonException(
|
throw new MalformedJsonException(
|
||||||
"JSON forbids NaN and infinities: " + result + locationString());
|
"JSON forbids NaN and infinities: " + result + locationString());
|
||||||
}
|
}
|
||||||
@ -941,7 +951,7 @@ public class JsonReader implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link com.google.gson.stream.JsonToken#NUMBER long} value of the next token,
|
* Returns the {@link JsonToken#NUMBER long} value of the next token,
|
||||||
* consuming it. If the next token is a string, this method will attempt to
|
* consuming it. If the next token is a string, this method will attempt to
|
||||||
* parse it as a long. If the next token's numeric value cannot be exactly
|
* parse it as a long. If the next token's numeric value cannot be exactly
|
||||||
* represented by a Java {@code long}, this method throws.
|
* represented by a Java {@code long}, this method throws.
|
||||||
@ -1174,7 +1184,7 @@ public class JsonReader implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link com.google.gson.stream.JsonToken#NUMBER int} value of the next token,
|
* Returns the {@link JsonToken#NUMBER int} value of the next token,
|
||||||
* consuming it. If the next token is a string, this method will attempt to
|
* consuming it. If the next token is a string, this method will attempt to
|
||||||
* parse it as an int. If the next token's numeric value cannot be exactly
|
* parse it as an int. If the next token's numeric value cannot be exactly
|
||||||
* represented by a Java {@code int}, this method throws.
|
* represented by a Java {@code int}, this method throws.
|
||||||
@ -1238,7 +1248,7 @@ public class JsonReader implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes this JSON reader and the underlying {@link java.io.Reader}.
|
* Closes this JSON reader and the underlying {@link Reader}.
|
||||||
*/
|
*/
|
||||||
@Override public void close() throws IOException {
|
@Override public void close() throws IOException {
|
||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
@ -1248,9 +1258,18 @@ public class JsonReader implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skips the next value recursively. If it is an object or array, all nested
|
* Skips the next value recursively. This method is intended for use when
|
||||||
* elements are skipped. This method is intended for use when the JSON token
|
* the JSON token stream contains unrecognized or unhandled values.
|
||||||
* stream contains unrecognized or unhandled values.
|
*
|
||||||
|
* <p>The behavior depends on the type of the next JSON token:
|
||||||
|
* <ul>
|
||||||
|
* <li>Start of a JSON array or object: It and all of its nested values are skipped.</li>
|
||||||
|
* <li>Primitive value (for example a JSON number): The primitive value is skipped.</li>
|
||||||
|
* <li>Property name: Only the name but not the value of the property is skipped.
|
||||||
|
* {@code skipValue()} has to be called again to skip the property value as well.</li>
|
||||||
|
* <li>End of a JSON array or object: Only this end token is skipped and an exception is thrown.</li>
|
||||||
|
* <li>End of JSON document: Skipping has no effect on the state, but an exception is thrown.</li>
|
||||||
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public void skipValue() throws IOException {
|
public void skipValue() throws IOException {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
@ -1260,33 +1279,69 @@ public class JsonReader implements Closeable {
|
|||||||
p = doPeek();
|
p = doPeek();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p == PEEKED_BEGIN_ARRAY) {
|
switch (p) {
|
||||||
|
case PEEKED_BEGIN_ARRAY:
|
||||||
push(JsonScope.EMPTY_ARRAY);
|
push(JsonScope.EMPTY_ARRAY);
|
||||||
count++;
|
count++;
|
||||||
} else if (p == PEEKED_BEGIN_OBJECT) {
|
break;
|
||||||
|
case PEEKED_BEGIN_OBJECT:
|
||||||
push(JsonScope.EMPTY_OBJECT);
|
push(JsonScope.EMPTY_OBJECT);
|
||||||
count++;
|
count++;
|
||||||
} else if (p == PEEKED_END_ARRAY) {
|
break;
|
||||||
|
case PEEKED_END_ARRAY:
|
||||||
stackSize--;
|
stackSize--;
|
||||||
count--;
|
count--;
|
||||||
} else if (p == PEEKED_END_OBJECT) {
|
break;
|
||||||
|
case PEEKED_END_OBJECT:
|
||||||
|
// Only update when object end is explicitly skipped, otherwise stack is not updated anyways
|
||||||
|
if (count == 0) {
|
||||||
|
pathNames[stackSize - 1] = null; // Free the last path name so that it can be garbage collected
|
||||||
|
}
|
||||||
stackSize--;
|
stackSize--;
|
||||||
count--;
|
count--;
|
||||||
} else if (p == PEEKED_UNQUOTED_NAME || p == PEEKED_UNQUOTED) {
|
break;
|
||||||
|
case PEEKED_UNQUOTED:
|
||||||
skipUnquotedValue();
|
skipUnquotedValue();
|
||||||
} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_SINGLE_QUOTED_NAME) {
|
break;
|
||||||
|
case PEEKED_SINGLE_QUOTED:
|
||||||
skipQuotedValue('\'');
|
skipQuotedValue('\'');
|
||||||
} else if (p == PEEKED_DOUBLE_QUOTED || p == PEEKED_DOUBLE_QUOTED_NAME) {
|
break;
|
||||||
|
case PEEKED_DOUBLE_QUOTED:
|
||||||
skipQuotedValue('"');
|
skipQuotedValue('"');
|
||||||
} else if (p == PEEKED_NUMBER) {
|
break;
|
||||||
|
case PEEKED_UNQUOTED_NAME:
|
||||||
|
skipUnquotedValue();
|
||||||
|
// Only update when name is explicitly skipped, otherwise stack is not updated anyways
|
||||||
|
if (count == 0) {
|
||||||
|
pathNames[stackSize - 1] = "<skipped>";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PEEKED_SINGLE_QUOTED_NAME:
|
||||||
|
skipQuotedValue('\'');
|
||||||
|
// Only update when name is explicitly skipped, otherwise stack is not updated anyways
|
||||||
|
if (count == 0) {
|
||||||
|
pathNames[stackSize - 1] = "<skipped>";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PEEKED_DOUBLE_QUOTED_NAME:
|
||||||
|
skipQuotedValue('"');
|
||||||
|
// Only update when name is explicitly skipped, otherwise stack is not updated anyways
|
||||||
|
if (count == 0) {
|
||||||
|
pathNames[stackSize - 1] = "<skipped>";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PEEKED_NUMBER:
|
||||||
pos += peekedNumberLength;
|
pos += peekedNumberLength;
|
||||||
|
break;
|
||||||
|
case PEEKED_EOF:
|
||||||
|
throw new IllegalStateException("Attempt to skip led outside the document");
|
||||||
|
// For all other tokens there is nothing to do; token has already been consumed from underlying reader
|
||||||
}
|
}
|
||||||
peeked = PEEKED_NONE;
|
peeked = PEEKED_NONE;
|
||||||
} while (count > 0);
|
} while (count > 0);
|
||||||
if (count < 0) throw new IllegalStateException("Attempt to skip led outside its parent");
|
|
||||||
|
|
||||||
pathIndices[stackSize - 1]++;
|
pathIndices[stackSize - 1]++;
|
||||||
pathNames[stackSize - 1] = "null";
|
if (count < 0) throw new IllegalStateException("Attempt to skip led outside its parent");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void push(int newTop) {
|
private void push(int newTop) {
|
||||||
@ -1522,7 +1577,7 @@ public class JsonReader implements Closeable {
|
|||||||
* <li>For JSON arrays the path points to the index of the previous element.<br>
|
* <li>For JSON arrays the path points to the index of the previous element.<br>
|
||||||
* If no element has been consumed yet it uses the index 0 (even if there are no elements).</li>
|
* If no element has been consumed yet it uses the index 0 (even if there are no elements).</li>
|
||||||
* <li>For JSON objects the path points to the last property, or to the current
|
* <li>For JSON objects the path points to the last property, or to the current
|
||||||
* property if its value has not been consumed yet.</li>
|
* property if its name has already been consumed.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>This method can be useful to add additional context to exception messages
|
* <p>This method can be useful to add additional context to exception messages
|
||||||
@ -1539,7 +1594,7 @@ public class JsonReader implements Closeable {
|
|||||||
* <li>For JSON arrays the path points to the index of the next element (even
|
* <li>For JSON arrays the path points to the index of the next element (even
|
||||||
* if there are no further elements).</li>
|
* if there are no further elements).</li>
|
||||||
* <li>For JSON objects the path points to the last property, or to the current
|
* <li>For JSON objects the path points to the last property, or to the current
|
||||||
* property if its value has not been consumed yet.</li>
|
* property if its name has already been consumed.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>This method can be useful to add additional context to exception messages
|
* <p>This method can be useful to add additional context to exception messages
|
||||||
|
@ -43,7 +43,7 @@ import java.util.regex.Pattern;
|
|||||||
* literal values (strings, numbers, booleans and nulls) as well as the begin
|
* literal values (strings, numbers, booleans and nulls) as well as the begin
|
||||||
* and end delimiters of objects and arrays.
|
* and end delimiters of objects and arrays.
|
||||||
*
|
*
|
||||||
* <h3>Encoding JSON</h3>
|
* <h2>Encoding JSON</h2>
|
||||||
* To encode your data as JSON, create a new {@code JsonWriter}. Call methods
|
* To encode your data as JSON, create a new {@code JsonWriter}. Call methods
|
||||||
* on the writer as you walk the structure's contents, nesting arrays and objects
|
* on the writer as you walk the structure's contents, nesting arrays and objects
|
||||||
* as necessary:
|
* as necessary:
|
||||||
@ -59,7 +59,7 @@ import java.util.regex.Pattern;
|
|||||||
* Finally close the object using {@link #endObject()}.
|
* Finally close the object using {@link #endObject()}.
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <h3>Example</h3>
|
* <h2>Example</h2>
|
||||||
* Suppose we'd like to encode a stream of messages such as the following: <pre> {@code
|
* Suppose we'd like to encode a stream of messages such as the following: <pre> {@code
|
||||||
* [
|
* [
|
||||||
* {
|
* {
|
||||||
@ -193,6 +193,8 @@ public class JsonWriter implements Closeable, Flushable {
|
|||||||
|
|
||||||
private boolean lenient = DefaultConfig.DEFAULT_LENIENT;
|
private boolean lenient = DefaultConfig.DEFAULT_LENIENT;
|
||||||
|
|
||||||
|
private boolean serializeSpecialFloatingPointValues = DefaultConfig.DEFAULT_SPECIALIZE_FLOAT_VALUES;
|
||||||
|
|
||||||
private boolean omitQuotes = DefaultConfig.DEFAULT_OMIT_QUOTES;
|
private boolean omitQuotes = DefaultConfig.DEFAULT_OMIT_QUOTES;
|
||||||
|
|
||||||
private boolean htmlSafe;
|
private boolean htmlSafe;
|
||||||
@ -242,6 +244,7 @@ public class JsonWriter implements Closeable, Flushable {
|
|||||||
*/
|
*/
|
||||||
public final void setLenient(boolean lenient) {
|
public final void setLenient(boolean lenient) {
|
||||||
this.lenient = lenient;
|
this.lenient = lenient;
|
||||||
|
if (lenient) this.serializeSpecialFloatingPointValues = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -251,6 +254,14 @@ public class JsonWriter implements Closeable, Flushable {
|
|||||||
return lenient;
|
return lenient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSerializeSpecialFloatingPointValues(boolean serializeSpecialFloatingPointValues) {
|
||||||
|
this.serializeSpecialFloatingPointValues = serializeSpecialFloatingPointValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSerializeSpecialFloatingPointValues() {
|
||||||
|
return serializeSpecialFloatingPointValues;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure this writer to emit JSON that's safe for direct inclusion in HTML
|
* Configure this writer to emit JSON that's safe for direct inclusion in HTML
|
||||||
* and XML documents. This escapes the HTML characters {@code <}, {@code >},
|
* and XML documents. This escapes the HTML characters {@code <}, {@code >},
|
||||||
@ -490,6 +501,7 @@ public class JsonWriter implements Closeable, Flushable {
|
|||||||
* @return this writer.
|
* @return this writer.
|
||||||
* @throws UnsupportedOperationException if this writer does not support
|
* @throws UnsupportedOperationException if this writer does not support
|
||||||
* writing raw JSON values.
|
* writing raw JSON values.
|
||||||
|
* @since 2.4
|
||||||
*/
|
*/
|
||||||
public JsonWriter jsonValue(String value) throws IOException {
|
public JsonWriter jsonValue(String value) throws IOException {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
@ -536,6 +548,7 @@ public class JsonWriter implements Closeable, Flushable {
|
|||||||
* Encodes {@code value}.
|
* Encodes {@code value}.
|
||||||
*
|
*
|
||||||
* @return this writer.
|
* @return this writer.
|
||||||
|
* @since 2.7
|
||||||
*/
|
*/
|
||||||
public JsonWriter value(Boolean value) throws IOException {
|
public JsonWriter value(Boolean value) throws IOException {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
@ -556,10 +569,11 @@ public class JsonWriter implements Closeable, Flushable {
|
|||||||
* @return this writer.
|
* @return this writer.
|
||||||
* @throws IllegalArgumentException if the value is NaN or Infinity and this writer is not {@link
|
* @throws IllegalArgumentException if the value is NaN or Infinity and this writer is not {@link
|
||||||
* #setLenient(boolean) lenient}.
|
* #setLenient(boolean) lenient}.
|
||||||
|
* @since 2.9.1
|
||||||
*/
|
*/
|
||||||
public JsonWriter value(float value) throws IOException {
|
public JsonWriter value(float value) throws IOException {
|
||||||
writeDeferredName();
|
writeDeferredName();
|
||||||
if (!lenient && (Float.isNaN(value) || Float.isInfinite(value))) {
|
if (!serializeSpecialFloatingPointValues && (Float.isNaN(value) || Float.isInfinite(value))) {
|
||||||
throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
|
throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
|
||||||
}
|
}
|
||||||
beforeValue();
|
beforeValue();
|
||||||
@ -578,7 +592,7 @@ public class JsonWriter implements Closeable, Flushable {
|
|||||||
*/
|
*/
|
||||||
public JsonWriter value(double value) throws IOException {
|
public JsonWriter value(double value) throws IOException {
|
||||||
writeDeferredName();
|
writeDeferredName();
|
||||||
if (!lenient && (Double.isNaN(value) || Double.isInfinite(value))) {
|
if (!serializeSpecialFloatingPointValues && (Double.isNaN(value) || Double.isInfinite(value))) {
|
||||||
throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
|
throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
|
||||||
}
|
}
|
||||||
beforeValue();
|
beforeValue();
|
||||||
@ -628,7 +642,7 @@ public class JsonWriter implements Closeable, Flushable {
|
|||||||
writeDeferredName();
|
writeDeferredName();
|
||||||
String string = value.toString();
|
String string = value.toString();
|
||||||
if (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN")) {
|
if (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN")) {
|
||||||
if (!lenient) {
|
if (!serializeSpecialFloatingPointValues) {
|
||||||
throw new IllegalArgumentException("Numeric values must be finite, but was " + string);
|
throw new IllegalArgumentException("Numeric values must be finite, but was " + string);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -16,20 +16,25 @@
|
|||||||
|
|
||||||
package com.google.gson;
|
package com.google.gson;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNotSame;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import com.google.gson.stream.JsonReader;
|
import com.google.gson.stream.JsonReader;
|
||||||
import com.google.gson.stream.JsonWriter;
|
import com.google.gson.stream.JsonWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import junit.framework.TestCase;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link GsonBuilder}.
|
* Unit tests for {@link GsonBuilder}.
|
||||||
*
|
*
|
||||||
* @author Inderjeet Singh
|
* @author Inderjeet Singh
|
||||||
*/
|
*/
|
||||||
public class GsonBuilderTest extends TestCase {
|
public class GsonBuilderTest {
|
||||||
private static final TypeAdapter<Object> NULL_TYPE_ADAPTER = new TypeAdapter<Object>() {
|
private static final TypeAdapter<Object> NULL_TYPE_ADAPTER = new TypeAdapter<Object>() {
|
||||||
@Override public void write(JsonWriter out, Object value) {
|
@Override public void write(JsonWriter out, Object value) {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
@ -39,6 +44,7 @@ public class GsonBuilderTest extends TestCase {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testCreatingMoreThanOnce() {
|
public void testCreatingMoreThanOnce() {
|
||||||
GsonBuilder builder = new GsonBuilder();
|
GsonBuilder builder = new GsonBuilder();
|
||||||
Gson gson = builder.create();
|
Gson gson = builder.create();
|
||||||
@ -61,6 +67,7 @@ public class GsonBuilderTest extends TestCase {
|
|||||||
* Gson instances should not be affected by subsequent modification of GsonBuilder
|
* Gson instances should not be affected by subsequent modification of GsonBuilder
|
||||||
* which created them.
|
* which created them.
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testModificationAfterCreate() {
|
public void testModificationAfterCreate() {
|
||||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||||
Gson gson = gsonBuilder.create();
|
Gson gson = gsonBuilder.create();
|
||||||
@ -136,6 +143,7 @@ public class GsonBuilderTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testExcludeFieldsWithModifiers() {
|
public void testExcludeFieldsWithModifiers() {
|
||||||
Gson gson = new GsonBuilder()
|
Gson gson = new GsonBuilder()
|
||||||
.excludeFieldsWithModifiers(Modifier.VOLATILE, Modifier.PRIVATE)
|
.excludeFieldsWithModifiers(Modifier.VOLATILE, Modifier.PRIVATE)
|
||||||
@ -151,6 +159,7 @@ public class GsonBuilderTest extends TestCase {
|
|||||||
String d = "d";
|
String d = "d";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testTransientFieldExclusion() {
|
public void testTransientFieldExclusion() {
|
||||||
Gson gson = new GsonBuilder()
|
Gson gson = new GsonBuilder()
|
||||||
.excludeFieldsWithModifiers()
|
.excludeFieldsWithModifiers()
|
||||||
@ -162,6 +171,7 @@ public class GsonBuilderTest extends TestCase {
|
|||||||
transient String a = "a";
|
transient String a = "a";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testRegisterTypeAdapterForCoreType() {
|
public void testRegisterTypeAdapterForCoreType() {
|
||||||
Type[] types = {
|
Type[] types = {
|
||||||
byte.class,
|
byte.class,
|
||||||
@ -176,6 +186,7 @@ public class GsonBuilderTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDisableJdkUnsafe() {
|
public void testDisableJdkUnsafe() {
|
||||||
Gson gson = new GsonBuilder()
|
Gson gson = new GsonBuilder()
|
||||||
.disableJdkUnsafe()
|
.disableJdkUnsafe()
|
||||||
@ -198,4 +209,22 @@ public class GsonBuilderTest extends TestCase {
|
|||||||
public ClassWithoutNoArgsConstructor(String s) {
|
public ClassWithoutNoArgsConstructor(String s) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetVersionInvalid() {
|
||||||
|
GsonBuilder builder = new GsonBuilder();
|
||||||
|
try {
|
||||||
|
builder.setVersion(Double.NaN);
|
||||||
|
fail();
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
assertEquals("Invalid version: NaN", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
builder.setVersion(-0.1);
|
||||||
|
fail();
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
assertEquals("Invalid version: -0.1", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
285
gson/src/test/java/com/google/gson/JsonArrayAsListTest.java
Normal file
285
gson/src/test/java/com/google/gson/JsonArrayAsListTest.java
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
package com.google.gson;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import com.google.gson.common.MoreAsserts;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JsonArray#asList()}.
|
||||||
|
*/
|
||||||
|
public class JsonArrayAsListTest {
|
||||||
|
@Test
|
||||||
|
public void testGet() {
|
||||||
|
JsonArray a = new JsonArray();
|
||||||
|
a.add(1);
|
||||||
|
|
||||||
|
List<JsonElement> list = a.asList();
|
||||||
|
assertEquals(new JsonPrimitive(1), list.get(0));
|
||||||
|
|
||||||
|
try {
|
||||||
|
list.get(-1);
|
||||||
|
fail();
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
list.get(2);
|
||||||
|
fail();
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
a.add((JsonElement) null);
|
||||||
|
assertEquals(JsonNull.INSTANCE, list.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSize() {
|
||||||
|
JsonArray a = new JsonArray();
|
||||||
|
a.add(1);
|
||||||
|
|
||||||
|
List<JsonElement> list = a.asList();
|
||||||
|
assertEquals(1, list.size());
|
||||||
|
list.add(new JsonPrimitive(2));
|
||||||
|
assertEquals(2, list.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSet() {
|
||||||
|
JsonArray a = new JsonArray();
|
||||||
|
a.add(1);
|
||||||
|
|
||||||
|
List<JsonElement> list = a.asList();
|
||||||
|
JsonElement old = list.set(0, new JsonPrimitive(2));
|
||||||
|
assertEquals(new JsonPrimitive(1), old);
|
||||||
|
assertEquals(new JsonPrimitive(2), list.get(0));
|
||||||
|
assertEquals(new JsonPrimitive(2), a.get(0));
|
||||||
|
|
||||||
|
try {
|
||||||
|
list.set(-1, new JsonPrimitive(1));
|
||||||
|
fail();
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
list.set(2, new JsonPrimitive(1));
|
||||||
|
fail();
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
list.set(0, null);
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("Element must be non-null", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAdd() {
|
||||||
|
JsonArray a = new JsonArray();
|
||||||
|
a.add(1);
|
||||||
|
|
||||||
|
List<JsonElement> list = a.asList();
|
||||||
|
list.add(0, new JsonPrimitive(2));
|
||||||
|
list.add(1, new JsonPrimitive(3));
|
||||||
|
assertTrue(list.add(new JsonPrimitive(4)));
|
||||||
|
assertTrue(list.add(JsonNull.INSTANCE));
|
||||||
|
|
||||||
|
List<JsonElement> expectedList = Arrays.<JsonElement>asList(
|
||||||
|
new JsonPrimitive(2),
|
||||||
|
new JsonPrimitive(3),
|
||||||
|
new JsonPrimitive(1),
|
||||||
|
new JsonPrimitive(4),
|
||||||
|
JsonNull.INSTANCE
|
||||||
|
);
|
||||||
|
assertEquals(expectedList, list);
|
||||||
|
|
||||||
|
try {
|
||||||
|
list.set(-1, new JsonPrimitive(1));
|
||||||
|
fail();
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
list.set(list.size(), new JsonPrimitive(1));
|
||||||
|
fail();
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
list.add(0, null);
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("Element must be non-null", e.getMessage());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
list.add(null);
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("Element must be non-null", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddAll() {
|
||||||
|
JsonArray a = new JsonArray();
|
||||||
|
a.add(1);
|
||||||
|
|
||||||
|
List<JsonElement> list = a.asList();
|
||||||
|
list.addAll(Arrays.asList(new JsonPrimitive(2), new JsonPrimitive(3)));
|
||||||
|
|
||||||
|
List<JsonElement> expectedList = Arrays.<JsonElement>asList(
|
||||||
|
new JsonPrimitive(1),
|
||||||
|
new JsonPrimitive(2),
|
||||||
|
new JsonPrimitive(3)
|
||||||
|
);
|
||||||
|
assertEquals(expectedList, list);
|
||||||
|
|
||||||
|
try {
|
||||||
|
list.addAll(0, Collections.<JsonElement>singletonList(null));
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("Element must be non-null", e.getMessage());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
list.addAll(Collections.<JsonElement>singletonList(null));
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("Element must be non-null", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveIndex() {
|
||||||
|
JsonArray a = new JsonArray();
|
||||||
|
a.add(1);
|
||||||
|
|
||||||
|
List<JsonElement> list = a.asList();
|
||||||
|
assertEquals(new JsonPrimitive(1), list.remove(0));
|
||||||
|
assertEquals(0, list.size());
|
||||||
|
assertEquals(0, a.size());
|
||||||
|
|
||||||
|
try {
|
||||||
|
list.remove(0);
|
||||||
|
fail();
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveElement() {
|
||||||
|
JsonArray a = new JsonArray();
|
||||||
|
a.add(1);
|
||||||
|
|
||||||
|
List<JsonElement> list = a.asList();
|
||||||
|
assertTrue(list.remove(new JsonPrimitive(1)));
|
||||||
|
assertEquals(0, list.size());
|
||||||
|
assertEquals(0, a.size());
|
||||||
|
|
||||||
|
assertFalse(list.remove(new JsonPrimitive(1)));
|
||||||
|
assertFalse(list.remove(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClear() {
|
||||||
|
JsonArray a = new JsonArray();
|
||||||
|
a.add(1);
|
||||||
|
|
||||||
|
List<JsonElement> list = a.asList();
|
||||||
|
list.clear();
|
||||||
|
assertEquals(0, list.size());
|
||||||
|
assertEquals(0, a.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContains() {
|
||||||
|
JsonArray a = new JsonArray();
|
||||||
|
a.add(1);
|
||||||
|
|
||||||
|
List<JsonElement> list = a.asList();
|
||||||
|
assertTrue(list.contains(new JsonPrimitive(1)));
|
||||||
|
assertFalse(list.contains(new JsonPrimitive(2)));
|
||||||
|
assertFalse(list.contains(null));
|
||||||
|
|
||||||
|
@SuppressWarnings({"unlikely-arg-type", "CollectionIncompatibleType"})
|
||||||
|
boolean containsInt = list.contains(1); // should only contain JsonPrimitive(1)
|
||||||
|
assertFalse(containsInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIndexOf() {
|
||||||
|
JsonArray a = new JsonArray();
|
||||||
|
// Add the same value twice to test indexOf vs. lastIndexOf
|
||||||
|
a.add(1);
|
||||||
|
a.add(1);
|
||||||
|
|
||||||
|
List<JsonElement> list = a.asList();
|
||||||
|
assertEquals(0, list.indexOf(new JsonPrimitive(1)));
|
||||||
|
assertEquals(-1, list.indexOf(new JsonPrimitive(2)));
|
||||||
|
assertEquals(-1, list.indexOf(null));
|
||||||
|
|
||||||
|
@SuppressWarnings({"unlikely-arg-type", "CollectionIncompatibleType"})
|
||||||
|
int indexOfInt = list.indexOf(1); // should only contain JsonPrimitive(1)
|
||||||
|
assertEquals(-1, indexOfInt);
|
||||||
|
|
||||||
|
assertEquals(1, list.lastIndexOf(new JsonPrimitive(1)));
|
||||||
|
assertEquals(-1, list.lastIndexOf(new JsonPrimitive(2)));
|
||||||
|
assertEquals(-1, list.lastIndexOf(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToArray() {
|
||||||
|
JsonArray a = new JsonArray();
|
||||||
|
a.add(1);
|
||||||
|
|
||||||
|
List<JsonElement> list = a.asList();
|
||||||
|
assertArrayEquals(new Object[] {new JsonPrimitive(1)}, list.toArray());
|
||||||
|
|
||||||
|
JsonElement[] array = list.toArray(new JsonElement[0]);
|
||||||
|
assertArrayEquals(new Object[] {new JsonPrimitive(1)}, array);
|
||||||
|
|
||||||
|
array = new JsonElement[1];
|
||||||
|
assertSame(array, list.toArray(array));
|
||||||
|
assertArrayEquals(new Object[] {new JsonPrimitive(1)}, array);
|
||||||
|
|
||||||
|
array = new JsonElement[] {null, new JsonPrimitive(2)};
|
||||||
|
assertSame(array, list.toArray(array));
|
||||||
|
// Should have set existing array element to null
|
||||||
|
assertArrayEquals(new Object[] {new JsonPrimitive(1), null}, array);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEqualsHashCode() {
|
||||||
|
JsonArray a = new JsonArray();
|
||||||
|
a.add(1);
|
||||||
|
|
||||||
|
List<JsonElement> list = a.asList();
|
||||||
|
MoreAsserts.assertEqualsAndHashCode(list, Collections.singletonList(new JsonPrimitive(1)));
|
||||||
|
assertFalse(list.equals(Collections.emptyList()));
|
||||||
|
assertFalse(list.equals(Collections.singletonList(new JsonPrimitive(2))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Verify that {@code JsonArray} updates are visible to view and vice versa */
|
||||||
|
@Test
|
||||||
|
public void testViewUpdates() {
|
||||||
|
JsonArray a = new JsonArray();
|
||||||
|
List<JsonElement> list = a.asList();
|
||||||
|
|
||||||
|
a.add(1);
|
||||||
|
assertEquals(1, list.size());
|
||||||
|
assertEquals(new JsonPrimitive(1), list.get(0));
|
||||||
|
|
||||||
|
list.add(new JsonPrimitive(2));
|
||||||
|
assertEquals(2, a.size());
|
||||||
|
assertEquals(new JsonPrimitive(2), a.get(1));
|
||||||
|
}
|
||||||
|
}
|
@ -16,18 +16,26 @@
|
|||||||
|
|
||||||
package com.google.gson;
|
package com.google.gson;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import com.google.gson.common.MoreAsserts;
|
import com.google.gson.common.MoreAsserts;
|
||||||
import junit.framework.TestCase;
|
import java.math.BigInteger;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Jesse Wilson
|
* @author Jesse Wilson
|
||||||
*/
|
*/
|
||||||
public final class JsonArrayTest extends TestCase {
|
public final class JsonArrayTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testEqualsOnEmptyArray() {
|
public void testEqualsOnEmptyArray() {
|
||||||
MoreAsserts.assertEqualsAndHashCode(new JsonArray(), new JsonArray());
|
MoreAsserts.assertEqualsAndHashCode(new JsonArray(), new JsonArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testEqualsNonEmptyArray() {
|
public void testEqualsNonEmptyArray() {
|
||||||
JsonArray a = new JsonArray();
|
JsonArray a = new JsonArray();
|
||||||
JsonArray b = new JsonArray();
|
JsonArray b = new JsonArray();
|
||||||
@ -50,6 +58,7 @@ public final class JsonArrayTest extends TestCase {
|
|||||||
assertFalse(b.equals(a));
|
assertFalse(b.equals(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testRemove() {
|
public void testRemove() {
|
||||||
JsonArray array = new JsonArray();
|
JsonArray array = new JsonArray();
|
||||||
try {
|
try {
|
||||||
@ -67,6 +76,7 @@ public final class JsonArrayTest extends TestCase {
|
|||||||
assertTrue(array.contains(a));
|
assertTrue(array.contains(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSet() {
|
public void testSet() {
|
||||||
JsonArray array = new JsonArray();
|
JsonArray array = new JsonArray();
|
||||||
try {
|
try {
|
||||||
@ -91,6 +101,7 @@ public final class JsonArrayTest extends TestCase {
|
|||||||
assertEquals(1, array.size());
|
assertEquals(1, array.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDeepCopy() {
|
public void testDeepCopy() {
|
||||||
JsonArray original = new JsonArray();
|
JsonArray original = new JsonArray();
|
||||||
JsonArray firstEntry = new JsonArray();
|
JsonArray firstEntry = new JsonArray();
|
||||||
@ -106,6 +117,7 @@ public final class JsonArrayTest extends TestCase {
|
|||||||
assertEquals(0, copy.get(0).getAsJsonArray().size());
|
assertEquals(0, copy.get(0).getAsJsonArray().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testIsEmpty() {
|
public void testIsEmpty() {
|
||||||
JsonArray array = new JsonArray();
|
JsonArray array = new JsonArray();
|
||||||
assertTrue(array.isEmpty());
|
assertTrue(array.isEmpty());
|
||||||
@ -118,6 +130,7 @@ public final class JsonArrayTest extends TestCase {
|
|||||||
assertTrue(array.isEmpty());
|
assertTrue(array.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testFailedGetArrayValues() {
|
public void testFailedGetArrayValues() {
|
||||||
JsonArray jsonArray = new JsonArray();
|
JsonArray jsonArray = new JsonArray();
|
||||||
jsonArray.add(JsonParser.parseString("{" + "\"key1\":\"value1\"," + "\"key2\":\"value2\"," + "\"key3\":\"value3\"," + "\"key4\":\"value4\"" + "}"));
|
jsonArray.add(JsonParser.parseString("{" + "\"key1\":\"value1\"," + "\"key2\":\"value2\"," + "\"key3\":\"value3\"," + "\"key4\":\"value4\"" + "}"));
|
||||||
@ -182,6 +195,7 @@ public final class JsonArrayTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testGetAs_WrongArraySize() {
|
public void testGetAs_WrongArraySize() {
|
||||||
JsonArray jsonArray = new JsonArray();
|
JsonArray jsonArray = new JsonArray();
|
||||||
try {
|
try {
|
||||||
@ -200,4 +214,160 @@ public final class JsonArrayTest extends TestCase {
|
|||||||
assertEquals("Array must have size 1, but has size 2", e.getMessage());
|
assertEquals("Array must have size 1, but has size 2", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStringPrimitiveAddition() {
|
||||||
|
JsonArray jsonArray = new JsonArray();
|
||||||
|
|
||||||
|
jsonArray.add("Hello");
|
||||||
|
jsonArray.add("Goodbye");
|
||||||
|
jsonArray.add("Thank you");
|
||||||
|
jsonArray.add((String) null);
|
||||||
|
jsonArray.add("Yes");
|
||||||
|
|
||||||
|
assertEquals("[\"Hello\",\"Goodbye\",\"Thank you\",null,\"Yes\"]", jsonArray.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIntegerPrimitiveAddition() {
|
||||||
|
JsonArray jsonArray = new JsonArray();
|
||||||
|
|
||||||
|
int x = 1;
|
||||||
|
jsonArray.add(x);
|
||||||
|
|
||||||
|
x = 2;
|
||||||
|
jsonArray.add(x);
|
||||||
|
|
||||||
|
x = -3;
|
||||||
|
jsonArray.add(x);
|
||||||
|
|
||||||
|
jsonArray.add((Integer) null);
|
||||||
|
|
||||||
|
x = 4;
|
||||||
|
jsonArray.add(x);
|
||||||
|
|
||||||
|
x = 0;
|
||||||
|
jsonArray.add(x);
|
||||||
|
|
||||||
|
assertEquals("[1,2,-3,null,4,0]", jsonArray.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDoublePrimitiveAddition() {
|
||||||
|
JsonArray jsonArray = new JsonArray();
|
||||||
|
|
||||||
|
double x = 1.0;
|
||||||
|
jsonArray.add(x);
|
||||||
|
|
||||||
|
x = 2.13232;
|
||||||
|
jsonArray.add(x);
|
||||||
|
|
||||||
|
x = 0.121;
|
||||||
|
jsonArray.add(x);
|
||||||
|
|
||||||
|
jsonArray.add((Double) null);
|
||||||
|
|
||||||
|
x = -0.00234;
|
||||||
|
jsonArray.add(x);
|
||||||
|
|
||||||
|
jsonArray.add((Double) null);
|
||||||
|
|
||||||
|
assertEquals("[1.0,2.13232,0.121,null,-0.00234,null]", jsonArray.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBooleanPrimitiveAddition() {
|
||||||
|
JsonArray jsonArray = new JsonArray();
|
||||||
|
|
||||||
|
jsonArray.add(true);
|
||||||
|
jsonArray.add(true);
|
||||||
|
jsonArray.add(false);
|
||||||
|
jsonArray.add(false);
|
||||||
|
jsonArray.add((Boolean) null);
|
||||||
|
jsonArray.add(true);
|
||||||
|
|
||||||
|
assertEquals("[true,true,false,false,null,true]", jsonArray.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCharPrimitiveAddition() {
|
||||||
|
JsonArray jsonArray = new JsonArray();
|
||||||
|
|
||||||
|
jsonArray.add('a');
|
||||||
|
jsonArray.add('e');
|
||||||
|
jsonArray.add('i');
|
||||||
|
jsonArray.add((char) 111);
|
||||||
|
jsonArray.add((Character) null);
|
||||||
|
jsonArray.add('u');
|
||||||
|
jsonArray.add("and sometimes Y");
|
||||||
|
|
||||||
|
assertEquals("[\"a\",\"e\",\"i\",\"o\",null,\"u\",\"and sometimes Y\"]", jsonArray.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMixedPrimitiveAddition() {
|
||||||
|
JsonArray jsonArray = new JsonArray();
|
||||||
|
|
||||||
|
jsonArray.add('a');
|
||||||
|
jsonArray.add("apple");
|
||||||
|
jsonArray.add(12121);
|
||||||
|
jsonArray.add((char) 111);
|
||||||
|
|
||||||
|
jsonArray.add((Boolean) null);
|
||||||
|
assertEquals(JsonNull.INSTANCE, jsonArray.get(jsonArray.size() - 1));
|
||||||
|
|
||||||
|
jsonArray.add((Character) null);
|
||||||
|
assertEquals(JsonNull.INSTANCE, jsonArray.get(jsonArray.size() - 1));
|
||||||
|
|
||||||
|
jsonArray.add(12.232);
|
||||||
|
jsonArray.add(BigInteger.valueOf(2323));
|
||||||
|
|
||||||
|
assertEquals("[\"a\",\"apple\",12121,\"o\",null,null,12.232,2323]", jsonArray.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNullPrimitiveAddition() {
|
||||||
|
JsonArray jsonArray = new JsonArray();
|
||||||
|
|
||||||
|
jsonArray.add((Character) null);
|
||||||
|
jsonArray.add((Boolean) null);
|
||||||
|
jsonArray.add((Integer) null);
|
||||||
|
jsonArray.add((Double) null);
|
||||||
|
jsonArray.add((Float) null);
|
||||||
|
jsonArray.add((BigInteger) null);
|
||||||
|
jsonArray.add((String) null);
|
||||||
|
jsonArray.add((Boolean) null);
|
||||||
|
jsonArray.add((Number) null);
|
||||||
|
|
||||||
|
assertEquals("[null,null,null,null,null,null,null,null,null]", jsonArray.toString());
|
||||||
|
for (int i = 0; i < jsonArray.size(); i++) {
|
||||||
|
// Verify that they are actually a JsonNull and not a Java null
|
||||||
|
assertEquals(JsonNull.INSTANCE, jsonArray.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNullJsonElementAddition() {
|
||||||
|
JsonArray jsonArray = new JsonArray();
|
||||||
|
jsonArray.add((JsonElement) null);
|
||||||
|
assertEquals(JsonNull.INSTANCE, jsonArray.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSameAddition() {
|
||||||
|
JsonArray jsonArray = new JsonArray();
|
||||||
|
|
||||||
|
jsonArray.add('a');
|
||||||
|
jsonArray.add('a');
|
||||||
|
jsonArray.add(true);
|
||||||
|
jsonArray.add(true);
|
||||||
|
jsonArray.add(1212);
|
||||||
|
jsonArray.add(1212);
|
||||||
|
jsonArray.add(34.34);
|
||||||
|
jsonArray.add(34.34);
|
||||||
|
jsonArray.add((Boolean) null);
|
||||||
|
jsonArray.add((Boolean) null);
|
||||||
|
|
||||||
|
assertEquals("[\"a\",\"a\",true,true,1212,1212,34.34,34.34,null,null]", jsonArray.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
287
gson/src/test/java/com/google/gson/JsonObjectAsMapTest.java
Normal file
287
gson/src/test/java/com/google/gson/JsonObjectAsMapTest.java
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
package com.google.gson;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import com.google.gson.common.MoreAsserts;
|
||||||
|
import java.util.AbstractMap.SimpleEntry;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JsonObject#asMap()}.
|
||||||
|
*/
|
||||||
|
public class JsonObjectAsMapTest {
|
||||||
|
@Test
|
||||||
|
public void testSize() {
|
||||||
|
JsonObject o = new JsonObject();
|
||||||
|
assertEquals(0, o.asMap().size());
|
||||||
|
|
||||||
|
o.addProperty("a", 1);
|
||||||
|
Map<String, JsonElement> map = o.asMap();
|
||||||
|
assertEquals(1, map.size());
|
||||||
|
|
||||||
|
map.clear();
|
||||||
|
assertEquals(0, map.size());
|
||||||
|
assertEquals(0, o.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContainsKey() {
|
||||||
|
JsonObject o = new JsonObject();
|
||||||
|
o.addProperty("a", 1);
|
||||||
|
|
||||||
|
Map<String, JsonElement> map = o.asMap();
|
||||||
|
assertTrue(map.containsKey("a"));
|
||||||
|
assertFalse(map.containsKey("b"));
|
||||||
|
assertFalse(map.containsKey(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContainsValue() {
|
||||||
|
JsonObject o = new JsonObject();
|
||||||
|
o.addProperty("a", 1);
|
||||||
|
o.add("b", JsonNull.INSTANCE);
|
||||||
|
|
||||||
|
Map<String, JsonElement> map = o.asMap();
|
||||||
|
assertTrue(map.containsValue(new JsonPrimitive(1)));
|
||||||
|
assertFalse(map.containsValue(new JsonPrimitive(2)));
|
||||||
|
assertFalse(map.containsValue(null));
|
||||||
|
|
||||||
|
@SuppressWarnings({"unlikely-arg-type", "CollectionIncompatibleType"})
|
||||||
|
boolean containsInt = map.containsValue(1); // should only contain JsonPrimitive(1)
|
||||||
|
assertFalse(containsInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGet() {
|
||||||
|
JsonObject o = new JsonObject();
|
||||||
|
o.addProperty("a", 1);
|
||||||
|
|
||||||
|
Map<String, JsonElement> map = o.asMap();
|
||||||
|
assertEquals(new JsonPrimitive(1), map.get("a"));
|
||||||
|
assertNull(map.get("b"));
|
||||||
|
assertNull(map.get(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPut() {
|
||||||
|
JsonObject o = new JsonObject();
|
||||||
|
Map<String, JsonElement> map = o.asMap();
|
||||||
|
|
||||||
|
assertNull(map.put("a", new JsonPrimitive(1)));
|
||||||
|
assertEquals(1, map.size());
|
||||||
|
assertEquals(new JsonPrimitive(1), map.get("a"));
|
||||||
|
|
||||||
|
JsonElement old = map.put("a", new JsonPrimitive(2));
|
||||||
|
assertEquals(new JsonPrimitive(1), old);
|
||||||
|
assertEquals(1, map.size());
|
||||||
|
assertEquals(new JsonPrimitive(2), map.get("a"));
|
||||||
|
assertEquals(new JsonPrimitive(2), o.get("a"));
|
||||||
|
|
||||||
|
assertNull(map.put("b", JsonNull.INSTANCE));
|
||||||
|
assertEquals(JsonNull.INSTANCE, map.get("b"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
map.put(null, new JsonPrimitive(1));
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("key == null", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
map.put("a", null);
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("value == null", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemove() {
|
||||||
|
JsonObject o = new JsonObject();
|
||||||
|
o.addProperty("a", 1);
|
||||||
|
|
||||||
|
Map<String, JsonElement> map = o.asMap();
|
||||||
|
assertNull(map.remove("b"));
|
||||||
|
assertEquals(1, map.size());
|
||||||
|
|
||||||
|
JsonElement old = map.remove("a");
|
||||||
|
assertEquals(new JsonPrimitive(1), old);
|
||||||
|
assertEquals(0, map.size());
|
||||||
|
|
||||||
|
assertNull(map.remove("a"));
|
||||||
|
assertEquals(0, map.size());
|
||||||
|
assertEquals(0, o.size());
|
||||||
|
|
||||||
|
assertNull(map.remove(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPutAll() {
|
||||||
|
JsonObject o = new JsonObject();
|
||||||
|
o.addProperty("a", 1);
|
||||||
|
|
||||||
|
Map<String, JsonElement> otherMap = new HashMap<>();
|
||||||
|
otherMap.put("a", new JsonPrimitive(2));
|
||||||
|
otherMap.put("b", new JsonPrimitive(3));
|
||||||
|
|
||||||
|
Map<String, JsonElement> map = o.asMap();
|
||||||
|
map.putAll(otherMap);
|
||||||
|
assertEquals(2, map.size());
|
||||||
|
assertEquals(new JsonPrimitive(2), map.get("a"));
|
||||||
|
assertEquals(new JsonPrimitive(3), map.get("b"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
map.putAll(Collections.<String, JsonElement>singletonMap(null, new JsonPrimitive(1)));
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("key == null", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
map.putAll(Collections.<String, JsonElement>singletonMap("a", null));
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("value == null", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClear() {
|
||||||
|
JsonObject o = new JsonObject();
|
||||||
|
o.addProperty("a", 1);
|
||||||
|
|
||||||
|
Map<String, JsonElement> map = o.asMap();
|
||||||
|
map.clear();
|
||||||
|
assertEquals(0, map.size());
|
||||||
|
assertEquals(0, o.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testKeySet() {
|
||||||
|
JsonObject o = new JsonObject();
|
||||||
|
o.addProperty("b", 1);
|
||||||
|
o.addProperty("a", 2);
|
||||||
|
|
||||||
|
Map<String, JsonElement> map = o.asMap();
|
||||||
|
Set<String> keySet = map.keySet();
|
||||||
|
// Should contain keys in same order
|
||||||
|
assertEquals(Arrays.asList("b", "a"), new ArrayList<>(keySet));
|
||||||
|
|
||||||
|
// Key set doesn't support insertions
|
||||||
|
try {
|
||||||
|
keySet.add("c");
|
||||||
|
fail();
|
||||||
|
} catch (UnsupportedOperationException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(keySet.remove("a"));
|
||||||
|
assertEquals(Collections.singleton("b"), map.keySet());
|
||||||
|
assertEquals(Collections.singleton("b"), o.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValues() {
|
||||||
|
JsonObject o = new JsonObject();
|
||||||
|
o.addProperty("a", 2);
|
||||||
|
o.addProperty("b", 1);
|
||||||
|
|
||||||
|
Map<String, JsonElement> map = o.asMap();
|
||||||
|
Collection<JsonElement> values = map.values();
|
||||||
|
// Should contain values in same order
|
||||||
|
assertEquals(Arrays.asList(new JsonPrimitive(2), new JsonPrimitive(1)), new ArrayList<>(values));
|
||||||
|
|
||||||
|
// Values collection doesn't support insertions
|
||||||
|
try {
|
||||||
|
values.add(new JsonPrimitive(3));
|
||||||
|
fail();
|
||||||
|
} catch (UnsupportedOperationException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(values.remove(new JsonPrimitive(2)));
|
||||||
|
assertEquals(Collections.singletonList(new JsonPrimitive(1)), new ArrayList<>(map.values()));
|
||||||
|
assertEquals(1, o.size());
|
||||||
|
assertEquals(new JsonPrimitive(1), o.get("b"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEntrySet() {
|
||||||
|
JsonObject o = new JsonObject();
|
||||||
|
o.addProperty("b", 2);
|
||||||
|
o.addProperty("a", 1);
|
||||||
|
|
||||||
|
Map<String, JsonElement> map = o.asMap();
|
||||||
|
Set<Entry<String, JsonElement>> entrySet = map.entrySet();
|
||||||
|
|
||||||
|
List<Entry<?, ?>> expectedEntrySet = Arrays.<Entry<?, ?>>asList(
|
||||||
|
new SimpleEntry<>("b", new JsonPrimitive(2)),
|
||||||
|
new SimpleEntry<>("a", new JsonPrimitive(1))
|
||||||
|
);
|
||||||
|
// Should contain entries in same order
|
||||||
|
assertEquals(expectedEntrySet, new ArrayList<>(entrySet));
|
||||||
|
|
||||||
|
try {
|
||||||
|
entrySet.add(new SimpleEntry<String, JsonElement>("c", new JsonPrimitive(3)));
|
||||||
|
fail();
|
||||||
|
} catch (UnsupportedOperationException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(entrySet.remove(new SimpleEntry<>("a", new JsonPrimitive(1))));
|
||||||
|
assertEquals(Collections.singleton(new SimpleEntry<>("b", new JsonPrimitive(2))), map.entrySet());
|
||||||
|
assertEquals(Collections.singleton(new SimpleEntry<>("b", new JsonPrimitive(2))), o.entrySet());
|
||||||
|
|
||||||
|
// Should return false because entry has already been removed
|
||||||
|
assertFalse(entrySet.remove(new SimpleEntry<>("a", new JsonPrimitive(1))));
|
||||||
|
|
||||||
|
Entry<String, JsonElement> entry = entrySet.iterator().next();
|
||||||
|
JsonElement old = entry.setValue(new JsonPrimitive(3));
|
||||||
|
assertEquals(new JsonPrimitive(2), old);
|
||||||
|
assertEquals(Collections.singleton(new SimpleEntry<>("b", new JsonPrimitive(3))), map.entrySet());
|
||||||
|
assertEquals(Collections.singleton(new SimpleEntry<>("b", new JsonPrimitive(3))), o.entrySet());
|
||||||
|
|
||||||
|
try {
|
||||||
|
entry.setValue(null);
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("value == null", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEqualsHashCode() {
|
||||||
|
JsonObject o = new JsonObject();
|
||||||
|
o.addProperty("a", 1);
|
||||||
|
|
||||||
|
Map<String, JsonElement> map = o.asMap();
|
||||||
|
MoreAsserts.assertEqualsAndHashCode(map, Collections.singletonMap("a", new JsonPrimitive(1)));
|
||||||
|
assertFalse(map.equals(Collections.emptyMap()));
|
||||||
|
assertFalse(map.equals(Collections.singletonMap("a", new JsonPrimitive(2))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Verify that {@code JsonObject} updates are visible to view and vice versa */
|
||||||
|
@Test
|
||||||
|
public void testViewUpdates() {
|
||||||
|
JsonObject o = new JsonObject();
|
||||||
|
Map<String, JsonElement> map = o.asMap();
|
||||||
|
|
||||||
|
o.addProperty("a", 1);
|
||||||
|
assertEquals(1, map.size());
|
||||||
|
assertEquals(new JsonPrimitive(1), map.get("a"));
|
||||||
|
|
||||||
|
map.put("b", new JsonPrimitive(2));
|
||||||
|
assertEquals(2, o.size());
|
||||||
|
assertEquals(new JsonPrimitive(2), o.get("b"));
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,13 @@
|
|||||||
|
|
||||||
package com.google.gson;
|
package com.google.gson;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import com.google.gson.common.MoreAsserts;
|
import com.google.gson.common.MoreAsserts;
|
||||||
import java.util.AbstractMap.SimpleEntry;
|
import java.util.AbstractMap.SimpleEntry;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
@ -27,15 +34,16 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import junit.framework.TestCase;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit test for the {@link JsonObject} class.
|
* Unit test for the {@link JsonObject} class.
|
||||||
*
|
*
|
||||||
* @author Joel Leitch
|
* @author Joel Leitch
|
||||||
*/
|
*/
|
||||||
public class JsonObjectTest extends TestCase {
|
public class JsonObjectTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testAddingAndRemovingObjectProperties() throws Exception {
|
public void testAddingAndRemovingObjectProperties() throws Exception {
|
||||||
JsonObject jsonObj = new JsonObject();
|
JsonObject jsonObj = new JsonObject();
|
||||||
String propertyName = "property";
|
String propertyName = "property";
|
||||||
@ -54,6 +62,7 @@ public class JsonObjectTest extends TestCase {
|
|||||||
assertNull(jsonObj.remove(propertyName));
|
assertNull(jsonObj.remove(propertyName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testAddingNullPropertyValue() throws Exception {
|
public void testAddingNullPropertyValue() throws Exception {
|
||||||
String propertyName = "property";
|
String propertyName = "property";
|
||||||
JsonObject jsonObj = new JsonObject();
|
JsonObject jsonObj = new JsonObject();
|
||||||
@ -66,6 +75,7 @@ public class JsonObjectTest extends TestCase {
|
|||||||
assertTrue(jsonElement.isJsonNull());
|
assertTrue(jsonElement.isJsonNull());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testAddingNullOrEmptyPropertyName() throws Exception {
|
public void testAddingNullOrEmptyPropertyName() throws Exception {
|
||||||
JsonObject jsonObj = new JsonObject();
|
JsonObject jsonObj = new JsonObject();
|
||||||
try {
|
try {
|
||||||
@ -77,6 +87,7 @@ public class JsonObjectTest extends TestCase {
|
|||||||
jsonObj.add(" \t", JsonNull.INSTANCE);
|
jsonObj.add(" \t", JsonNull.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testAddingBooleanProperties() throws Exception {
|
public void testAddingBooleanProperties() throws Exception {
|
||||||
String propertyName = "property";
|
String propertyName = "property";
|
||||||
JsonObject jsonObj = new JsonObject();
|
JsonObject jsonObj = new JsonObject();
|
||||||
@ -89,6 +100,7 @@ public class JsonObjectTest extends TestCase {
|
|||||||
assertTrue(jsonElement.getAsBoolean());
|
assertTrue(jsonElement.getAsBoolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testAddingStringProperties() throws Exception {
|
public void testAddingStringProperties() throws Exception {
|
||||||
String propertyName = "property";
|
String propertyName = "property";
|
||||||
String value = "blah";
|
String value = "blah";
|
||||||
@ -103,6 +115,7 @@ public class JsonObjectTest extends TestCase {
|
|||||||
assertEquals(value, jsonElement.getAsString());
|
assertEquals(value, jsonElement.getAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testAddingCharacterProperties() throws Exception {
|
public void testAddingCharacterProperties() throws Exception {
|
||||||
String propertyName = "property";
|
String propertyName = "property";
|
||||||
char value = 'a';
|
char value = 'a';
|
||||||
@ -124,6 +137,7 @@ public class JsonObjectTest extends TestCase {
|
|||||||
/**
|
/**
|
||||||
* From bug report http://code.google.com/p/google-gson/issues/detail?id=182
|
* From bug report http://code.google.com/p/google-gson/issues/detail?id=182
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testPropertyWithQuotes() {
|
public void testPropertyWithQuotes() {
|
||||||
JsonObject jsonObj = new JsonObject();
|
JsonObject jsonObj = new JsonObject();
|
||||||
jsonObj.add("a\"b", new JsonPrimitive("c\"d"));
|
jsonObj.add("a\"b", new JsonPrimitive("c\"d"));
|
||||||
@ -134,6 +148,7 @@ public class JsonObjectTest extends TestCase {
|
|||||||
/**
|
/**
|
||||||
* From issue 227.
|
* From issue 227.
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testWritePropertyWithEmptyStringName() {
|
public void testWritePropertyWithEmptyStringName() {
|
||||||
JsonObject jsonObj = new JsonObject();
|
JsonObject jsonObj = new JsonObject();
|
||||||
jsonObj.add("", new JsonPrimitive(true));
|
jsonObj.add("", new JsonPrimitive(true));
|
||||||
@ -141,15 +156,18 @@ public class JsonObjectTest extends TestCase {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testReadPropertyWithEmptyStringName() {
|
public void testReadPropertyWithEmptyStringName() {
|
||||||
JsonObject jsonObj = JsonParser.parseString("{\"\":true}").getAsJsonObject();
|
JsonObject jsonObj = JsonParser.parseString("{\"\":true}").getAsJsonObject();
|
||||||
assertEquals(true, jsonObj.get("").getAsBoolean());
|
assertEquals(true, jsonObj.get("").getAsBoolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testEqualsOnEmptyObject() {
|
public void testEqualsOnEmptyObject() {
|
||||||
MoreAsserts.assertEqualsAndHashCode(new JsonObject(), new JsonObject());
|
MoreAsserts.assertEqualsAndHashCode(new JsonObject(), new JsonObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testEqualsNonEmptyObject() {
|
public void testEqualsNonEmptyObject() {
|
||||||
JsonObject a = new JsonObject();
|
JsonObject a = new JsonObject();
|
||||||
JsonObject b = new JsonObject();
|
JsonObject b = new JsonObject();
|
||||||
@ -172,6 +190,7 @@ public class JsonObjectTest extends TestCase {
|
|||||||
assertFalse(b.equals(a));
|
assertFalse(b.equals(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testEqualsHashCodeIgnoringOrder() {
|
public void testEqualsHashCodeIgnoringOrder() {
|
||||||
JsonObject a = new JsonObject();
|
JsonObject a = new JsonObject();
|
||||||
JsonObject b = new JsonObject();
|
JsonObject b = new JsonObject();
|
||||||
@ -188,6 +207,7 @@ public class JsonObjectTest extends TestCase {
|
|||||||
MoreAsserts.assertEqualsAndHashCode(a, b);
|
MoreAsserts.assertEqualsAndHashCode(a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSize() {
|
public void testSize() {
|
||||||
JsonObject o = new JsonObject();
|
JsonObject o = new JsonObject();
|
||||||
assertEquals(0, o.size());
|
assertEquals(0, o.size());
|
||||||
@ -202,6 +222,7 @@ public class JsonObjectTest extends TestCase {
|
|||||||
assertEquals(1, o.size());
|
assertEquals(1, o.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDeepCopy() {
|
public void testDeepCopy() {
|
||||||
JsonObject original = new JsonObject();
|
JsonObject original = new JsonObject();
|
||||||
JsonArray firstEntry = new JsonArray();
|
JsonArray firstEntry = new JsonArray();
|
||||||
@ -217,6 +238,7 @@ public class JsonObjectTest extends TestCase {
|
|||||||
/**
|
/**
|
||||||
* From issue 941
|
* From issue 941
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testKeySet() {
|
public void testKeySet() {
|
||||||
JsonObject a = new JsonObject();
|
JsonObject a = new JsonObject();
|
||||||
assertEquals(0, a.keySet().size());
|
assertEquals(0, a.keySet().size());
|
||||||
@ -250,6 +272,7 @@ public class JsonObjectTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testEntrySet() {
|
public void testEntrySet() {
|
||||||
JsonObject o = new JsonObject();
|
JsonObject o = new JsonObject();
|
||||||
assertEquals(0, o.entrySet().size());
|
assertEquals(0, o.entrySet().size());
|
||||||
|
@ -174,7 +174,7 @@ public final class MixedStreamTest extends TestCase {
|
|||||||
} catch (NullPointerException expected) {
|
} catch (NullPointerException expected) {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
gson.fromJson(new JsonReader(new StringReader("true")), null);
|
gson.fromJson(new JsonReader(new StringReader("true")), (Type) null);
|
||||||
fail();
|
fail();
|
||||||
} catch (NullPointerException expected) {
|
} catch (NullPointerException expected) {
|
||||||
}
|
}
|
||||||
|
@ -16,40 +16,82 @@
|
|||||||
|
|
||||||
package com.google.gson;
|
package com.google.gson;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import com.google.gson.annotations.Since;
|
import com.google.gson.annotations.Since;
|
||||||
|
import com.google.gson.annotations.Until;
|
||||||
import com.google.gson.internal.Excluder;
|
import com.google.gson.internal.Excluder;
|
||||||
import junit.framework.TestCase;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for the {@link Excluder} class.
|
* Unit tests for the {@link Excluder} class.
|
||||||
*
|
*
|
||||||
* @author Joel Leitch
|
* @author Joel Leitch
|
||||||
*/
|
*/
|
||||||
public class VersionExclusionStrategyTest extends TestCase {
|
public class VersionExclusionStrategyTest {
|
||||||
private static final double VERSION = 5.0D;
|
private static final double VERSION = 5.0D;
|
||||||
|
|
||||||
public void testClassAndFieldAreAtSameVersion() throws Exception {
|
@Test
|
||||||
|
public void testSameVersion() throws Exception {
|
||||||
Excluder excluder = Excluder.DEFAULT.withVersion(VERSION);
|
Excluder excluder = Excluder.DEFAULT.withVersion(VERSION);
|
||||||
assertFalse(excluder.excludeClass(MockObject.class, true));
|
assertFalse(excluder.excludeClass(MockClassSince.class, true));
|
||||||
assertFalse(excluder.excludeField(MockObject.class.getField("someField"), true));
|
assertFalse(excluder.excludeField(MockClassSince.class.getField("someField"), true));
|
||||||
|
|
||||||
|
// Until version is exclusive
|
||||||
|
assertTrue(excluder.excludeClass(MockClassUntil.class, true));
|
||||||
|
assertTrue(excluder.excludeField(MockClassUntil.class.getField("someField"), true));
|
||||||
|
|
||||||
|
assertFalse(excluder.excludeClass(MockClassBoth.class, true));
|
||||||
|
assertFalse(excluder.excludeField(MockClassBoth.class.getField("someField"), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testClassAndFieldAreBehindInVersion() throws Exception {
|
@Test
|
||||||
Excluder excluder = Excluder.DEFAULT.withVersion(VERSION + 1);
|
public void testNewerVersion() throws Exception {
|
||||||
assertFalse(excluder.excludeClass(MockObject.class, true));
|
Excluder excluder = Excluder.DEFAULT.withVersion(VERSION + 5);
|
||||||
assertFalse(excluder.excludeField(MockObject.class.getField("someField"), true));
|
assertFalse(excluder.excludeClass(MockClassSince.class, true));
|
||||||
|
assertFalse(excluder.excludeField(MockClassSince.class.getField("someField"), true));
|
||||||
|
|
||||||
|
assertTrue(excluder.excludeClass(MockClassUntil.class, true));
|
||||||
|
assertTrue(excluder.excludeField(MockClassUntil.class.getField("someField"), true));
|
||||||
|
|
||||||
|
assertTrue(excluder.excludeClass(MockClassBoth.class, true));
|
||||||
|
assertTrue(excluder.excludeField(MockClassBoth.class.getField("someField"), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testClassAndFieldAreAheadInVersion() throws Exception {
|
@Test
|
||||||
Excluder excluder = Excluder.DEFAULT.withVersion(VERSION - 1);
|
public void testOlderVersion() throws Exception {
|
||||||
assertTrue(excluder.excludeClass(MockObject.class, true));
|
Excluder excluder = Excluder.DEFAULT.withVersion(VERSION - 5);
|
||||||
assertTrue(excluder.excludeField(MockObject.class.getField("someField"), true));
|
assertTrue(excluder.excludeClass(MockClassSince.class, true));
|
||||||
|
assertTrue(excluder.excludeField(MockClassSince.class.getField("someField"), true));
|
||||||
|
|
||||||
|
assertFalse(excluder.excludeClass(MockClassUntil.class, true));
|
||||||
|
assertFalse(excluder.excludeField(MockClassUntil.class.getField("someField"), true));
|
||||||
|
|
||||||
|
assertTrue(excluder.excludeClass(MockClassBoth.class, true));
|
||||||
|
assertTrue(excluder.excludeField(MockClassBoth.class.getField("someField"), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Since(VERSION)
|
@Since(VERSION)
|
||||||
private static class MockObject {
|
private static class MockClassSince {
|
||||||
|
|
||||||
@Since(VERSION)
|
@Since(VERSION)
|
||||||
public final int someField = 0;
|
public final int someField = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Until(VERSION)
|
||||||
|
private static class MockClassUntil {
|
||||||
|
|
||||||
|
@Until(VERSION)
|
||||||
|
public final int someField = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Since(VERSION)
|
||||||
|
@Until(VERSION + 2)
|
||||||
|
private static class MockClassBoth {
|
||||||
|
|
||||||
|
@Since(VERSION)
|
||||||
|
@Until(VERSION + 2)
|
||||||
|
public final int someField = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ import junit.framework.TestCase;
|
|||||||
* @author Inderjeet Singh
|
* @author Inderjeet Singh
|
||||||
*/
|
*/
|
||||||
public class GsonVersionDiagnosticsTest extends TestCase {
|
public class GsonVersionDiagnosticsTest extends TestCase {
|
||||||
private static final Pattern GSON_VERSION_PATTERN = Pattern.compile("(\\(GSON \\d\\.\\d\\.\\d)(?:[-.][A-Z]+)?\\)$");
|
private static final Pattern GSON_VERSION_PATTERN = Pattern.compile("(\\(GSON \\d\\.\\d+(\\.\\d)?)(?:[-.][A-Z]+)?\\)$");
|
||||||
|
|
||||||
private Gson gson;
|
private Gson gson;
|
||||||
|
|
||||||
|
@ -0,0 +1,431 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Google Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.gson.functional;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import com.google.gson.ExclusionStrategy;
|
||||||
|
import com.google.gson.FieldAttributes;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonIOException;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import com.google.gson.JsonSerializationContext;
|
||||||
|
import com.google.gson.JsonSerializer;
|
||||||
|
import com.google.gson.ReflectionAccessFilter.FilterResult;
|
||||||
|
import com.google.gson.TypeAdapter;
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public final class Java17RecordTest {
|
||||||
|
private final Gson gson = new Gson();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFirstNameIsChosenForSerialization() {
|
||||||
|
RecordWithCustomNames target = new RecordWithCustomNames("v1", "v2");
|
||||||
|
// Ensure name1 occurs exactly once, and name2 and name3 don't appear
|
||||||
|
assertEquals("{\"name\":\"v1\",\"name1\":\"v2\"}", gson.toJson(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleNamesDeserializedCorrectly() {
|
||||||
|
assertEquals("v1", gson.fromJson("{'name':'v1'}", RecordWithCustomNames.class).a);
|
||||||
|
|
||||||
|
// Both name1 and name2 gets deserialized to b
|
||||||
|
assertEquals("v11", gson.fromJson("{'name': 'v1', 'name1':'v11'}", RecordWithCustomNames.class).b);
|
||||||
|
assertEquals("v2", gson.fromJson("{'name': 'v1', 'name2':'v2'}", RecordWithCustomNames.class).b);
|
||||||
|
assertEquals("v3", gson.fromJson("{'name': 'v1', 'name3':'v3'}", RecordWithCustomNames.class).b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleNamesInTheSameString() {
|
||||||
|
// The last value takes precedence
|
||||||
|
assertEquals("v3",
|
||||||
|
gson.fromJson("{'name': 'foo', 'name1':'v1','name2':'v2','name3':'v3'}", RecordWithCustomNames.class).b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record RecordWithCustomNames(
|
||||||
|
@SerializedName("name") String a,
|
||||||
|
@SerializedName(value = "name1", alternate = {"name2", "name3"}) String b) {}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSerializedNameOnAccessor() {
|
||||||
|
record LocalRecord(int i) {
|
||||||
|
@SerializedName("a")
|
||||||
|
@Override
|
||||||
|
public int i() {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var exception = assertThrows(JsonIOException.class, () -> gson.getAdapter(LocalRecord.class));
|
||||||
|
assertEquals("@SerializedName on method '" + LocalRecord.class.getName() + "#i()' is not supported",
|
||||||
|
exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFieldNamingStrategy() {
|
||||||
|
record LocalRecord(int i) {}
|
||||||
|
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.setFieldNamingStrategy(f -> f.getName() + "-custom")
|
||||||
|
.create();
|
||||||
|
|
||||||
|
assertEquals("{\"i-custom\":1}", gson.toJson(new LocalRecord(1)));
|
||||||
|
assertEquals(new LocalRecord(2), gson.fromJson("{\"i-custom\":2}", LocalRecord.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnknownJsonProperty() {
|
||||||
|
record LocalRecord(int i) {}
|
||||||
|
|
||||||
|
// Unknown property 'x' should be ignored
|
||||||
|
assertEquals(new LocalRecord(1), gson.fromJson("{\"i\":1,\"x\":2}", LocalRecord.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDuplicateJsonProperties() {
|
||||||
|
record LocalRecord(Integer a, Integer b) {}
|
||||||
|
|
||||||
|
String json = "{\"a\":null,\"a\":2,\"b\":1,\"b\":null}";
|
||||||
|
// Should use value of last occurrence
|
||||||
|
assertEquals(new LocalRecord(2, null), gson.fromJson(json, LocalRecord.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorRuns() {
|
||||||
|
record LocalRecord(String s) {
|
||||||
|
LocalRecord {
|
||||||
|
s = "custom-" + s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalRecord deserialized = gson.fromJson("{\"s\": null}", LocalRecord.class);
|
||||||
|
assertEquals(new LocalRecord(null), deserialized);
|
||||||
|
assertEquals("custom-null", deserialized.s());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tests behavior when the canonical constructor throws an exception */
|
||||||
|
@Test
|
||||||
|
public void testThrowingConstructor() {
|
||||||
|
record LocalRecord(String s) {
|
||||||
|
static final RuntimeException thrownException = new RuntimeException("Custom exception");
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
LocalRecord {
|
||||||
|
throw thrownException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
gson.fromJson("{\"s\":\"value\"}", LocalRecord.class);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
// TODO: Adjust this once Gson throws more specific exception type
|
||||||
|
catch (RuntimeException e) {
|
||||||
|
assertEquals("Failed to invoke constructor '" + LocalRecord.class.getName() + "(String)' with args [value]",
|
||||||
|
e.getMessage());
|
||||||
|
assertSame(LocalRecord.thrownException, e.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAccessorIsCalled() {
|
||||||
|
record LocalRecord(String s) {
|
||||||
|
@Override
|
||||||
|
public String s() {
|
||||||
|
return "accessor-value";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("{\"s\":\"accessor-value\"}", gson.toJson(new LocalRecord(null)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tests behavior when a record accessor method throws an exception */
|
||||||
|
@Test
|
||||||
|
public void testThrowingAccessor() {
|
||||||
|
record LocalRecord(String s) {
|
||||||
|
static final RuntimeException thrownException = new RuntimeException("Custom exception");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String s() {
|
||||||
|
throw thrownException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
gson.toJson(new LocalRecord("a"));
|
||||||
|
fail();
|
||||||
|
} catch (JsonIOException e) {
|
||||||
|
assertEquals("Accessor method '" + LocalRecord.class.getName() + "#s()' threw exception",
|
||||||
|
e.getMessage());
|
||||||
|
assertSame(LocalRecord.thrownException, e.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tests behavior for a record without components */
|
||||||
|
@Test
|
||||||
|
public void testEmptyRecord() {
|
||||||
|
record EmptyRecord() {}
|
||||||
|
|
||||||
|
assertEquals("{}", gson.toJson(new EmptyRecord()));
|
||||||
|
assertEquals(new EmptyRecord(), gson.fromJson("{}", EmptyRecord.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests behavior when {@code null} is serialized / deserialized as record value;
|
||||||
|
* basically makes sure the adapter is 'null-safe'
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testRecordNull() throws IOException {
|
||||||
|
record LocalRecord(int i) {}
|
||||||
|
|
||||||
|
TypeAdapter<LocalRecord> adapter = gson.getAdapter(LocalRecord.class);
|
||||||
|
assertEquals("null", adapter.toJson(null));
|
||||||
|
assertNull(adapter.fromJson("null"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPrimitiveDefaultValues() {
|
||||||
|
RecordWithPrimitives expected = new RecordWithPrimitives("s", (byte) 0, (short) 0, 0, 0, 0, 0, '\0', false);
|
||||||
|
assertEquals(expected, gson.fromJson("{'aString': 's'}", RecordWithPrimitives.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPrimitiveJsonNullValue() {
|
||||||
|
String s = "{'aString': 's', 'aByte': null, 'aShort': 0}";
|
||||||
|
var e = assertThrows(JsonParseException.class, () -> gson.fromJson(s, RecordWithPrimitives.class));
|
||||||
|
assertEquals("null is not allowed as value for record component 'aByte' of primitive type; at path $.aByte",
|
||||||
|
e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests behavior when JSON contains non-null value, but custom adapter returns null
|
||||||
|
* for primitive component
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testPrimitiveAdapterNullValue() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(byte.class, new TypeAdapter<Byte>() {
|
||||||
|
@Override public Byte read(JsonReader in) throws IOException {
|
||||||
|
in.skipValue();
|
||||||
|
// Always return null
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void write(JsonWriter out, Byte value) {
|
||||||
|
throw new AssertionError("not needed for test");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setLenient()
|
||||||
|
.create();
|
||||||
|
|
||||||
|
String s = "{'aString': 's', 'aByte': 0}";
|
||||||
|
var exception = assertThrows(JsonParseException.class, () -> gson.fromJson(s, RecordWithPrimitives.class));
|
||||||
|
assertEquals("null is not allowed as value for record component 'aByte' of primitive type; at path $.aByte",
|
||||||
|
exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
private record RecordWithPrimitives(
|
||||||
|
String aString, byte aByte, short aShort, int anInt, long aLong, float aFloat, double aDouble, char aChar, boolean aBoolean) {}
|
||||||
|
|
||||||
|
/** Tests behavior when value of Object component is missing; should default to null */
|
||||||
|
@Test
|
||||||
|
public void testObjectDefaultValue() {
|
||||||
|
record LocalRecord(String s, int i) {}
|
||||||
|
|
||||||
|
assertEquals(new LocalRecord(null, 1), gson.fromJson("{\"i\":1}", LocalRecord.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests serialization of a record with {@code static} field.
|
||||||
|
*
|
||||||
|
* <p>Important: It is not documented that this is officially supported; this
|
||||||
|
* test just checks the current behavior.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testStaticFieldSerialization() {
|
||||||
|
// By default Gson should ignore static fields
|
||||||
|
assertEquals("{}", gson.toJson(new RecordWithStaticField()));
|
||||||
|
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
// Include static fields
|
||||||
|
.excludeFieldsWithModifiers(0)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
String json = gson.toJson(new RecordWithStaticField());
|
||||||
|
assertEquals("{\"s\":\"initial\"}", json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests deserialization of a record with {@code static} field.
|
||||||
|
*
|
||||||
|
* <p>Important: It is not documented that this is officially supported; this
|
||||||
|
* test just checks the current behavior.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testStaticFieldDeserialization() {
|
||||||
|
// By default Gson should ignore static fields
|
||||||
|
gson.fromJson("{\"s\":\"custom\"}", RecordWithStaticField.class);
|
||||||
|
assertEquals("initial", RecordWithStaticField.s);
|
||||||
|
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
// Include static fields
|
||||||
|
.excludeFieldsWithModifiers(0)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
String oldValue = RecordWithStaticField.s;
|
||||||
|
try {
|
||||||
|
RecordWithStaticField obj = gson.fromJson("{\"s\":\"custom\"}", RecordWithStaticField.class);
|
||||||
|
assertNotNull(obj);
|
||||||
|
// Currently record deserialization always ignores static fields
|
||||||
|
assertEquals("initial", RecordWithStaticField.s);
|
||||||
|
} finally {
|
||||||
|
RecordWithStaticField.s = oldValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record RecordWithStaticField() {
|
||||||
|
static String s = "initial";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExposeAnnotation() {
|
||||||
|
record RecordWithExpose(
|
||||||
|
@Expose int a,
|
||||||
|
int b
|
||||||
|
) {}
|
||||||
|
|
||||||
|
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
|
||||||
|
String json = gson.toJson(new RecordWithExpose(1, 2));
|
||||||
|
assertEquals("{\"a\":1}", json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFieldExclusionStrategy() {
|
||||||
|
record LocalRecord(int a, int b, double c) {}
|
||||||
|
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.setExclusionStrategies(new ExclusionStrategy() {
|
||||||
|
@Override public boolean shouldSkipField(FieldAttributes f) {
|
||||||
|
return f.getName().equals("a");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public boolean shouldSkipClass(Class<?> clazz) {
|
||||||
|
return clazz == double.class;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.create();
|
||||||
|
|
||||||
|
assertEquals("{\"b\":2}", gson.toJson(new LocalRecord(1, 2, 3.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJsonAdapterAnnotation() {
|
||||||
|
record Adapter() implements JsonSerializer<String>, JsonDeserializer<String> {
|
||||||
|
@Override public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
|
||||||
|
return "deserializer-" + json.getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public JsonElement serialize(String src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive("serializer-" + src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
record LocalRecord(
|
||||||
|
@JsonAdapter(Adapter.class) String s
|
||||||
|
) {}
|
||||||
|
|
||||||
|
assertEquals("{\"s\":\"serializer-a\"}", gson.toJson(new LocalRecord("a")));
|
||||||
|
assertEquals(new LocalRecord("deserializer-a"), gson.fromJson("{\"s\":\"a\"}", LocalRecord.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClassReflectionFilter() {
|
||||||
|
record Allowed(int a) {}
|
||||||
|
record Blocked(int b) {}
|
||||||
|
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.addReflectionAccessFilter(c -> c == Allowed.class ? FilterResult.ALLOW : FilterResult.BLOCK_ALL)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
String json = gson.toJson(new Allowed(1));
|
||||||
|
assertEquals("{\"a\":1}", json);
|
||||||
|
|
||||||
|
var exception = assertThrows(JsonIOException.class, () -> gson.toJson(new Blocked(1)));
|
||||||
|
assertEquals("ReflectionAccessFilter does not permit using reflection for class " + Blocked.class.getName() +
|
||||||
|
". Register a TypeAdapter for this type or adjust the access filter.",
|
||||||
|
exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReflectionFilterBlockInaccessible() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.addReflectionAccessFilter(c -> FilterResult.BLOCK_INACCESSIBLE)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
var exception = assertThrows(JsonIOException.class, () -> gson.toJson(new PrivateRecord(1)));
|
||||||
|
assertEquals("Constructor 'com.google.gson.functional.Java17RecordTest$PrivateRecord(int)' is not accessible and"
|
||||||
|
+ " ReflectionAccessFilter does not permit making it accessible. Register a TypeAdapter for the declaring"
|
||||||
|
+ " type, adjust the access filter or increase the visibility of the element and its declaring type.",
|
||||||
|
exception.getMessage());
|
||||||
|
|
||||||
|
exception = assertThrows(JsonIOException.class, () -> gson.fromJson("{}", PrivateRecord.class));
|
||||||
|
assertEquals("Constructor 'com.google.gson.functional.Java17RecordTest$PrivateRecord(int)' is not accessible and"
|
||||||
|
+ " ReflectionAccessFilter does not permit making it accessible. Register a TypeAdapter for the declaring"
|
||||||
|
+ " type, adjust the access filter or increase the visibility of the element and its declaring type.",
|
||||||
|
exception.getMessage());
|
||||||
|
|
||||||
|
assertEquals("{\"i\":1}", gson.toJson(new PublicRecord(1)));
|
||||||
|
assertEquals(new PublicRecord(2), gson.fromJson("{\"i\":2}", PublicRecord.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
private record PrivateRecord(int i) {}
|
||||||
|
public record PublicRecord(int i) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests behavior when {@code java.lang.Record} is used as type for serialization
|
||||||
|
* and deserialization.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testRecordBaseClass() {
|
||||||
|
record LocalRecord(int i) {}
|
||||||
|
|
||||||
|
assertEquals("{}", gson.toJson(new LocalRecord(1), Record.class));
|
||||||
|
|
||||||
|
var exception = assertThrows(JsonIOException.class, () -> gson.fromJson("{}", Record.class));
|
||||||
|
assertEquals("Abstract classes can't be instantiated! Register an InstanceCreator or a TypeAdapter for"
|
||||||
|
+ " this type. Class name: java.lang.Record",
|
||||||
|
exception.getMessage());
|
||||||
|
}
|
||||||
|
}
|
@ -1,161 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2008 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.google.gson.functional;
|
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Functional tests for adding primitives to a JsonArray.
|
|
||||||
*
|
|
||||||
* @author Dillon Dixon
|
|
||||||
*/
|
|
||||||
public class JsonArrayTest extends TestCase {
|
|
||||||
|
|
||||||
public void testStringPrimitiveAddition() {
|
|
||||||
JsonArray jsonArray = new JsonArray();
|
|
||||||
|
|
||||||
jsonArray.add("Hello");
|
|
||||||
jsonArray.add("Goodbye");
|
|
||||||
jsonArray.add("Thank you");
|
|
||||||
jsonArray.add((String) null);
|
|
||||||
jsonArray.add("Yes");
|
|
||||||
|
|
||||||
assertEquals("[\"Hello\",\"Goodbye\",\"Thank you\",null,\"Yes\"]", jsonArray.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testIntegerPrimitiveAddition() {
|
|
||||||
JsonArray jsonArray = new JsonArray();
|
|
||||||
|
|
||||||
int x = 1;
|
|
||||||
jsonArray.add(x);
|
|
||||||
|
|
||||||
x = 2;
|
|
||||||
jsonArray.add(x);
|
|
||||||
|
|
||||||
x = -3;
|
|
||||||
jsonArray.add(x);
|
|
||||||
|
|
||||||
jsonArray.add((Integer) null);
|
|
||||||
|
|
||||||
x = 4;
|
|
||||||
jsonArray.add(x);
|
|
||||||
|
|
||||||
x = 0;
|
|
||||||
jsonArray.add(x);
|
|
||||||
|
|
||||||
assertEquals("[1,2,-3,null,4,0]", jsonArray.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testDoublePrimitiveAddition() {
|
|
||||||
JsonArray jsonArray = new JsonArray();
|
|
||||||
|
|
||||||
double x = 1.0;
|
|
||||||
jsonArray.add(x);
|
|
||||||
|
|
||||||
x = 2.13232;
|
|
||||||
jsonArray.add(x);
|
|
||||||
|
|
||||||
x = 0.121;
|
|
||||||
jsonArray.add(x);
|
|
||||||
|
|
||||||
jsonArray.add((Double) null);
|
|
||||||
|
|
||||||
x = -0.00234;
|
|
||||||
jsonArray.add(x);
|
|
||||||
|
|
||||||
jsonArray.add((Double) null);
|
|
||||||
|
|
||||||
assertEquals("[1.0,2.13232,0.121,null,-0.00234,null]", jsonArray.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testBooleanPrimitiveAddition() {
|
|
||||||
JsonArray jsonArray = new JsonArray();
|
|
||||||
|
|
||||||
jsonArray.add(true);
|
|
||||||
jsonArray.add(true);
|
|
||||||
jsonArray.add(false);
|
|
||||||
jsonArray.add(false);
|
|
||||||
jsonArray.add((Boolean) null);
|
|
||||||
jsonArray.add(true);
|
|
||||||
|
|
||||||
assertEquals("[true,true,false,false,null,true]", jsonArray.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCharPrimitiveAddition() {
|
|
||||||
JsonArray jsonArray = new JsonArray();
|
|
||||||
|
|
||||||
jsonArray.add('a');
|
|
||||||
jsonArray.add('e');
|
|
||||||
jsonArray.add('i');
|
|
||||||
jsonArray.add((char) 111);
|
|
||||||
jsonArray.add((Character) null);
|
|
||||||
jsonArray.add('u');
|
|
||||||
jsonArray.add("and sometimes Y");
|
|
||||||
|
|
||||||
assertEquals("[\"a\",\"e\",\"i\",\"o\",null,\"u\",\"and sometimes Y\"]", jsonArray.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testMixedPrimitiveAddition() {
|
|
||||||
JsonArray jsonArray = new JsonArray();
|
|
||||||
|
|
||||||
jsonArray.add('a');
|
|
||||||
jsonArray.add("apple");
|
|
||||||
jsonArray.add(12121);
|
|
||||||
jsonArray.add((char) 111);
|
|
||||||
jsonArray.add((Boolean) null);
|
|
||||||
jsonArray.add((Character) null);
|
|
||||||
jsonArray.add(12.232);
|
|
||||||
jsonArray.add(BigInteger.valueOf(2323));
|
|
||||||
|
|
||||||
assertEquals("[\"a\",\"apple\",12121,\"o\",null,null,12.232,2323]", jsonArray.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testNullPrimitiveAddition() {
|
|
||||||
JsonArray jsonArray = new JsonArray();
|
|
||||||
|
|
||||||
jsonArray.add((Character) null);
|
|
||||||
jsonArray.add((Boolean) null);
|
|
||||||
jsonArray.add((Integer) null);
|
|
||||||
jsonArray.add((Double) null);
|
|
||||||
jsonArray.add((Float) null);
|
|
||||||
jsonArray.add((BigInteger) null);
|
|
||||||
jsonArray.add((String) null);
|
|
||||||
jsonArray.add((Boolean) null);
|
|
||||||
jsonArray.add((Number) null);
|
|
||||||
|
|
||||||
assertEquals("[null,null,null,null,null,null,null,null,null]", jsonArray.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testSameAddition() {
|
|
||||||
JsonArray jsonArray = new JsonArray();
|
|
||||||
|
|
||||||
jsonArray.add('a');
|
|
||||||
jsonArray.add('a');
|
|
||||||
jsonArray.add(true);
|
|
||||||
jsonArray.add(true);
|
|
||||||
jsonArray.add(1212);
|
|
||||||
jsonArray.add(1212);
|
|
||||||
jsonArray.add(34.34);
|
|
||||||
jsonArray.add(34.34);
|
|
||||||
jsonArray.add((Boolean) null);
|
|
||||||
jsonArray.add((Boolean) null);
|
|
||||||
|
|
||||||
assertEquals("[\"a\",\"a\",true,true,1212,1212,34.34,34.34,null,null]", jsonArray.toString());
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,6 +20,7 @@ import com.google.gson.Gson;
|
|||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.InstanceCreator;
|
import com.google.gson.InstanceCreator;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonIOException;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
import com.google.gson.JsonSerializationContext;
|
import com.google.gson.JsonSerializationContext;
|
||||||
@ -482,6 +483,16 @@ public class ObjectTest extends TestCase {
|
|||||||
gson.fromJson(gson.toJson(product), Product.class);
|
gson.fromJson(gson.toJson(product), Product.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final class Department {
|
||||||
|
public String name = "abc";
|
||||||
|
public String code = "123";
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class Product {
|
||||||
|
private List<String> attributes = new ArrayList<>();
|
||||||
|
private List<Department> departments = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
// http://code.google.com/p/google-gson/issues/detail?id=270
|
// http://code.google.com/p/google-gson/issues/detail?id=270
|
||||||
public void testDateAsMapObjectField() {
|
public void testDateAsMapObjectField() {
|
||||||
HasObjectMap a = new HasObjectMap();
|
HasObjectMap a = new HasObjectMap();
|
||||||
@ -493,17 +504,92 @@ public class ObjectTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class HasObjectMap {
|
static class HasObjectMap {
|
||||||
Map<String, Object> map = new HashMap<>();
|
Map<String, Object> map = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class Department {
|
/**
|
||||||
public String name = "abc";
|
* Tests serialization of a class with {@code static} field.
|
||||||
public String code = "123";
|
*
|
||||||
|
* <p>Important: It is not documented that this is officially supported; this
|
||||||
|
* test just checks the current behavior.
|
||||||
|
*/
|
||||||
|
public void testStaticFieldSerialization() {
|
||||||
|
// By default Gson should ignore static fields
|
||||||
|
assertEquals("{}", gson.toJson(new ClassWithStaticField()));
|
||||||
|
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
// Include static fields
|
||||||
|
.excludeFieldsWithModifiers(0)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
String json = gson.toJson(new ClassWithStaticField());
|
||||||
|
assertEquals("{\"s\":\"initial\"}", json);
|
||||||
|
|
||||||
|
json = gson.toJson(new ClassWithStaticFinalField());
|
||||||
|
assertEquals("{\"s\":\"initial\"}", json);
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class Product {
|
/**
|
||||||
private List<String> attributes = new ArrayList<>();
|
* Tests deserialization of a class with {@code static} field.
|
||||||
private List<Department> departments = new ArrayList<>();
|
*
|
||||||
|
* <p>Important: It is not documented that this is officially supported; this
|
||||||
|
* test just checks the current behavior.
|
||||||
|
*/
|
||||||
|
public void testStaticFieldDeserialization() {
|
||||||
|
// By default Gson should ignore static fields
|
||||||
|
gson.fromJson("{\"s\":\"custom\"}", ClassWithStaticField.class);
|
||||||
|
assertEquals("initial", ClassWithStaticField.s);
|
||||||
|
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
// Include static fields
|
||||||
|
.excludeFieldsWithModifiers(0)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
String oldValue = ClassWithStaticField.s;
|
||||||
|
try {
|
||||||
|
ClassWithStaticField obj = gson.fromJson("{\"s\":\"custom\"}", ClassWithStaticField.class);
|
||||||
|
assertNotNull(obj);
|
||||||
|
assertEquals("custom", ClassWithStaticField.s);
|
||||||
|
} finally {
|
||||||
|
ClassWithStaticField.s = oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
gson.fromJson("{\"s\":\"custom\"}", ClassWithStaticFinalField.class);
|
||||||
|
fail();
|
||||||
|
} catch (JsonIOException e) {
|
||||||
|
assertEquals("Cannot set value of 'static final' field 'com.google.gson.functional.ObjectTest$ClassWithStaticFinalField#s'",
|
||||||
|
e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ClassWithStaticField {
|
||||||
|
static String s = "initial";
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ClassWithStaticFinalField {
|
||||||
|
static final String s = "initial";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testThrowingDefaultConstructor() {
|
||||||
|
try {
|
||||||
|
gson.fromJson("{}", ClassWithThrowingConstructor.class);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
// TODO: Adjust this once Gson throws more specific exception type
|
||||||
|
catch (RuntimeException e) {
|
||||||
|
assertEquals("Failed to invoke constructor 'com.google.gson.functional.ObjectTest$ClassWithThrowingConstructor()' with no args",
|
||||||
|
e.getMessage());
|
||||||
|
assertSame(ClassWithThrowingConstructor.thrownException, e.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ClassWithThrowingConstructor {
|
||||||
|
static final RuntimeException thrownException = new RuntimeException("Custom exception");
|
||||||
|
|
||||||
|
public ClassWithThrowingConstructor() {
|
||||||
|
throw thrownException;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,13 +16,19 @@
|
|||||||
|
|
||||||
package com.google.gson.functional;
|
package com.google.gson.functional;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.ParameterizedTypeFixtures.MyParameterizedType;
|
import com.google.gson.ParameterizedTypeFixtures.MyParameterizedType;
|
||||||
import com.google.gson.ParameterizedTypeFixtures.MyParameterizedTypeAdapter;
|
import com.google.gson.ParameterizedTypeFixtures.MyParameterizedTypeAdapter;
|
||||||
import com.google.gson.ParameterizedTypeFixtures.MyParameterizedTypeInstanceCreator;
|
import com.google.gson.ParameterizedTypeFixtures.MyParameterizedTypeInstanceCreator;
|
||||||
import com.google.gson.common.TestTypes.BagOfPrimitives;
|
import com.google.gson.common.TestTypes.BagOfPrimitives;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
@ -32,7 +38,8 @@ import java.lang.reflect.Type;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import junit.framework.TestCase;
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Functional tests for the serialization and deserialization of parameterized types in Gson.
|
* Functional tests for the serialization and deserialization of parameterized types in Gson.
|
||||||
@ -40,15 +47,15 @@ import junit.framework.TestCase;
|
|||||||
* @author Inderjeet Singh
|
* @author Inderjeet Singh
|
||||||
* @author Joel Leitch
|
* @author Joel Leitch
|
||||||
*/
|
*/
|
||||||
public class ParameterizedTypesTest extends TestCase {
|
public class ParameterizedTypesTest {
|
||||||
private Gson gson;
|
private Gson gson;
|
||||||
|
|
||||||
@Override
|
@Before
|
||||||
protected void setUp() throws Exception {
|
public void setUp() {
|
||||||
super.setUp();
|
|
||||||
gson = new Gson();
|
gson = new Gson();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testParameterizedTypesSerialization() throws Exception {
|
public void testParameterizedTypesSerialization() throws Exception {
|
||||||
MyParameterizedType<Integer> src = new MyParameterizedType<>(10);
|
MyParameterizedType<Integer> src = new MyParameterizedType<>(10);
|
||||||
Type typeOfSrc = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
|
Type typeOfSrc = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
|
||||||
@ -56,6 +63,7 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
assertEquals(src.getExpectedJson(), json);
|
assertEquals(src.getExpectedJson(), json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testParameterizedTypeDeserialization() throws Exception {
|
public void testParameterizedTypeDeserialization() throws Exception {
|
||||||
BagOfPrimitives bag = new BagOfPrimitives();
|
BagOfPrimitives bag = new BagOfPrimitives();
|
||||||
MyParameterizedType<BagOfPrimitives> expected = new MyParameterizedType<>(bag);
|
MyParameterizedType<BagOfPrimitives> expected = new MyParameterizedType<>(bag);
|
||||||
@ -70,6 +78,7 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
assertEquals(expected, actual);
|
assertEquals(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testTypesWithMultipleParametersSerialization() throws Exception {
|
public void testTypesWithMultipleParametersSerialization() throws Exception {
|
||||||
MultiParameters<Integer, Float, Double, String, BagOfPrimitives> src =
|
MultiParameters<Integer, Float, Double, String, BagOfPrimitives> src =
|
||||||
new MultiParameters<>(10, 1.0F, 2.1D, "abc", new BagOfPrimitives());
|
new MultiParameters<>(10, 1.0F, 2.1D, "abc", new BagOfPrimitives());
|
||||||
@ -81,6 +90,7 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
assertEquals(expected, json);
|
assertEquals(expected, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testTypesWithMultipleParametersDeserialization() throws Exception {
|
public void testTypesWithMultipleParametersDeserialization() throws Exception {
|
||||||
Type typeOfTarget = new TypeToken<MultiParameters<Integer, Float, Double, String,
|
Type typeOfTarget = new TypeToken<MultiParameters<Integer, Float, Double, String,
|
||||||
BagOfPrimitives>>() {}.getType();
|
BagOfPrimitives>>() {}.getType();
|
||||||
@ -93,6 +103,7 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
assertEquals(expected, target);
|
assertEquals(expected, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testParameterizedTypeWithCustomSerializer() {
|
public void testParameterizedTypeWithCustomSerializer() {
|
||||||
Type ptIntegerType = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
|
Type ptIntegerType = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
|
||||||
Type ptStringType = new TypeToken<MyParameterizedType<String>>() {}.getType();
|
Type ptStringType = new TypeToken<MyParameterizedType<String>>() {}.getType();
|
||||||
@ -109,6 +120,7 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
assertEquals(MyParameterizedTypeAdapter.<String>getExpectedJson(stringTarget), json);
|
assertEquals(MyParameterizedTypeAdapter.<String>getExpectedJson(stringTarget), json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testParameterizedTypesWithCustomDeserializer() {
|
public void testParameterizedTypesWithCustomDeserializer() {
|
||||||
Type ptIntegerType = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
|
Type ptIntegerType = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
|
||||||
Type ptStringType = new TypeToken<MyParameterizedType<String>>() {}.getType();
|
Type ptStringType = new TypeToken<MyParameterizedType<String>>() {}.getType();
|
||||||
@ -130,6 +142,7 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
assertEquals("abc", stringTarget.value);
|
assertEquals("abc", stringTarget.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testParameterizedTypesWithWriterSerialization() throws Exception {
|
public void testParameterizedTypesWithWriterSerialization() throws Exception {
|
||||||
Writer writer = new StringWriter();
|
Writer writer = new StringWriter();
|
||||||
MyParameterizedType<Integer> src = new MyParameterizedType<>(10);
|
MyParameterizedType<Integer> src = new MyParameterizedType<>(10);
|
||||||
@ -138,6 +151,7 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
assertEquals(src.getExpectedJson(), writer.toString());
|
assertEquals(src.getExpectedJson(), writer.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testParameterizedTypeWithReaderDeserialization() throws Exception {
|
public void testParameterizedTypeWithReaderDeserialization() throws Exception {
|
||||||
BagOfPrimitives bag = new BagOfPrimitives();
|
BagOfPrimitives bag = new BagOfPrimitives();
|
||||||
MyParameterizedType<BagOfPrimitives> expected = new MyParameterizedType<>(bag);
|
MyParameterizedType<BagOfPrimitives> expected = new MyParameterizedType<>(bag);
|
||||||
@ -158,6 +172,7 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testVariableTypeFieldsAndGenericArraysSerialization() throws Exception {
|
public void testVariableTypeFieldsAndGenericArraysSerialization() throws Exception {
|
||||||
Integer obj = 0;
|
Integer obj = 0;
|
||||||
Integer[] array = { 1, 2, 3 };
|
Integer[] array = { 1, 2, 3 };
|
||||||
@ -174,6 +189,7 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
assertEquals(objToSerialize.getExpectedJson(), json);
|
assertEquals(objToSerialize.getExpectedJson(), json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testVariableTypeFieldsAndGenericArraysDeserialization() throws Exception {
|
public void testVariableTypeFieldsAndGenericArraysDeserialization() throws Exception {
|
||||||
Integer obj = 0;
|
Integer obj = 0;
|
||||||
Integer[] array = { 1, 2, 3 };
|
Integer[] array = { 1, 2, 3 };
|
||||||
@ -191,6 +207,7 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
assertEquals(objAfterDeserialization.getExpectedJson(), json);
|
assertEquals(objAfterDeserialization.getExpectedJson(), json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testVariableTypeDeserialization() throws Exception {
|
public void testVariableTypeDeserialization() throws Exception {
|
||||||
Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
|
Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
|
||||||
ObjectWithTypeVariables<Integer> objToSerialize =
|
ObjectWithTypeVariables<Integer> objToSerialize =
|
||||||
@ -201,6 +218,7 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
assertEquals(objAfterDeserialization.getExpectedJson(), json);
|
assertEquals(objAfterDeserialization.getExpectedJson(), json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testVariableTypeArrayDeserialization() throws Exception {
|
public void testVariableTypeArrayDeserialization() throws Exception {
|
||||||
Integer[] array = { 1, 2, 3 };
|
Integer[] array = { 1, 2, 3 };
|
||||||
|
|
||||||
@ -213,6 +231,7 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
assertEquals(objAfterDeserialization.getExpectedJson(), json);
|
assertEquals(objAfterDeserialization.getExpectedJson(), json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testParameterizedTypeWithVariableTypeDeserialization() throws Exception {
|
public void testParameterizedTypeWithVariableTypeDeserialization() throws Exception {
|
||||||
List<Integer> list = new ArrayList<>();
|
List<Integer> list = new ArrayList<>();
|
||||||
list.add(4);
|
list.add(4);
|
||||||
@ -227,6 +246,7 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
assertEquals(objAfterDeserialization.getExpectedJson(), json);
|
assertEquals(objAfterDeserialization.getExpectedJson(), json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testParameterizedTypeGenericArraysSerialization() throws Exception {
|
public void testParameterizedTypeGenericArraysSerialization() throws Exception {
|
||||||
List<Integer> list = new ArrayList<>();
|
List<Integer> list = new ArrayList<>();
|
||||||
list.add(1);
|
list.add(1);
|
||||||
@ -240,6 +260,7 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
assertEquals("{\"arrayOfListOfTypeParameters\":[[1,2],[1,2]]}", json);
|
assertEquals("{\"arrayOfListOfTypeParameters\":[[1,2],[1,2]]}", json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testParameterizedTypeGenericArraysDeserialization() throws Exception {
|
public void testParameterizedTypeGenericArraysDeserialization() throws Exception {
|
||||||
List<Integer> list = new ArrayList<>();
|
List<Integer> list = new ArrayList<>();
|
||||||
list.add(1);
|
list.add(1);
|
||||||
@ -483,6 +504,7 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
int value = 30;
|
int value = 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDeepParameterizedTypeSerialization() {
|
public void testDeepParameterizedTypeSerialization() {
|
||||||
Amount<MyQuantity> amount = new Amount<>();
|
Amount<MyQuantity> amount = new Amount<>();
|
||||||
String json = gson.toJson(amount);
|
String json = gson.toJson(amount);
|
||||||
@ -490,6 +512,7 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
assertTrue(json.contains("30"));
|
assertTrue(json.contains("30"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDeepParameterizedTypeDeserialization() {
|
public void testDeepParameterizedTypeDeserialization() {
|
||||||
String json = "{value:30}";
|
String json = "{value:30}";
|
||||||
Type type = new TypeToken<Amount<MyQuantity>>() {}.getType();
|
Type type = new TypeToken<Amount<MyQuantity>>() {}.getType();
|
||||||
@ -497,4 +520,47 @@ public class ParameterizedTypesTest extends TestCase {
|
|||||||
assertEquals(30, amount.value);
|
assertEquals(30, amount.value);
|
||||||
}
|
}
|
||||||
// End: tests to reproduce issue 103
|
// End: tests to reproduce issue 103
|
||||||
|
|
||||||
|
private static void assertCorrectlyDeserialized(Object object) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Quantity> list = (List<Quantity>) object;
|
||||||
|
assertEquals(1, list.size());
|
||||||
|
assertEquals(4, list.get(0).q);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGsonFromJsonTypeToken() {
|
||||||
|
TypeToken<List<Quantity>> typeToken = new TypeToken<List<Quantity>>() {};
|
||||||
|
Type type = typeToken.getType();
|
||||||
|
|
||||||
|
{
|
||||||
|
JsonObject jsonObject = new JsonObject();
|
||||||
|
jsonObject.addProperty("q", 4);
|
||||||
|
JsonArray jsonArray = new JsonArray();
|
||||||
|
jsonArray.add(jsonObject);
|
||||||
|
|
||||||
|
assertCorrectlyDeserialized(gson.fromJson(jsonArray, typeToken));
|
||||||
|
assertCorrectlyDeserialized(gson.fromJson(jsonArray, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
String json = "[{\"q\":4}]";
|
||||||
|
|
||||||
|
{
|
||||||
|
assertCorrectlyDeserialized(gson.fromJson(json, typeToken));
|
||||||
|
assertCorrectlyDeserialized(gson.fromJson(json, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
assertCorrectlyDeserialized(gson.fromJson(new StringReader(json), typeToken));
|
||||||
|
assertCorrectlyDeserialized(gson.fromJson(new StringReader(json), type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
JsonReader reader = new JsonReader(new StringReader(json));
|
||||||
|
assertCorrectlyDeserialized(gson.fromJson(reader, typeToken));
|
||||||
|
|
||||||
|
reader = new JsonReader(new StringReader(json));
|
||||||
|
assertCorrectlyDeserialized(gson.fromJson(reader, type));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,11 @@ public class PrimitiveTest extends TestCase {
|
|||||||
public void testByteSerialization() {
|
public void testByteSerialization() {
|
||||||
assertEquals("1", gson.toJson(1, byte.class));
|
assertEquals("1", gson.toJson(1, byte.class));
|
||||||
assertEquals("1", gson.toJson(1, Byte.class));
|
assertEquals("1", gson.toJson(1, Byte.class));
|
||||||
|
assertEquals(Byte.toString(Byte.MIN_VALUE), gson.toJson(Byte.MIN_VALUE, Byte.class));
|
||||||
|
assertEquals(Byte.toString(Byte.MAX_VALUE), gson.toJson(Byte.MAX_VALUE, Byte.class));
|
||||||
|
// Should perform narrowing conversion
|
||||||
|
assertEquals("-128", gson.toJson(128, Byte.class));
|
||||||
|
assertEquals("1", gson.toJson(1.5, Byte.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testByteDeserialization() {
|
public void testByteDeserialization() {
|
||||||
@ -98,6 +103,13 @@ public class PrimitiveTest extends TestCase {
|
|||||||
public void testShortSerialization() {
|
public void testShortSerialization() {
|
||||||
assertEquals("1", gson.toJson(1, short.class));
|
assertEquals("1", gson.toJson(1, short.class));
|
||||||
assertEquals("1", gson.toJson(1, Short.class));
|
assertEquals("1", gson.toJson(1, Short.class));
|
||||||
|
assertEquals(Short.toString(Short.MIN_VALUE), gson.toJson(Short.MIN_VALUE, Short.class));
|
||||||
|
assertEquals(Short.toString(Short.MAX_VALUE), gson.toJson(Short.MAX_VALUE, Short.class));
|
||||||
|
// Should perform widening conversion
|
||||||
|
assertEquals("1", gson.toJson((byte) 1, Short.class));
|
||||||
|
// Should perform narrowing conversion
|
||||||
|
assertEquals("-32768", gson.toJson(32768, Short.class));
|
||||||
|
assertEquals("1", gson.toJson(1.5, Short.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testShortDeserialization() {
|
public void testShortDeserialization() {
|
||||||
@ -133,6 +145,54 @@ public class PrimitiveTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testIntSerialization() {
|
||||||
|
assertEquals("1", gson.toJson(1, int.class));
|
||||||
|
assertEquals("1", gson.toJson(1, Integer.class));
|
||||||
|
assertEquals(Integer.toString(Integer.MIN_VALUE), gson.toJson(Integer.MIN_VALUE, Integer.class));
|
||||||
|
assertEquals(Integer.toString(Integer.MAX_VALUE), gson.toJson(Integer.MAX_VALUE, Integer.class));
|
||||||
|
// Should perform widening conversion
|
||||||
|
assertEquals("1", gson.toJson((byte) 1, Integer.class));
|
||||||
|
// Should perform narrowing conversion
|
||||||
|
assertEquals("-2147483648", gson.toJson(2147483648L, Integer.class));
|
||||||
|
assertEquals("1", gson.toJson(1.5, Integer.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLongSerialization() {
|
||||||
|
assertEquals("1", gson.toJson(1L, long.class));
|
||||||
|
assertEquals("1", gson.toJson(1L, Long.class));
|
||||||
|
assertEquals(Long.toString(Long.MIN_VALUE), gson.toJson(Long.MIN_VALUE, Long.class));
|
||||||
|
assertEquals(Long.toString(Long.MAX_VALUE), gson.toJson(Long.MAX_VALUE, Long.class));
|
||||||
|
// Should perform widening conversion
|
||||||
|
assertEquals("1", gson.toJson((byte) 1, Long.class));
|
||||||
|
// Should perform narrowing conversion
|
||||||
|
assertEquals("1", gson.toJson(1.5, Long.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testFloatSerialization() {
|
||||||
|
assertEquals("1.5", gson.toJson(1.5f, float.class));
|
||||||
|
assertEquals("1.5", gson.toJson(1.5f, Float.class));
|
||||||
|
assertEquals(Float.toString(Float.MIN_VALUE), gson.toJson(Float.MIN_VALUE, Float.class));
|
||||||
|
assertEquals(Float.toString(Float.MAX_VALUE), gson.toJson(Float.MAX_VALUE, Float.class));
|
||||||
|
// Should perform widening conversion
|
||||||
|
assertEquals("1.0", gson.toJson((byte) 1, Float.class));
|
||||||
|
// (This widening conversion is actually lossy)
|
||||||
|
assertEquals(Float.toString(Long.MAX_VALUE - 10L), gson.toJson(Long.MAX_VALUE - 10L, Float.class));
|
||||||
|
// Should perform narrowing conversion
|
||||||
|
gson = new GsonBuilder().serializeSpecialFloatingPointValues().create();
|
||||||
|
assertEquals("Infinity", gson.toJson(Double.MAX_VALUE, Float.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDoubleSerialization() {
|
||||||
|
assertEquals("1.5", gson.toJson(1.5, double.class));
|
||||||
|
assertEquals("1.5", gson.toJson(1.5, Double.class));
|
||||||
|
assertEquals(Double.toString(Double.MIN_VALUE), gson.toJson(Double.MIN_VALUE, Double.class));
|
||||||
|
assertEquals(Double.toString(Double.MAX_VALUE), gson.toJson(Double.MAX_VALUE, Double.class));
|
||||||
|
// Should perform widening conversion
|
||||||
|
assertEquals("1.0", gson.toJson((byte) 1, Double.class));
|
||||||
|
// (This widening conversion is actually lossy)
|
||||||
|
assertEquals(Double.toString(Long.MAX_VALUE - 10L), gson.toJson(Long.MAX_VALUE - 10L, Double.class));
|
||||||
|
}
|
||||||
|
|
||||||
public void testPrimitiveIntegerAutoboxedInASingleElementArraySerialization() {
|
public void testPrimitiveIntegerAutoboxedInASingleElementArraySerialization() {
|
||||||
int target[] = {-9332};
|
int target[] = {-9332};
|
||||||
assertEquals("[-9332]", gson.toJson(target));
|
assertEquals("[-9332]", gson.toJson(target));
|
||||||
|
@ -2,6 +2,7 @@ package com.google.gson.functional;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.junit.Assume.assumeNotNull;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
@ -20,6 +21,7 @@ import com.google.gson.stream.JsonWriter;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -39,7 +41,7 @@ public class ReflectionAccessFilterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBlockInaccessibleJava() {
|
public void testBlockInaccessibleJava() throws ReflectiveOperationException {
|
||||||
Gson gson = new GsonBuilder()
|
Gson gson = new GsonBuilder()
|
||||||
.addReflectionAccessFilter(ReflectionAccessFilter.BLOCK_INACCESSIBLE_JAVA)
|
.addReflectionAccessFilter(ReflectionAccessFilter.BLOCK_INACCESSIBLE_JAVA)
|
||||||
.create();
|
.create();
|
||||||
@ -51,16 +53,26 @@ public class ReflectionAccessFilterTest {
|
|||||||
} catch (JsonIOException expected) {
|
} catch (JsonIOException expected) {
|
||||||
// Note: This test is rather brittle and depends on the JDK implementation
|
// Note: This test is rather brittle and depends on the JDK implementation
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"Field 'java.io.File#path' is not accessible and ReflectionAccessFilter does not permit "
|
"Field 'java.io.File#path' is not accessible and ReflectionAccessFilter does not permit"
|
||||||
+ "making it accessible. Register a TypeAdapter for the declaring type or adjust the access filter.",
|
+ " making it accessible. Register a TypeAdapter for the declaring type, adjust the access"
|
||||||
|
+ " filter or increase the visibility of the element and its declaring type.",
|
||||||
expected.getMessage()
|
expected.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// But serialization should succeed for classes with only public fields
|
|
||||||
// awt is unavailable and this should just work (tm)
|
// But serialization should succeed for classes with only public fields.
|
||||||
// String json = gson.toJson(new Point(1, 2));
|
// Not many JDK classes have mutable public fields, thank goodness, but java.awt.Point does.
|
||||||
// assertEquals("{\"x\":1,\"y\":2}", json);
|
Class<?> pointClass = null;
|
||||||
|
try {
|
||||||
|
pointClass = Class.forName("java.awt.Point");
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
}
|
||||||
|
assumeNotNull(pointClass);
|
||||||
|
Constructor<?> pointConstructor = pointClass.getConstructor(int.class, int.class);
|
||||||
|
Object point = pointConstructor.newInstance(1, 2);
|
||||||
|
String json = gson.toJson(point);
|
||||||
|
assertEquals("{\"x\":1,\"y\":2}", json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -74,8 +86,9 @@ public class ReflectionAccessFilterTest {
|
|||||||
fail("Expected exception; test needs to be run with Java >= 9");
|
fail("Expected exception; test needs to be run with Java >= 9");
|
||||||
} catch (JsonIOException expected) {
|
} catch (JsonIOException expected) {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"Field 'java.io.Reader#lock' is not accessible and ReflectionAccessFilter does not permit "
|
"Field 'java.io.Reader#lock' is not accessible and ReflectionAccessFilter does not permit"
|
||||||
+ "making it accessible. Register a TypeAdapter for the declaring type or adjust the access filter.",
|
+ " making it accessible. Register a TypeAdapter for the declaring type, adjust the access"
|
||||||
|
+ " filter or increase the visibility of the element and its declaring type.",
|
||||||
expected.getMessage()
|
expected.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -93,8 +106,8 @@ public class ReflectionAccessFilterTest {
|
|||||||
fail();
|
fail();
|
||||||
} catch (JsonIOException expected) {
|
} catch (JsonIOException expected) {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"ReflectionAccessFilter does not permit using reflection for class java.lang.Thread. "
|
"ReflectionAccessFilter does not permit using reflection for class java.lang.Thread."
|
||||||
+ "Register a TypeAdapter for this type or adjust the access filter.",
|
+ " Register a TypeAdapter for this type or adjust the access filter.",
|
||||||
expected.getMessage()
|
expected.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -111,9 +124,9 @@ public class ReflectionAccessFilterTest {
|
|||||||
fail();
|
fail();
|
||||||
} catch (JsonIOException expected) {
|
} catch (JsonIOException expected) {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"ReflectionAccessFilter does not permit using reflection for class java.io.Reader "
|
"ReflectionAccessFilter does not permit using reflection for class java.io.Reader"
|
||||||
+ "(supertype of class com.google.gson.functional.ReflectionAccessFilterTest$ClassExtendingJdkClass). "
|
+ " (supertype of class com.google.gson.functional.ReflectionAccessFilterTest$ClassExtendingJdkClass)."
|
||||||
+ "Register a TypeAdapter for this type or adjust the access filter.",
|
+ " Register a TypeAdapter for this type or adjust the access filter.",
|
||||||
expected.getMessage()
|
expected.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -141,9 +154,10 @@ public class ReflectionAccessFilterTest {
|
|||||||
fail("Expected exception; test needs to be run with Java >= 9");
|
fail("Expected exception; test needs to be run with Java >= 9");
|
||||||
} catch (JsonIOException expected) {
|
} catch (JsonIOException expected) {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"Field 'com.google.gson.functional.ReflectionAccessFilterTest$ClassWithStaticField#i' "
|
"Field 'com.google.gson.functional.ReflectionAccessFilterTest$ClassWithStaticField#i'"
|
||||||
+ "is not accessible and ReflectionAccessFilter does not permit making it accessible. "
|
+ " is not accessible and ReflectionAccessFilter does not permit making it accessible."
|
||||||
+ "Register a TypeAdapter for the declaring type or adjust the access filter.",
|
+ " Register a TypeAdapter for the declaring type, adjust the access filter or increase"
|
||||||
|
+ " the visibility of the element and its declaring type.",
|
||||||
expected.getMessage()
|
expected.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -183,9 +197,9 @@ public class ReflectionAccessFilterTest {
|
|||||||
fail();
|
fail();
|
||||||
} catch (JsonIOException expected) {
|
} catch (JsonIOException expected) {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"ReflectionAccessFilter does not permit using reflection for class "
|
"ReflectionAccessFilter does not permit using reflection for class"
|
||||||
+ "com.google.gson.functional.ReflectionAccessFilterTest$SuperTestClass. "
|
+ " com.google.gson.functional.ReflectionAccessFilterTest$SuperTestClass."
|
||||||
+ "Register a TypeAdapter for this type or adjust the access filter.",
|
+ " Register a TypeAdapter for this type or adjust the access filter.",
|
||||||
expected.getMessage()
|
expected.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -222,9 +236,10 @@ public class ReflectionAccessFilterTest {
|
|||||||
fail("Expected exception; test needs to be run with Java >= 9");
|
fail("Expected exception; test needs to be run with Java >= 9");
|
||||||
} catch (JsonIOException expected) {
|
} catch (JsonIOException expected) {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"Field 'com.google.gson.functional.ReflectionAccessFilterTest$ClassWithPrivateField#i' "
|
"Field 'com.google.gson.functional.ReflectionAccessFilterTest$ClassWithPrivateField#i'"
|
||||||
+ "is not accessible and ReflectionAccessFilter does not permit making it accessible. "
|
+ " is not accessible and ReflectionAccessFilter does not permit making it accessible."
|
||||||
+ "Register a TypeAdapter for the declaring type or adjust the access filter.",
|
+ " Register a TypeAdapter for the declaring type, adjust the access filter or increase"
|
||||||
|
+ " the visibility of the element and its declaring type.",
|
||||||
expected.getMessage()
|
expected.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -263,9 +278,9 @@ public class ReflectionAccessFilterTest {
|
|||||||
fail("Expected exception; test needs to be run with Java >= 9");
|
fail("Expected exception; test needs to be run with Java >= 9");
|
||||||
} catch (JsonIOException expected) {
|
} catch (JsonIOException expected) {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"Unable to invoke no-args constructor of class com.google.gson.functional.ReflectionAccessFilterTest$ClassWithPrivateNoArgsConstructor; "
|
"Unable to invoke no-args constructor of class com.google.gson.functional.ReflectionAccessFilterTest$ClassWithPrivateNoArgsConstructor;"
|
||||||
+ "constructor is not accessible and ReflectionAccessFilter does not permit making it accessible. Register an "
|
+ " constructor is not accessible and ReflectionAccessFilter does not permit making it accessible. Register an"
|
||||||
+ "InstanceCreator or a TypeAdapter for this type, change the visibility of the constructor or adjust the access filter.",
|
+ " InstanceCreator or a TypeAdapter for this type, change the visibility of the constructor or adjust the access filter.",
|
||||||
expected.getMessage()
|
expected.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -295,9 +310,9 @@ public class ReflectionAccessFilterTest {
|
|||||||
fail();
|
fail();
|
||||||
} catch (JsonIOException expected) {
|
} catch (JsonIOException expected) {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"Unable to create instance of class com.google.gson.functional.ReflectionAccessFilterTest$ClassWithoutNoArgsConstructor; "
|
"Unable to create instance of class com.google.gson.functional.ReflectionAccessFilterTest$ClassWithoutNoArgsConstructor;"
|
||||||
+ "ReflectionAccessFilter does not permit using reflection or Unsafe. Register an InstanceCreator "
|
+ " ReflectionAccessFilter does not permit using reflection or Unsafe. Register an InstanceCreator"
|
||||||
+ "or a TypeAdapter for this type or adjust the access filter to allow using reflection.",
|
+ " or a TypeAdapter for this type or adjust the access filter to allow using reflection.",
|
||||||
expected.getMessage()
|
expected.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -311,7 +326,7 @@ public class ReflectionAccessFilterTest {
|
|||||||
}
|
}
|
||||||
@Override public void write(JsonWriter out, ClassWithoutNoArgsConstructor value) throws IOException {
|
@Override public void write(JsonWriter out, ClassWithoutNoArgsConstructor value) throws IOException {
|
||||||
throw new AssertionError("Not needed for test");
|
throw new AssertionError("Not needed for test");
|
||||||
};
|
}
|
||||||
})
|
})
|
||||||
.create();
|
.create();
|
||||||
ClassWithoutNoArgsConstructor deserialized = gson.fromJson("{}", ClassWithoutNoArgsConstructor.class);
|
ClassWithoutNoArgsConstructor deserialized = gson.fromJson("{}", ClassWithoutNoArgsConstructor.class);
|
||||||
@ -357,8 +372,8 @@ public class ReflectionAccessFilterTest {
|
|||||||
fail();
|
fail();
|
||||||
} catch (JsonIOException expected) {
|
} catch (JsonIOException expected) {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"ReflectionAccessFilter does not permit using reflection for class com.google.gson.functional.ReflectionAccessFilterTest$OtherClass. "
|
"ReflectionAccessFilter does not permit using reflection for class com.google.gson.functional.ReflectionAccessFilterTest$OtherClass."
|
||||||
+ "Register a TypeAdapter for this type or adjust the access filter.",
|
+ " Register a TypeAdapter for this type or adjust the access filter.",
|
||||||
expected.getMessage()
|
expected.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -417,8 +432,8 @@ public class ReflectionAccessFilterTest {
|
|||||||
fail();
|
fail();
|
||||||
} catch (JsonIOException expected) {
|
} catch (JsonIOException expected) {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"Interfaces can't be instantiated! Register an InstanceCreator or a TypeAdapter for "
|
"Interfaces can't be instantiated! Register an InstanceCreator or a TypeAdapter for"
|
||||||
+ "this type. Interface name: java.lang.Runnable",
|
+ " this type. Interface name: java.lang.Runnable",
|
||||||
expected.getMessage()
|
expected.getMessage()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -56,8 +56,8 @@ public class ReflectionAccessTest {
|
|||||||
fail("Unexpected exception; test has to be run with `--illegal-access=deny`");
|
fail("Unexpected exception; test has to be run with `--illegal-access=deny`");
|
||||||
} catch (JsonIOException expected) {
|
} catch (JsonIOException expected) {
|
||||||
assertTrue(expected.getMessage().startsWith(
|
assertTrue(expected.getMessage().startsWith(
|
||||||
"Failed making constructor 'java.util.Collections$EmptyList#EmptyList()' accessible; "
|
"Failed making constructor 'java.util.Collections$EmptyList()' accessible;"
|
||||||
+ "either change its visibility or write a custom InstanceCreator or TypeAdapter for its declaring type"
|
+ " either increase its visibility or write a custom InstanceCreator or TypeAdapter for its declaring type: "
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,193 @@
|
|||||||
|
package com.google.gson.functional;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import com.google.gson.JsonSerializationContext;
|
||||||
|
import com.google.gson.JsonSerializer;
|
||||||
|
import com.google.gson.TypeAdapter;
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class TypeAdapterRuntimeTypeWrapperTest {
|
||||||
|
private static class Base {
|
||||||
|
}
|
||||||
|
private static class Subclass extends Base {
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
String f = "test";
|
||||||
|
}
|
||||||
|
private static class Container {
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
Base b = new Subclass();
|
||||||
|
}
|
||||||
|
private static class Deserializer implements JsonDeserializer<Base> {
|
||||||
|
@Override
|
||||||
|
public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
|
||||||
|
throw new AssertionError("not needed for this test");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When custom {@link JsonSerializer} is registered for Base should
|
||||||
|
* prefer that over reflective adapter for Subclass for serialization.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testJsonSerializer() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(Base.class, new JsonSerializer<Base>() {
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(Base src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive("serializer");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.create();
|
||||||
|
|
||||||
|
String json = gson.toJson(new Container());
|
||||||
|
assertEquals("{\"b\":\"serializer\"}", json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When only {@link JsonDeserializer} is registered for Base, then on
|
||||||
|
* serialization should prefer reflective adapter for Subclass since
|
||||||
|
* Base would use reflective adapter as delegate.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testJsonDeserializer_ReflectiveSerializerDelegate() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(Base.class, new Deserializer())
|
||||||
|
.create();
|
||||||
|
|
||||||
|
String json = gson.toJson(new Container());
|
||||||
|
assertEquals("{\"b\":{\"f\":\"test\"}}", json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When {@link JsonDeserializer} with custom adapter as delegate is
|
||||||
|
* registered for Base, then on serialization should prefer custom adapter
|
||||||
|
* delegate for Base over reflective adapter for Subclass.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testJsonDeserializer_CustomSerializerDelegate() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
// Register custom delegate
|
||||||
|
.registerTypeAdapter(Base.class, new TypeAdapter<Base>() {
|
||||||
|
@Override
|
||||||
|
public Base read(JsonReader in) throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void write(JsonWriter out, Base value) throws IOException {
|
||||||
|
out.value("custom delegate");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.registerTypeAdapter(Base.class, new Deserializer())
|
||||||
|
.create();
|
||||||
|
|
||||||
|
String json = gson.toJson(new Container());
|
||||||
|
assertEquals("{\"b\":\"custom delegate\"}", json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When two (or more) {@link JsonDeserializer}s are registered for Base
|
||||||
|
* which eventually fall back to reflective adapter as delegate, then on
|
||||||
|
* serialization should prefer reflective adapter for Subclass.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testJsonDeserializer_ReflectiveTreeSerializerDelegate() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
// Register delegate which itself falls back to reflective serialization
|
||||||
|
.registerTypeAdapter(Base.class, new Deserializer())
|
||||||
|
.registerTypeAdapter(Base.class, new Deserializer())
|
||||||
|
.create();
|
||||||
|
|
||||||
|
String json = gson.toJson(new Container());
|
||||||
|
assertEquals("{\"b\":{\"f\":\"test\"}}", json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When {@link JsonDeserializer} with {@link JsonSerializer} as delegate
|
||||||
|
* is registered for Base, then on serialization should prefer
|
||||||
|
* {@code JsonSerializer} over reflective adapter for Subclass.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testJsonDeserializer_JsonSerializerDelegate() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
// Register JsonSerializer as delegate
|
||||||
|
.registerTypeAdapter(Base.class, new JsonSerializer<Base>() {
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(Base src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive("custom delegate");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.registerTypeAdapter(Base.class, new Deserializer())
|
||||||
|
.create();
|
||||||
|
|
||||||
|
String json = gson.toJson(new Container());
|
||||||
|
assertEquals("{\"b\":\"custom delegate\"}", json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a {@link JsonDeserializer} is registered for Subclass, and a custom
|
||||||
|
* {@link JsonSerializer} is registered for Base, then Gson should prefer
|
||||||
|
* the reflective adapter for Subclass for backward compatibility (see
|
||||||
|
* https://github.com/google/gson/pull/1787#issuecomment-1222175189) even
|
||||||
|
* though normally TypeAdapterRuntimeTypeWrapper should prefer the custom
|
||||||
|
* serializer for Base.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testJsonDeserializer_SubclassBackwardCompatibility() {
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(Subclass.class, new JsonDeserializer<Subclass>() {
|
||||||
|
@Override
|
||||||
|
public Subclass deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
|
||||||
|
throw new AssertionError("not needed for this test");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.registerTypeAdapter(Base.class, new JsonSerializer<Base>() {
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(Base src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive("base");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.create();
|
||||||
|
|
||||||
|
String json = gson.toJson(new Container());
|
||||||
|
assertEquals("{\"b\":{\"f\":\"test\"}}", json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CyclicBase {
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
CyclicBase f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CyclicSub extends CyclicBase {
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
int i;
|
||||||
|
|
||||||
|
public CyclicSub(int i) {
|
||||||
|
this.i = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests behavior when the type of a field refers to a type whose adapter is
|
||||||
|
* currently in the process of being created. For these cases {@link Gson}
|
||||||
|
* uses a future adapter for the type. That adapter later uses the actual
|
||||||
|
* adapter as delegate.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGsonFutureAdapter() {
|
||||||
|
CyclicBase b = new CyclicBase();
|
||||||
|
b.f = new CyclicSub(2);
|
||||||
|
String json = new Gson().toJson(b);
|
||||||
|
assertEquals("{\"f\":{\"i\":2}}", json);
|
||||||
|
}
|
||||||
|
}
|
@ -15,13 +15,17 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.gson.functional;
|
package com.google.gson.functional;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.annotations.Since;
|
import com.google.gson.annotations.Since;
|
||||||
import com.google.gson.annotations.Until;
|
import com.google.gson.annotations.Until;
|
||||||
import com.google.gson.common.TestTypes.BagOfPrimitives;
|
import com.google.gson.common.TestTypes.BagOfPrimitives;
|
||||||
|
import org.junit.Test;
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Functional tests for versioning support in Gson.
|
* Functional tests for versioning support in Gson.
|
||||||
@ -29,47 +33,60 @@ import junit.framework.TestCase;
|
|||||||
* @author Inderjeet Singh
|
* @author Inderjeet Singh
|
||||||
* @author Joel Leitch
|
* @author Joel Leitch
|
||||||
*/
|
*/
|
||||||
public class VersioningTest extends TestCase {
|
public class VersioningTest {
|
||||||
private static final int A = 0;
|
private static final int A = 0;
|
||||||
private static final int B = 1;
|
private static final int B = 1;
|
||||||
private static final int C = 2;
|
private static final int C = 2;
|
||||||
private static final int D = 3;
|
private static final int D = 3;
|
||||||
|
|
||||||
private GsonBuilder builder;
|
private static Gson gsonWithVersion(double version) {
|
||||||
|
return new GsonBuilder().setVersion(version).create();
|
||||||
@Override
|
|
||||||
protected void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
builder = new GsonBuilder();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testVersionedUntilSerialization() {
|
public void testVersionedUntilSerialization() {
|
||||||
Version1 target = new Version1();
|
Version1 target = new Version1();
|
||||||
Gson gson = builder.setVersion(1.29).create();
|
Gson gson = gsonWithVersion(1.29);
|
||||||
String json = gson.toJson(target);
|
String json = gson.toJson(target);
|
||||||
assertTrue(json.contains("\"a\":" + A));
|
assertTrue(json.contains("\"a\":" + A));
|
||||||
|
|
||||||
gson = builder.setVersion(1.3).create();
|
gson = gsonWithVersion(1.3);
|
||||||
|
json = gson.toJson(target);
|
||||||
|
assertFalse(json.contains("\"a\":" + A));
|
||||||
|
|
||||||
|
gson = gsonWithVersion(1.31);
|
||||||
json = gson.toJson(target);
|
json = gson.toJson(target);
|
||||||
assertFalse(json.contains("\"a\":" + A));
|
assertFalse(json.contains("\"a\":" + A));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testVersionedUntilDeserialization() {
|
public void testVersionedUntilDeserialization() {
|
||||||
Gson gson = builder.setVersion(1.3).create();
|
|
||||||
String json = "{\"a\":3,\"b\":4,\"c\":5}";
|
String json = "{\"a\":3,\"b\":4,\"c\":5}";
|
||||||
|
|
||||||
|
Gson gson = gsonWithVersion(1.29);
|
||||||
Version1 version1 = gson.fromJson(json, Version1.class);
|
Version1 version1 = gson.fromJson(json, Version1.class);
|
||||||
|
assertEquals(3, version1.a);
|
||||||
|
|
||||||
|
gson = gsonWithVersion(1.3);
|
||||||
|
version1 = gson.fromJson(json, Version1.class);
|
||||||
|
assertEquals(A, version1.a);
|
||||||
|
|
||||||
|
gson = gsonWithVersion(1.31);
|
||||||
|
version1 = gson.fromJson(json, Version1.class);
|
||||||
assertEquals(A, version1.a);
|
assertEquals(A, version1.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testVersionedClassesSerialization() {
|
public void testVersionedClassesSerialization() {
|
||||||
Gson gson = builder.setVersion(1.0).create();
|
Gson gson = gsonWithVersion(1.0);
|
||||||
String json1 = gson.toJson(new Version1());
|
String json1 = gson.toJson(new Version1());
|
||||||
String json2 = gson.toJson(new Version1_1());
|
String json2 = gson.toJson(new Version1_1());
|
||||||
assertEquals(json1, json2);
|
assertEquals(json1, json2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testVersionedClassesDeserialization() {
|
public void testVersionedClassesDeserialization() {
|
||||||
Gson gson = builder.setVersion(1.0).create();
|
Gson gson = gsonWithVersion(1.0);
|
||||||
String json = "{\"a\":3,\"b\":4,\"c\":5}";
|
String json = "{\"a\":3,\"b\":4,\"c\":5}";
|
||||||
Version1 version1 = gson.fromJson(json, Version1.class);
|
Version1 version1 = gson.fromJson(json, Version1.class);
|
||||||
assertEquals(3, version1.a);
|
assertEquals(3, version1.a);
|
||||||
@ -80,13 +97,15 @@ public class VersioningTest extends TestCase {
|
|||||||
assertEquals(C, version1_1.c);
|
assertEquals(C, version1_1.c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testIgnoreLaterVersionClassSerialization() {
|
public void testIgnoreLaterVersionClassSerialization() {
|
||||||
Gson gson = builder.setVersion(1.0).create();
|
Gson gson = gsonWithVersion(1.0);
|
||||||
assertEquals("null", gson.toJson(new Version1_2()));
|
assertEquals("null", gson.toJson(new Version1_2()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testIgnoreLaterVersionClassDeserialization() {
|
public void testIgnoreLaterVersionClassDeserialization() {
|
||||||
Gson gson = builder.setVersion(1.0).create();
|
Gson gson = gsonWithVersion(1.0);
|
||||||
String json = "{\"a\":3,\"b\":4,\"c\":5,\"d\":6}";
|
String json = "{\"a\":3,\"b\":4,\"c\":5,\"d\":6}";
|
||||||
Version1_2 version1_2 = gson.fromJson(json, Version1_2.class);
|
Version1_2 version1_2 = gson.fromJson(json, Version1_2.class);
|
||||||
// Since the class is versioned to be after 1.0, we expect null
|
// Since the class is versioned to be after 1.0, we expect null
|
||||||
@ -94,14 +113,16 @@ public class VersioningTest extends TestCase {
|
|||||||
assertNull(version1_2);
|
assertNull(version1_2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testVersionedGsonWithUnversionedClassesSerialization() {
|
public void testVersionedGsonWithUnversionedClassesSerialization() {
|
||||||
Gson gson = builder.setVersion(1.0).create();
|
Gson gson = gsonWithVersion(1.0);
|
||||||
BagOfPrimitives target = new BagOfPrimitives(10, 20, false, "stringValue");
|
BagOfPrimitives target = new BagOfPrimitives(10, 20, false, "stringValue");
|
||||||
assertEquals(target.getExpectedJson(), gson.toJson(target));
|
assertEquals(target.getExpectedJson(), gson.toJson(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testVersionedGsonWithUnversionedClassesDeserialization() {
|
public void testVersionedGsonWithUnversionedClassesDeserialization() {
|
||||||
Gson gson = builder.setVersion(1.0).create();
|
Gson gson = gsonWithVersion(1.0);
|
||||||
String json = "{\"longValue\":10,\"intValue\":20,\"booleanValue\":false}";
|
String json = "{\"longValue\":10,\"intValue\":20,\"booleanValue\":false}";
|
||||||
|
|
||||||
BagOfPrimitives expected = new BagOfPrimitives();
|
BagOfPrimitives expected = new BagOfPrimitives();
|
||||||
@ -112,34 +133,45 @@ public class VersioningTest extends TestCase {
|
|||||||
assertEquals(expected, actual);
|
assertEquals(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testVersionedGsonMixingSinceAndUntilSerialization() {
|
public void testVersionedGsonMixingSinceAndUntilSerialization() {
|
||||||
Gson gson = builder.setVersion(1.0).create();
|
Gson gson = gsonWithVersion(1.0);
|
||||||
SinceUntilMixing target = new SinceUntilMixing();
|
SinceUntilMixing target = new SinceUntilMixing();
|
||||||
String json = gson.toJson(target);
|
String json = gson.toJson(target);
|
||||||
assertFalse(json.contains("\"b\":" + B));
|
assertFalse(json.contains("\"b\":" + B));
|
||||||
|
|
||||||
gson = builder.setVersion(1.2).create();
|
gson = gsonWithVersion(1.2);
|
||||||
json = gson.toJson(target);
|
json = gson.toJson(target);
|
||||||
assertTrue(json.contains("\"b\":" + B));
|
assertTrue(json.contains("\"b\":" + B));
|
||||||
|
|
||||||
gson = builder.setVersion(1.3).create();
|
gson = gsonWithVersion(1.3);
|
||||||
|
json = gson.toJson(target);
|
||||||
|
assertFalse(json.contains("\"b\":" + B));
|
||||||
|
|
||||||
|
gson = gsonWithVersion(1.4);
|
||||||
json = gson.toJson(target);
|
json = gson.toJson(target);
|
||||||
assertFalse(json.contains("\"b\":" + B));
|
assertFalse(json.contains("\"b\":" + B));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testVersionedGsonMixingSinceAndUntilDeserialization() {
|
public void testVersionedGsonMixingSinceAndUntilDeserialization() {
|
||||||
String json = "{\"a\":5,\"b\":6}";
|
String json = "{\"a\":5,\"b\":6}";
|
||||||
Gson gson = builder.setVersion(1.0).create();
|
Gson gson = gsonWithVersion(1.0);
|
||||||
SinceUntilMixing result = gson.fromJson(json, SinceUntilMixing.class);
|
SinceUntilMixing result = gson.fromJson(json, SinceUntilMixing.class);
|
||||||
assertEquals(5, result.a);
|
assertEquals(5, result.a);
|
||||||
assertEquals(B, result.b);
|
assertEquals(B, result.b);
|
||||||
|
|
||||||
gson = builder.setVersion(1.2).create();
|
gson = gsonWithVersion(1.2);
|
||||||
result = gson.fromJson(json, SinceUntilMixing.class);
|
result = gson.fromJson(json, SinceUntilMixing.class);
|
||||||
assertEquals(5, result.a);
|
assertEquals(5, result.a);
|
||||||
assertEquals(6, result.b);
|
assertEquals(6, result.b);
|
||||||
|
|
||||||
gson = builder.setVersion(1.3).create();
|
gson = gsonWithVersion(1.3);
|
||||||
|
result = gson.fromJson(json, SinceUntilMixing.class);
|
||||||
|
assertEquals(5, result.a);
|
||||||
|
assertEquals(B, result.b);
|
||||||
|
|
||||||
|
gson = gsonWithVersion(1.4);
|
||||||
result = gson.fromJson(json, SinceUntilMixing.class);
|
result = gson.fromJson(json, SinceUntilMixing.class);
|
||||||
assertEquals(5, result.a);
|
assertEquals(5, result.a);
|
||||||
assertEquals(B, result.b);
|
assertEquals(B, result.b);
|
||||||
|
@ -37,9 +37,8 @@ public final class UnsafeAllocatorInstantiationTest extends TestCase {
|
|||||||
* to instantiate an interface
|
* to instantiate an interface
|
||||||
*/
|
*/
|
||||||
public void testInterfaceInstantiation() throws Exception {
|
public void testInterfaceInstantiation() throws Exception {
|
||||||
UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
|
|
||||||
try {
|
try {
|
||||||
unsafeAllocator.newInstance(Interface.class);
|
UnsafeAllocator.INSTANCE.newInstance(Interface.class);
|
||||||
fail();
|
fail();
|
||||||
} catch (AssertionError e) {
|
} catch (AssertionError e) {
|
||||||
assertTrue(e.getMessage().startsWith("UnsafeAllocator is used for non-instantiable type"));
|
assertTrue(e.getMessage().startsWith("UnsafeAllocator is used for non-instantiable type"));
|
||||||
@ -51,9 +50,8 @@ public final class UnsafeAllocatorInstantiationTest extends TestCase {
|
|||||||
* to instantiate an abstract class
|
* to instantiate an abstract class
|
||||||
*/
|
*/
|
||||||
public void testAbstractClassInstantiation() throws Exception {
|
public void testAbstractClassInstantiation() throws Exception {
|
||||||
UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
|
|
||||||
try {
|
try {
|
||||||
unsafeAllocator.newInstance(AbstractClass.class);
|
UnsafeAllocator.INSTANCE.newInstance(AbstractClass.class);
|
||||||
fail();
|
fail();
|
||||||
} catch (AssertionError e) {
|
} catch (AssertionError e) {
|
||||||
assertTrue(e.getMessage().startsWith("UnsafeAllocator is used for non-instantiable type"));
|
assertTrue(e.getMessage().startsWith("UnsafeAllocator is used for non-instantiable type"));
|
||||||
@ -64,8 +62,7 @@ public final class UnsafeAllocatorInstantiationTest extends TestCase {
|
|||||||
* Ensure that no exception is thrown when trying to instantiate a concrete class
|
* Ensure that no exception is thrown when trying to instantiate a concrete class
|
||||||
*/
|
*/
|
||||||
public void testConcreteClassInstantiation() throws Exception {
|
public void testConcreteClassInstantiation() throws Exception {
|
||||||
UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
|
ConcreteClass instance = UnsafeAllocator.INSTANCE.newInstance(ConcreteClass.class);
|
||||||
ConcreteClass instance = unsafeAllocator.newInstance(ConcreteClass.class);
|
|
||||||
assertNotNull(instance);
|
assertNotNull(instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,14 +22,12 @@ import java.text.SimpleDateFormat;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.TypeAdapter;
|
import com.google.gson.TypeAdapter;
|
||||||
import com.google.gson.TypeAdapterFactory;
|
import com.google.gson.TypeAdapterFactory;
|
||||||
import com.google.gson.internal.JavaVersion;
|
import com.google.gson.internal.JavaVersion;
|
||||||
import com.google.gson.internal.bind.DefaultDateTypeAdapter.DateType;
|
import com.google.gson.internal.bind.DefaultDateTypeAdapter.DateType;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,6 +73,39 @@ public class DefaultDateTypeAdapterTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testParsingDatesFormattedWithSystemLocale() throws Exception {
|
||||||
|
// TODO(eamonnmcmanus): fix this test, which fails on JDK 8 and 17
|
||||||
|
if (JavaVersion.getMajorJavaVersion() != 11) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TimeZone defaultTimeZone = TimeZone.getDefault();
|
||||||
|
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||||
|
Locale defaultLocale = Locale.getDefault();
|
||||||
|
Locale.setDefault(Locale.FRANCE);
|
||||||
|
try {
|
||||||
|
String afterYearSep = JavaVersion.isJava9OrLater() ? " à " : " ";
|
||||||
|
assertParsed(String.format("1 janv. 1970%s00:00:00", afterYearSep),
|
||||||
|
DateType.DATE.createDefaultsAdapterFactory());
|
||||||
|
assertParsed("01/01/70", DateType.DATE.createAdapterFactory(DateFormat.SHORT));
|
||||||
|
assertParsed("1 janv. 1970", DateType.DATE.createAdapterFactory(DateFormat.MEDIUM));
|
||||||
|
assertParsed("1 janvier 1970", DateType.DATE.createAdapterFactory(DateFormat.LONG));
|
||||||
|
assertParsed("01/01/70 00:00",
|
||||||
|
DateType.DATE.createAdapterFactory(DateFormat.SHORT, DateFormat.SHORT));
|
||||||
|
assertParsed(String.format("1 janv. 1970%s00:00:00", afterYearSep),
|
||||||
|
DateType.DATE.createAdapterFactory(DateFormat.MEDIUM, DateFormat.MEDIUM));
|
||||||
|
assertParsed(String.format("1 janvier 1970%s00:00:00 UTC", afterYearSep),
|
||||||
|
DateType.DATE.createAdapterFactory(DateFormat.LONG, DateFormat.LONG));
|
||||||
|
assertParsed(JavaVersion.isJava9OrLater() ? (JavaVersion.getMajorJavaVersion() <11 ?
|
||||||
|
"jeudi 1 janvier 1970 à 00:00:00 Coordinated Universal Time" :
|
||||||
|
"jeudi 1 janvier 1970 à 00:00:00 Temps universel coordonné") :
|
||||||
|
"jeudi 1 janvier 1970 00 h 00 UTC",
|
||||||
|
DateType.DATE.createAdapterFactory(DateFormat.FULL, DateFormat.FULL));
|
||||||
|
} finally {
|
||||||
|
TimeZone.setDefault(defaultTimeZone);
|
||||||
|
Locale.setDefault(defaultLocale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void testParsingDatesFormattedWithUsLocale() throws Exception {
|
public void testParsingDatesFormattedWithUsLocale() throws Exception {
|
||||||
TimeZone defaultTimeZone = TimeZone.getDefault();
|
TimeZone defaultTimeZone = TimeZone.getDefault();
|
||||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
package com.google.gson.internal.bind;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.TypeAdapter;
|
||||||
|
import com.google.gson.internal.reflect.Java17ReflectionHelperTest;
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.attribute.GroupPrincipal;
|
||||||
|
import java.nio.file.attribute.UserPrincipal;
|
||||||
|
import java.security.Principal;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class Java17ReflectiveTypeAdapterFactoryTest {
|
||||||
|
|
||||||
|
// The class jdk.net.UnixDomainPrincipal is one of the few Record types that are included in the JDK.
|
||||||
|
// We use this to test serialization and deserialization of Record classes, so we do not need to
|
||||||
|
// have record support at the language level for these tests. This class was added in JDK 16.
|
||||||
|
Class<?> unixDomainPrincipalClass;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
unixDomainPrincipalClass = Class.forName("jdk.net.UnixDomainPrincipal");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class for which the normal reflection based adapter is used
|
||||||
|
private static class DummyClass {
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public String s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCustomAdapterForRecords() {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
TypeAdapter<?> recordAdapter = gson.getAdapter(unixDomainPrincipalClass);
|
||||||
|
TypeAdapter<?> defaultReflectionAdapter = gson.getAdapter(DummyClass.class);
|
||||||
|
assertNotEquals(recordAdapter.getClass(), defaultReflectionAdapter.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSerializeRecords() throws ReflectiveOperationException {
|
||||||
|
Gson gson =
|
||||||
|
new GsonBuilder()
|
||||||
|
.registerTypeAdapter(UserPrincipal.class, new PrincipalTypeAdapter<>())
|
||||||
|
.registerTypeAdapter(GroupPrincipal.class, new PrincipalTypeAdapter<>())
|
||||||
|
.create();
|
||||||
|
|
||||||
|
UserPrincipal userPrincipal = gson.fromJson("\"user\"", UserPrincipal.class);
|
||||||
|
GroupPrincipal groupPrincipal = gson.fromJson("\"group\"", GroupPrincipal.class);
|
||||||
|
Object recordInstance =
|
||||||
|
unixDomainPrincipalClass
|
||||||
|
.getDeclaredConstructor(UserPrincipal.class, GroupPrincipal.class)
|
||||||
|
.newInstance(userPrincipal, groupPrincipal);
|
||||||
|
String serialized = gson.toJson(recordInstance);
|
||||||
|
Object deserializedRecordInstance = gson.fromJson(serialized, unixDomainPrincipalClass);
|
||||||
|
|
||||||
|
assertEquals(recordInstance, deserializedRecordInstance);
|
||||||
|
assertEquals("{\"user\":\"user\",\"group\":\"group\"}", serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PrincipalTypeAdapter<T extends Principal> extends TypeAdapter<T> {
|
||||||
|
@Override
|
||||||
|
public void write(JsonWriter out, T principal) throws IOException {
|
||||||
|
out.value(principal.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T read(JsonReader in) throws IOException {
|
||||||
|
final String name = in.nextString();
|
||||||
|
// This type adapter is only used for Group and User Principal, both of which are implemented by PrincipalImpl.
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
T principal = (T) new Java17ReflectionHelperTest.PrincipalImpl(name);
|
||||||
|
return principal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -29,12 +29,15 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
@SuppressWarnings("resource")
|
@SuppressWarnings("resource")
|
||||||
public class JsonTreeReaderTest extends TestCase {
|
public class JsonTreeReaderTest extends TestCase {
|
||||||
public void testSkipValue_emptyJsonObject() throws IOException {
|
public void testSkipValue_emptyJsonObject() throws IOException {
|
||||||
JsonTreeReader in = new JsonTreeReader(new JsonObject());
|
JsonTreeReader in = new JsonTreeReader(new JsonObject());
|
||||||
in.skipValue();
|
in.skipValue();
|
||||||
assertEquals(JsonToken.END_DOCUMENT, in.peek());
|
assertEquals(JsonToken.END_DOCUMENT, in.peek());
|
||||||
|
assertEquals("$", in.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSkipValue_filledJsonObject() throws IOException {
|
public void testSkipValue_filledJsonObject() throws IOException {
|
||||||
@ -53,6 +56,46 @@ public class JsonTreeReaderTest extends TestCase {
|
|||||||
JsonTreeReader in = new JsonTreeReader(jsonObject);
|
JsonTreeReader in = new JsonTreeReader(jsonObject);
|
||||||
in.skipValue();
|
in.skipValue();
|
||||||
assertEquals(JsonToken.END_DOCUMENT, in.peek());
|
assertEquals(JsonToken.END_DOCUMENT, in.peek());
|
||||||
|
assertEquals("$", in.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSkipValue_name() throws IOException {
|
||||||
|
JsonObject jsonObject = new JsonObject();
|
||||||
|
jsonObject.addProperty("a", "value");
|
||||||
|
JsonTreeReader in = new JsonTreeReader(jsonObject);
|
||||||
|
in.beginObject();
|
||||||
|
in.skipValue();
|
||||||
|
assertEquals(JsonToken.STRING, in.peek());
|
||||||
|
assertEquals("$.<skipped>", in.getPath());
|
||||||
|
assertEquals("value", in.nextString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSkipValue_afterEndOfDocument() throws IOException {
|
||||||
|
JsonTreeReader reader = new JsonTreeReader(new JsonObject());
|
||||||
|
reader.beginObject();
|
||||||
|
reader.endObject();
|
||||||
|
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
|
||||||
|
|
||||||
|
assertEquals("$", reader.getPath());
|
||||||
|
assertThrows("Attempt to skip led outside the document", IllegalStateException.class, reader::skipValue);
|
||||||
|
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
|
||||||
|
assertEquals("$", reader.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSkipValue_atArrayEnd() throws IOException {
|
||||||
|
JsonTreeReader reader = new JsonTreeReader(new JsonArray());
|
||||||
|
reader.beginArray();
|
||||||
|
assertThrows("Attempt to skip led outside its parent", IllegalStateException.class, reader::skipValue);
|
||||||
|
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
|
||||||
|
assertEquals("$", reader.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSkipValue_atObjectEnd() throws IOException {
|
||||||
|
JsonTreeReader reader = new JsonTreeReader(new JsonObject());
|
||||||
|
reader.beginObject();
|
||||||
|
assertThrows("Attempt to skip led outside its parent", IllegalStateException.class, reader::skipValue);
|
||||||
|
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
|
||||||
|
assertEquals("$", reader.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testHasNext_endOfDocument() throws IOException {
|
public void testHasNext_endOfDocument() throws IOException {
|
||||||
@ -92,7 +135,8 @@ public class JsonTreeReaderTest extends TestCase {
|
|||||||
* {@code JsonReader} must be overridden.
|
* {@code JsonReader} must be overridden.
|
||||||
*/
|
*/
|
||||||
public void testOverrides() {
|
public void testOverrides() {
|
||||||
List<String> ignoredMethods = Arrays.asList("setLenient(boolean)", "isLenient()");
|
List<String> ignoredMethods = Arrays.asList("setLenient(boolean)", "isLenient()",
|
||||||
|
"setSerializeSpecialFloatingPointValues(boolean)", "isSerializeSpecialFloatingPointValues()");
|
||||||
MoreAsserts.assertOverridesMethods(JsonReader.class, JsonTreeReader.class, ignoredMethods);
|
MoreAsserts.assertOverridesMethods(JsonReader.class, JsonTreeReader.class, ignoredMethods);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,8 +256,12 @@ public final class JsonTreeWriterTest extends TestCase {
|
|||||||
* methods of {@code JsonWriter} must be overridden.
|
* methods of {@code JsonWriter} must be overridden.
|
||||||
*/
|
*/
|
||||||
public void testOverrides() {
|
public void testOverrides() {
|
||||||
List<String> ignoredMethods = Arrays.asList("setLenient(boolean)", "isLenient()", "setIndent(java.lang.String)",
|
List<String> ignoredMethods = Arrays.asList("setLenient(boolean)", "isLenient()",
|
||||||
"setHtmlSafe(boolean)", "isHtmlSafe()", "setSerializeNulls(boolean)", "getSerializeNulls()", "setOmitQuotes(boolean)", "getOmitQuotes()");
|
"setSerializeSpecialFloatingPointValues(boolean)", "isSerializeSpecialFloatingPointValues()",
|
||||||
|
"setIndent(java.lang.String)",
|
||||||
|
"setHtmlSafe(boolean)", "isHtmlSafe()",
|
||||||
|
"setSerializeNulls(boolean)", "getSerializeNulls()",
|
||||||
|
"setOmitQuotes(boolean)", "getOmitQuotes()");
|
||||||
MoreAsserts.assertOverridesMethods(JsonWriter.class, JsonTreeWriter.class, ignoredMethods);
|
MoreAsserts.assertOverridesMethods(JsonWriter.class, JsonTreeWriter.class, ignoredMethods);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
package com.google.gson.internal.reflect;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.nio.file.attribute.GroupPrincipal;
|
||||||
|
import java.nio.file.attribute.UserPrincipal;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class Java17ReflectionHelperTest {
|
||||||
|
@Test
|
||||||
|
public void testJava17Record() throws ClassNotFoundException {
|
||||||
|
Class<?> unixDomainPrincipalClass = Class.forName("jdk.net.UnixDomainPrincipal");
|
||||||
|
// UnixDomainPrincipal is a record
|
||||||
|
assertTrue(ReflectionHelper.isRecord(unixDomainPrincipalClass));
|
||||||
|
// with 2 components
|
||||||
|
assertArrayEquals(
|
||||||
|
new String[] {"user", "group"},
|
||||||
|
ReflectionHelper.getRecordComponentNames(unixDomainPrincipalClass));
|
||||||
|
// Check canonical constructor
|
||||||
|
Constructor<?> constructor =
|
||||||
|
ReflectionHelper.getCanonicalRecordConstructor(unixDomainPrincipalClass);
|
||||||
|
assertNotNull(constructor);
|
||||||
|
assertArrayEquals(
|
||||||
|
new Class<?>[] {UserPrincipal.class, GroupPrincipal.class},
|
||||||
|
constructor.getParameterTypes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJava17RecordAccessors() throws ReflectiveOperationException {
|
||||||
|
// Create an instance of UnixDomainPrincipal, using our custom implementation of UserPrincipal,
|
||||||
|
// and GroupPrincipal. Then attempt to access each component of the record using our accessor
|
||||||
|
// methods.
|
||||||
|
Class<?> unixDomainPrincipalClass = Class.forName("jdk.net.UnixDomainPrincipal");
|
||||||
|
Object unixDomainPrincipal =
|
||||||
|
ReflectionHelper.getCanonicalRecordConstructor(unixDomainPrincipalClass)
|
||||||
|
.newInstance(new PrincipalImpl("user"), new PrincipalImpl("group"));
|
||||||
|
|
||||||
|
String[] componentNames = ReflectionHelper.getRecordComponentNames(unixDomainPrincipalClass);
|
||||||
|
assertTrue(componentNames.length > 0);
|
||||||
|
|
||||||
|
for (String componentName : componentNames) {
|
||||||
|
Field componentField = unixDomainPrincipalClass.getDeclaredField(componentName);
|
||||||
|
Method accessor = ReflectionHelper.getAccessor(unixDomainPrincipalClass, componentField);
|
||||||
|
Object principal = accessor.invoke(unixDomainPrincipal);
|
||||||
|
|
||||||
|
assertEquals(new PrincipalImpl(componentName), principal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Implementation of {@link UserPrincipal} and {@link GroupPrincipal} just for record tests. */
|
||||||
|
public static class PrincipalImpl implements UserPrincipal, GroupPrincipal {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public PrincipalImpl(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o instanceof PrincipalImpl) {
|
||||||
|
return Objects.equals(name, ((PrincipalImpl) o).name);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
gson/src/test/java/com/google/gson/jf/ReadNullTest.java
Normal file
24
gson/src/test/java/com/google/gson/jf/ReadNullTest.java
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package com.google.gson.jf;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class ReadNullTest extends TestCase {
|
||||||
|
public void testReadNull() throws IOException {
|
||||||
|
Gson gson = new GsonBuilder().serializeNulls().setLenient().create();
|
||||||
|
String exampleFile = "{\n // Yes\n \"value1\": 1024,\n \"value2\": null,\n \"value3\": 10\n}";
|
||||||
|
try (Reader r = new StringReader(exampleFile); var jr = gson.newJsonReader(r)) {
|
||||||
|
jr.beginObject();
|
||||||
|
assertEquals("value1", jr.nextName());
|
||||||
|
assertEquals(1024, jr.nextInt());
|
||||||
|
assertEquals("value2", jr.nextName());
|
||||||
|
jr.nextNull();
|
||||||
|
assertEquals("value3", jr.nextName());
|
||||||
|
assertEquals(10, jr.nextInt());
|
||||||
|
jr.endObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -162,7 +162,7 @@ public class PerformanceTest extends TestCase {
|
|||||||
* Created in response to http://code.google.com/p/google-gson/issues/detail?id=96
|
* Created in response to http://code.google.com/p/google-gson/issues/detail?id=96
|
||||||
*/
|
*/
|
||||||
// Last I tested, Gson was able to deserialize a byte array of 11MB
|
// Last I tested, Gson was able to deserialize a byte array of 11MB
|
||||||
public void disable_testByteArrayDeserialization() {
|
public void disabled_testByteArrayDeserialization() {
|
||||||
for (int numElements = 10639296; true; numElements += 16384) {
|
for (int numElements = 10639296; true; numElements += 16384) {
|
||||||
StringBuilder sb = new StringBuilder(numElements*2);
|
StringBuilder sb = new StringBuilder(numElements*2);
|
||||||
sb.append("[");
|
sb.append("[");
|
||||||
@ -218,7 +218,7 @@ public class PerformanceTest extends TestCase {
|
|||||||
System.out.printf("Deserialize classes avg time: %d ms\n", avg);
|
System.out.printf("Deserialize classes avg time: %d ms\n", avg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disable_testLargeObjectSerializationAndDeserialization() {
|
public void disabled_testLargeObjectSerializationAndDeserialization() {
|
||||||
Map<String, Long> largeObject = new HashMap<>();
|
Map<String, Long> largeObject = new HashMap<>();
|
||||||
for (long l = 0; l < 100000; l++) {
|
for (long l = 0; l < 100000; l++) {
|
||||||
largeObject.put("field" + l, l);
|
largeObject.put("field" + l, l);
|
||||||
|
@ -16,6 +16,10 @@
|
|||||||
|
|
||||||
package com.google.gson.stream;
|
package com.google.gson.stream;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
import static org.junit.Assume.assumeTrue;
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.internal.Streams;
|
import com.google.gson.internal.Streams;
|
||||||
import com.google.gson.internal.bind.JsonTreeReader;
|
import com.google.gson.internal.bind.JsonTreeReader;
|
||||||
@ -27,9 +31,6 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assume.assumeTrue;
|
|
||||||
|
|
||||||
@SuppressWarnings("resource")
|
@SuppressWarnings("resource")
|
||||||
@RunWith(Parameterized.class)
|
@RunWith(Parameterized.class)
|
||||||
public class JsonReaderPathTest {
|
public class JsonReaderPathTest {
|
||||||
@ -221,12 +222,28 @@ public class JsonReaderPathTest {
|
|||||||
assertEquals("$[2]", reader.getPath());
|
assertEquals("$[2]", reader.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test public void skipArrayEnd() throws IOException {
|
||||||
|
JsonReader reader = factory.create("[[],1]");
|
||||||
|
reader.beginArray();
|
||||||
|
reader.beginArray();
|
||||||
|
assertEquals("$[0][0]", reader.getPreviousPath());
|
||||||
|
assertEquals("$[0][0]", reader.getPath());
|
||||||
|
// skip end of array
|
||||||
|
assertThrows("Attempt to skip led outside its parent", IllegalStateException.class, reader::skipValue);
|
||||||
|
assertEquals("$[0]", reader.getPreviousPath());
|
||||||
|
assertEquals("$[1]", reader.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
@Test public void skipObjectNames() throws IOException {
|
@Test public void skipObjectNames() throws IOException {
|
||||||
JsonReader reader = factory.create("{\"a\":1}");
|
JsonReader reader = factory.create("{\"a\":[]}");
|
||||||
reader.beginObject();
|
reader.beginObject();
|
||||||
reader.skipValue();
|
reader.skipValue();
|
||||||
assertEquals("$.null", reader.getPreviousPath());
|
assertEquals("$.<skipped>", reader.getPreviousPath());
|
||||||
assertEquals("$.null", reader.getPath());
|
assertEquals("$.<skipped>", reader.getPath());
|
||||||
|
|
||||||
|
reader.beginArray();
|
||||||
|
assertEquals("$.<skipped>[0]", reader.getPreviousPath());
|
||||||
|
assertEquals("$.<skipped>[0]", reader.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void skipObjectValues() throws IOException {
|
@Test public void skipObjectValues() throws IOException {
|
||||||
@ -236,13 +253,27 @@ public class JsonReaderPathTest {
|
|||||||
assertEquals("$.", reader.getPath());
|
assertEquals("$.", reader.getPath());
|
||||||
reader.nextName();
|
reader.nextName();
|
||||||
reader.skipValue();
|
reader.skipValue();
|
||||||
assertEquals("$.null", reader.getPreviousPath());
|
assertEquals("$.a", reader.getPreviousPath());
|
||||||
assertEquals("$.null", reader.getPath());
|
assertEquals("$.a", reader.getPath());
|
||||||
reader.nextName();
|
reader.nextName();
|
||||||
assertEquals("$.b", reader.getPreviousPath());
|
assertEquals("$.b", reader.getPreviousPath());
|
||||||
assertEquals("$.b", reader.getPath());
|
assertEquals("$.b", reader.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test public void skipObjectEnd() throws IOException {
|
||||||
|
JsonReader reader = factory.create("{\"a\":{},\"b\":2}");
|
||||||
|
reader.beginObject();
|
||||||
|
reader.nextName();
|
||||||
|
reader.beginObject();
|
||||||
|
assertEquals("$.a.", reader.getPreviousPath());
|
||||||
|
assertEquals("$.a.", reader.getPath());
|
||||||
|
// skip end of object
|
||||||
|
assertEquals(JsonToken.END_OBJECT, reader.peek());
|
||||||
|
assertThrows("Attempt to skip led outside its parent", IllegalStateException.class, reader::skipValue);
|
||||||
|
assertEquals("$.a", reader.getPreviousPath());
|
||||||
|
assertEquals("$.a", reader.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
@Test public void skipNestedStructures() throws IOException {
|
@Test public void skipNestedStructures() throws IOException {
|
||||||
JsonReader reader = factory.create("[[1,2,3],4]");
|
JsonReader reader = factory.create("[[1,2,3],4]");
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
@ -251,6 +282,17 @@ public class JsonReaderPathTest {
|
|||||||
assertEquals("$[1]", reader.getPath());
|
assertEquals("$[1]", reader.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test public void skipEndOfDocument() throws IOException {
|
||||||
|
JsonReader reader = factory.create("[]");
|
||||||
|
reader.beginArray();
|
||||||
|
reader.endArray();
|
||||||
|
assertEquals("$", reader.getPreviousPath());
|
||||||
|
assertEquals("$", reader.getPath());
|
||||||
|
assertThrows("Attempt to skip led outside the document", IllegalStateException.class, reader::skipValue);
|
||||||
|
assertEquals("$", reader.getPreviousPath());
|
||||||
|
assertEquals("$", reader.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
@Test public void arrayOfObjects() throws IOException {
|
@Test public void arrayOfObjects() throws IOException {
|
||||||
JsonReader reader = factory.create("[{},{},{}]");
|
JsonReader reader = factory.create("[{},{},{}]");
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
@ -307,6 +349,52 @@ public class JsonReaderPathTest {
|
|||||||
assertEquals("$", reader.getPath());
|
assertEquals("$", reader.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test public void objectOfObjects() throws IOException {
|
||||||
|
JsonReader reader = factory.create("{\"a\":{\"a1\":1,\"a2\":2},\"b\":{\"b1\":1}}");
|
||||||
|
reader.beginObject();
|
||||||
|
assertEquals("$.", reader.getPreviousPath());
|
||||||
|
assertEquals("$.", reader.getPath());
|
||||||
|
reader.nextName();
|
||||||
|
assertEquals("$.a", reader.getPreviousPath());
|
||||||
|
assertEquals("$.a", reader.getPath());
|
||||||
|
reader.beginObject();
|
||||||
|
assertEquals("$.a.", reader.getPreviousPath());
|
||||||
|
assertEquals("$.a.", reader.getPath());
|
||||||
|
reader.nextName();
|
||||||
|
assertEquals("$.a.a1", reader.getPreviousPath());
|
||||||
|
assertEquals("$.a.a1", reader.getPath());
|
||||||
|
reader.nextInt();
|
||||||
|
assertEquals("$.a.a1", reader.getPreviousPath());
|
||||||
|
assertEquals("$.a.a1", reader.getPath());
|
||||||
|
reader.nextName();
|
||||||
|
assertEquals("$.a.a2", reader.getPreviousPath());
|
||||||
|
assertEquals("$.a.a2", reader.getPath());
|
||||||
|
reader.nextInt();
|
||||||
|
assertEquals("$.a.a2", reader.getPreviousPath());
|
||||||
|
assertEquals("$.a.a2", reader.getPath());
|
||||||
|
reader.endObject();
|
||||||
|
assertEquals("$.a", reader.getPreviousPath());
|
||||||
|
assertEquals("$.a", reader.getPath());
|
||||||
|
reader.nextName();
|
||||||
|
assertEquals("$.b", reader.getPreviousPath());
|
||||||
|
assertEquals("$.b", reader.getPath());
|
||||||
|
reader.beginObject();
|
||||||
|
assertEquals("$.b.", reader.getPreviousPath());
|
||||||
|
assertEquals("$.b.", reader.getPath());
|
||||||
|
reader.nextName();
|
||||||
|
assertEquals("$.b.b1", reader.getPreviousPath());
|
||||||
|
assertEquals("$.b.b1", reader.getPath());
|
||||||
|
reader.nextInt();
|
||||||
|
assertEquals("$.b.b1", reader.getPreviousPath());
|
||||||
|
assertEquals("$.b.b1", reader.getPath());
|
||||||
|
reader.endObject();
|
||||||
|
assertEquals("$.b", reader.getPreviousPath());
|
||||||
|
assertEquals("$.b", reader.getPath());
|
||||||
|
reader.endObject();
|
||||||
|
assertEquals("$", reader.getPreviousPath());
|
||||||
|
assertEquals("$", reader.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
public enum Factory {
|
public enum Factory {
|
||||||
STRING_READER {
|
STRING_READER {
|
||||||
@Override public JsonReader create(String data) {
|
@Override public JsonReader create(String data) {
|
||||||
|
@ -16,13 +16,6 @@
|
|||||||
|
|
||||||
package com.google.gson.stream;
|
package com.google.gson.stream;
|
||||||
|
|
||||||
import java.io.EOFException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
import static com.google.gson.stream.JsonToken.BEGIN_ARRAY;
|
import static com.google.gson.stream.JsonToken.BEGIN_ARRAY;
|
||||||
import static com.google.gson.stream.JsonToken.BEGIN_OBJECT;
|
import static com.google.gson.stream.JsonToken.BEGIN_OBJECT;
|
||||||
import static com.google.gson.stream.JsonToken.BOOLEAN;
|
import static com.google.gson.stream.JsonToken.BOOLEAN;
|
||||||
@ -32,6 +25,14 @@ import static com.google.gson.stream.JsonToken.NAME;
|
|||||||
import static com.google.gson.stream.JsonToken.NULL;
|
import static com.google.gson.stream.JsonToken.NULL;
|
||||||
import static com.google.gson.stream.JsonToken.NUMBER;
|
import static com.google.gson.stream.JsonToken.NUMBER;
|
||||||
import static com.google.gson.stream.JsonToken.STRING;
|
import static com.google.gson.stream.JsonToken.STRING;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
@SuppressWarnings("resource")
|
@SuppressWarnings("resource")
|
||||||
public final class JsonReaderTest extends TestCase {
|
public final class JsonReaderTest extends TestCase {
|
||||||
@ -140,6 +141,35 @@ public final class JsonReaderTest extends TestCase {
|
|||||||
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
|
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testSkipObjectName() throws IOException {
|
||||||
|
JsonReader reader = new JsonReader(reader("{\"a\": 1}"));
|
||||||
|
reader.beginObject();
|
||||||
|
reader.skipValue();
|
||||||
|
assertEquals(JsonToken.NUMBER, reader.peek());
|
||||||
|
assertEquals("$.<skipped>", reader.getPath());
|
||||||
|
assertEquals(1, reader.nextInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSkipObjectNameSingleQuoted() throws IOException {
|
||||||
|
JsonReader reader = new JsonReader(reader("{'a': 1}"));
|
||||||
|
reader.setLenient(true);
|
||||||
|
reader.beginObject();
|
||||||
|
reader.skipValue();
|
||||||
|
assertEquals(JsonToken.NUMBER, reader.peek());
|
||||||
|
assertEquals("$.<skipped>", reader.getPath());
|
||||||
|
assertEquals(1, reader.nextInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSkipObjectNameUnquoted() throws IOException {
|
||||||
|
JsonReader reader = new JsonReader(reader("{a: 1}"));
|
||||||
|
reader.setLenient(true);
|
||||||
|
reader.beginObject();
|
||||||
|
reader.skipValue();
|
||||||
|
assertEquals(JsonToken.NUMBER, reader.peek());
|
||||||
|
assertEquals("$.<skipped>", reader.getPath());
|
||||||
|
assertEquals(1, reader.nextInt());
|
||||||
|
}
|
||||||
|
|
||||||
public void testSkipInteger() throws IOException {
|
public void testSkipInteger() throws IOException {
|
||||||
JsonReader reader = new JsonReader(reader(
|
JsonReader reader = new JsonReader(reader(
|
||||||
"{\"a\":123456789,\"b\":-123456789}"));
|
"{\"a\":123456789,\"b\":-123456789}"));
|
||||||
@ -164,6 +194,34 @@ public final class JsonReaderTest extends TestCase {
|
|||||||
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
|
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testSkipValueAfterEndOfDocument() throws IOException {
|
||||||
|
JsonReader reader = new JsonReader(reader("{}"));
|
||||||
|
reader.beginObject();
|
||||||
|
reader.endObject();
|
||||||
|
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
|
||||||
|
|
||||||
|
assertEquals("$", reader.getPath());
|
||||||
|
assertThrows("Attempt to skip led outside the document", IllegalStateException.class, reader::skipValue);
|
||||||
|
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
|
||||||
|
assertEquals("$", reader.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSkipValueAtArrayEnd() throws IOException {
|
||||||
|
JsonReader reader = new JsonReader(reader("[]"));
|
||||||
|
reader.beginArray();
|
||||||
|
assertThrows("Attempt to skip led outside its parent", IllegalStateException.class, reader::skipValue);
|
||||||
|
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
|
||||||
|
assertEquals("$", reader.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSkipValueAtObjectEnd() throws IOException {
|
||||||
|
JsonReader reader = new JsonReader(reader("{}"));
|
||||||
|
reader.beginObject();
|
||||||
|
assertThrows("Attempt to skip led outside its parent", IllegalStateException.class, reader::skipValue);
|
||||||
|
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
|
||||||
|
assertEquals("$", reader.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
public void testHelloWorld() throws IOException {
|
public void testHelloWorld() throws IOException {
|
||||||
String json = "{\n" +
|
String json = "{\n" +
|
||||||
" \"hello\": true,\n" +
|
" \"hello\": true,\n" +
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>io.gitlab.jfronny</groupId>
|
<groupId>io.gitlab.jfronny</groupId>
|
||||||
<artifactId>gson-parent</artifactId>
|
<artifactId>gson-parent</artifactId>
|
||||||
<version>2.9.2-SNAPSHOT</version>
|
<version>2.11-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>gson-metrics</artifactId>
|
<artifactId>gson-metrics</artifactId>
|
||||||
@ -32,7 +32,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>jackson-databind</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
<version>2.13.4</version>
|
<version>2.13.4.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.caliper</groupId>
|
<groupId>com.google.caliper</groupId>
|
||||||
|
@ -33,7 +33,8 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class CollectionsDeserializationBenchmark {
|
public class CollectionsDeserializationBenchmark {
|
||||||
|
|
||||||
private static final Type LIST_TYPE = new TypeToken<List<BagOfPrimitives>>(){}.getType();
|
private static final TypeToken<List<BagOfPrimitives>> LIST_TYPE_TOKEN = new TypeToken<List<BagOfPrimitives>>(){};
|
||||||
|
private static final Type LIST_TYPE = LIST_TYPE_TOKEN.getType();
|
||||||
private Gson gson;
|
private Gson gson;
|
||||||
private String json;
|
private String json;
|
||||||
|
|
||||||
@ -56,7 +57,7 @@ public class CollectionsDeserializationBenchmark {
|
|||||||
*/
|
*/
|
||||||
public void timeCollectionsDefault(int reps) {
|
public void timeCollectionsDefault(int reps) {
|
||||||
for (int i=0; i<reps; ++i) {
|
for (int i=0; i<reps; ++i) {
|
||||||
gson.fromJson(json, LIST_TYPE);
|
gson.fromJson(json, LIST_TYPE_TOKEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,11 +63,11 @@ public final class ParseBenchmark {
|
|||||||
READER_SHORT(new TypeToken<Feed>() {}, new TypeReference<Feed>() {}),
|
READER_SHORT(new TypeToken<Feed>() {}, new TypeReference<Feed>() {}),
|
||||||
READER_LONG(new TypeToken<Feed>() {}, new TypeReference<Feed>() {});
|
READER_LONG(new TypeToken<Feed>() {}, new TypeReference<Feed>() {});
|
||||||
|
|
||||||
private final Type gsonType;
|
private final TypeToken<?> gsonType;
|
||||||
private final TypeReference<?> jacksonType;
|
private final TypeReference<?> jacksonType;
|
||||||
|
|
||||||
private Document(TypeToken<?> typeToken, TypeReference<?> typeReference) {
|
private Document(TypeToken<?> typeToken, TypeReference<?> typeReference) {
|
||||||
this.gsonType = typeToken.getType();
|
this.gsonType = typeToken;
|
||||||
this.jacksonType = typeReference;
|
this.jacksonType = typeReference;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
91
pom.xml
91
pom.xml
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<groupId>io.gitlab.jfronny</groupId>
|
<groupId>io.gitlab.jfronny</groupId>
|
||||||
<artifactId>gson-parent</artifactId>
|
<artifactId>gson-parent</artifactId>
|
||||||
<version>2.9.2-SNAPSHOT</version>
|
<version>2.11-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<name>Gson Parent</name>
|
<name>Gson Parent</name>
|
||||||
@ -31,6 +31,14 @@
|
|||||||
<tag>HEAD</tag>
|
<tag>HEAD</tag>
|
||||||
</scm>
|
</scm>
|
||||||
|
|
||||||
|
<developers>
|
||||||
|
<developer>
|
||||||
|
<id>google</id>
|
||||||
|
<organization>Google</organization>
|
||||||
|
<organizationUrl>https://www.google.com</organizationUrl>
|
||||||
|
</developer>
|
||||||
|
</developers>
|
||||||
|
|
||||||
<issueManagement>
|
<issueManagement>
|
||||||
<system>GitHub Issues</system>
|
<system>GitHub Issues</system>
|
||||||
<url>https://github.com/google/gson/issues</url>
|
<url>https://github.com/google/gson/issues</url>
|
||||||
@ -79,7 +87,6 @@
|
|||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.10.1</version>
|
<version>3.10.1</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<release>${javaVersion}</release>
|
|
||||||
<showWarnings>true</showWarnings>
|
<showWarnings>true</showWarnings>
|
||||||
<showDeprecation>true</showDeprecation>
|
<showDeprecation>true</showDeprecation>
|
||||||
<failOnWarning>false</failOnWarning>
|
<failOnWarning>false</failOnWarning>
|
||||||
@ -101,6 +108,9 @@
|
|||||||
<jdkToolchain>
|
<jdkToolchain>
|
||||||
<version>[11,)</version>
|
<version>[11,)</version>
|
||||||
</jdkToolchain>
|
</jdkToolchain>
|
||||||
|
<!-- Specify newer JDK as target to allow linking to newer Java API, and to generate
|
||||||
|
module overview in Javadoc for Gson's module descriptor -->
|
||||||
|
<release>11</release>
|
||||||
<!-- Exclude `missing` group because some tags have been omitted when they are redundant -->
|
<!-- Exclude `missing` group because some tags have been omitted when they are redundant -->
|
||||||
<doclint>all,-missing</doclint>
|
<doclint>all,-missing</doclint>
|
||||||
<!-- Link against newer Java API Javadoc because most users likely
|
<!-- Link against newer Java API Javadoc because most users likely
|
||||||
@ -122,7 +132,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
<version>3.2.2</version>
|
<version>3.3.0</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
@ -143,8 +153,83 @@
|
|||||||
<!-- Disable Maven Super POM release profile and instead use own one -->
|
<!-- Disable Maven Super POM release profile and instead use own one -->
|
||||||
<useReleaseProfile>false</useReleaseProfile>
|
<useReleaseProfile>false</useReleaseProfile>
|
||||||
<releaseProfiles>release</releaseProfiles>
|
<releaseProfiles>release</releaseProfiles>
|
||||||
|
<!-- Run custom goals to replace version references, see plugin configuration below -->
|
||||||
|
<!-- Also run `verify` to make sure tests still pass with new version number;
|
||||||
|
also seems to be necessary because without `package`, goals fail for modules depending
|
||||||
|
on each other; possibly same issue as https://issues.apache.org/jira/browse/MRELEASE-271 -->
|
||||||
|
<preparationGoals>
|
||||||
|
clean verify
|
||||||
|
antrun:run@replace-version-placeholders
|
||||||
|
antrun:run@replace-old-version-references
|
||||||
|
antrun:run@git-add-changed
|
||||||
|
</preparationGoals>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-antrun-plugin</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<executions>
|
||||||
|
<!-- Replaces version placeholders with the current version; this is mainly useful for
|
||||||
|
Javadoc where this allows writing `@since $next-version$` -->
|
||||||
|
<execution>
|
||||||
|
<id>replace-version-placeholders</id>
|
||||||
|
<goals>
|
||||||
|
<goal>run</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<target>
|
||||||
|
<replace token="$next-version$" value="${project.version}" encoding="${project.build.sourceEncoding}">
|
||||||
|
<!-- erroronmissingdir=false for gson-parent which does not have source directory -->
|
||||||
|
<fileset dir="${project.build.sourceDirectory}" includes="**" erroronmissingdir="false" />
|
||||||
|
</replace>
|
||||||
|
</target>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<!-- Replaces references to the old version in the documentation -->
|
||||||
|
<execution>
|
||||||
|
<id>replace-old-version-references</id>
|
||||||
|
<goals>
|
||||||
|
<goal>run</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<target>
|
||||||
|
<!-- Replace Maven and Gradle version references; uses regex lookbehind and lookahead -->
|
||||||
|
<replaceregexp match="(?<=<version>).*(?=</version>)|(?<='com\.google\.code\.gson:gson:).*(?=')" flags="g" replace="${project.version}" encoding="${project.build.sourceEncoding}">
|
||||||
|
<fileset dir="${project.basedir}">
|
||||||
|
<include name="README.md" />
|
||||||
|
<include name="UserGuide.md" />
|
||||||
|
</fileset>
|
||||||
|
</replaceregexp>
|
||||||
|
</target>
|
||||||
|
</configuration>
|
||||||
|
<!-- Only has to be executed for parent project; don't inherit this to modules -->
|
||||||
|
<!-- This might be a bit hacky; execution with this ID seems to be missing for modules and Maven just executes default
|
||||||
|
configuration which does not have any targets configured. (not sure if this behavior is guaranteed) -->
|
||||||
|
<inherited>false</inherited>
|
||||||
|
</execution>
|
||||||
|
<!-- Adds changed files to the Git index; workaround because Maven Release Plugin does not support committing
|
||||||
|
additional files yet (https://issues.apache.org/jira/browse/MRELEASE-798), and for workarounds with
|
||||||
|
Maven SCM Plugin it is apparently necessary to know modified files in advance -->
|
||||||
|
<!-- Maven Release Plugin then just happens to include these changed files in its Git commit;
|
||||||
|
not sure if this behavior is guaranteed or if this relies on implementation details -->
|
||||||
|
<execution>
|
||||||
|
<id>git-add-changed</id>
|
||||||
|
<goals>
|
||||||
|
<goal>run</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<target>
|
||||||
|
<exec executable="git" dir="${project.basedir}" failonerror="true">
|
||||||
|
<arg value="add" />
|
||||||
|
<!-- Don't add (unrelated) not yet tracked files -->
|
||||||
|
<arg value="--update" />
|
||||||
|
<arg value="." />
|
||||||
|
</exec>
|
||||||
|
</target>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
<!-- Plugin for checking source and binary compatibility; used by GitHub workflow -->
|
<!-- Plugin for checking source and binary compatibility; used by GitHub workflow -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>com.github.siom79.japicmp</groupId>
|
<groupId>com.github.siom79.japicmp</groupId>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>io.gitlab.jfronny</groupId>
|
<groupId>io.gitlab.jfronny</groupId>
|
||||||
<artifactId>gson-parent</artifactId>
|
<artifactId>gson-parent</artifactId>
|
||||||
<version>2.9.2-SNAPSHOT</version>
|
<version>2.11-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>proto</artifactId>
|
<artifactId>proto</artifactId>
|
||||||
|
@ -64,7 +64,6 @@ import java.util.concurrent.ConcurrentMap;
|
|||||||
* string os_build_id = 1 [(serialized_name) = "osBuildID"];
|
* string os_build_id = 1 [(serialized_name) = "osBuildID"];
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
* <p>
|
|
||||||
*
|
*
|
||||||
* @author Inderjeet Singh
|
* @author Inderjeet Singh
|
||||||
* @author Emmanuel Cron
|
* @author Emmanuel Cron
|
||||||
|
Loading…
Reference in New Issue
Block a user