Repository: serj-lotutovici/moshi-lazy-adapters
Branch: master
Commit: 5f2d890c5c5d
Files: 63
Total size: 177.5 KB
Directory structure:
gitextract_k8ld05jz/
├── .buildscript/
│ └── deploy_snapshot.sh
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── build.gradle
├── checkstyle.gradle
├── config/
│ └── checkstyle/
│ └── checkstyle.xml
├── dependencies.gradle
├── gradle/
│ ├── gradle-mvn-push.gradle
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── jacoco.gradle
├── settings.gradle
└── src/
├── integrationTest/
│ └── java/
│ └── com/
│ └── serjltt/
│ └── moshi/
│ └── adapters/
│ ├── Data.java
│ ├── DataFactories.java
│ ├── DeserializeOnlyAutoValueTest.java
│ ├── LazyAdaptersAutoValueTest.java
│ ├── LazyAdaptersRetrofitTest.java
│ ├── LazyAdaptersRxJavaTest.java
│ ├── Nullable.java
│ ├── SerializeOnlyAutoValueTest.java
│ └── TransientAutoValueTest.java
├── main/
│ └── java/
│ └── com/
│ └── serjltt/
│ └── moshi/
│ └── adapters/
│ ├── DefaultOnDataMismatchAdapter.java
│ ├── DeserializeOnly.java
│ ├── ElementAt.java
│ ├── ElementAtJsonAdapter.java
│ ├── FallbackEnum.java
│ ├── FallbackEnumJsonAdapter.java
│ ├── FallbackOnNull.java
│ ├── FallbackOnNullJsonAdapter.java
│ ├── FilterNulls.java
│ ├── FilterNullsJsonAdapter.java
│ ├── FirstElement.java
│ ├── LastElement.java
│ ├── LastElementJsonAdapter.java
│ ├── Pair.java
│ ├── SerializeNulls.java
│ ├── SerializeOnly.java
│ ├── SerializeOnlyNonEmpty.java
│ ├── SerializeOnlyNonEmptyJsonAdapter.java
│ ├── Transient.java
│ ├── TransientJsonAdapter.java
│ ├── Util.java
│ ├── Wrapped.java
│ └── WrappedJsonAdapter.java
└── unitTest/
└── java/
└── com/
└── serjltt/
└── moshi/
└── adapters/
├── Custom.java
├── DefaultOnDataMismatchAdapterTest.java
├── DeserializeOnlyJsonAdapterTest.java
├── ElementAtJsonAdapterTest.java
├── FallbackEnumJsonAdapterTest.java
├── FallbackOnNullJsonAdapterTest.java
├── FilterNullsJsonAdapterTest.java
├── FirstElementJsonAdapterTest.java
├── LastElementJsonAdapterTest.java
├── SerializeNullsJsonAdapterTest.java
├── SerializeOnlyJsonAdapterTest.java
├── SerializeOnlyNonEmptyJsonAdapterTest.java
└── WrappedJsonAdapterTest.java
================================================
FILE CONTENTS
================================================
================================================
FILE: .buildscript/deploy_snapshot.sh
================================================
#!/bin/bash
#
# Deploy a jar, source jar, and javadoc jar to Sonatype's snapshot repo.
#
# Adapted from https://coderwall.com/p/9b_lfq and
# http://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/
SLUG="serj-lotutovici/moshi-lazy-adapters"
BRANCH="master"
set -e
if [ "$TRAVIS_REPO_SLUG" != "$SLUG" ]; then
echo "Skipping snapshot deployment: wrong repository. Expected '$SLUG' but was '$TRAVIS_REPO_SLUG'."
elif [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
echo "Skipping snapshot deployment: was pull request."
elif [ "$TRAVIS_BRANCH" != "$BRANCH" ]; then
echo "Skipping snapshot deployment: wrong branch. Expected '$BRANCH' but was '$TRAVIS_BRANCH'."
else
echo "Deploying snapshot..."
./gradlew clean uploadArchives -PnexusUsername="$TRAVIS_USER_NAME" -PnexusPassword="$TRAVIS_PASSWORD"
echo "Snapshot deployed!"
fi
================================================
FILE: .gitignore
================================================
# Gradle junk
.gradle/
build/
local.properties
# Idea junk
.idea/
*.iml
out/
# MacOS junk
.DS_Store
================================================
FILE: .travis.yml
================================================
language: java
jdk:
- oraclejdk8
script:
- ./gradlew clean check --stacktrace
after_success:
- bash <(curl -s https://codecov.io/bash)
- .buildscript/deploy_snapshot.sh
env:
global:
- secure: "PD2ZaZxyrpLDq8dsC+JdfkGg7kEPgmhBzqO+zr+ONBuVe+YDLqsTAUF6J6zXoMF6mgBvkalW/v1nRbDvKJBXhGJdrIqPoQ2yhc7nPH1shzUK6WFptsRsPk2+B45M+FmAjlsSHqCjXIcPmRKykCjgjFyR6DVsik2mPA/C8dINwM2LUsD6eKsed79VHjhrpmqBfIKsvSY1saGfqlGmNzYOFHJ8wHfgjBVUlJmzJzWM49gudW8J27zphP59GjAR1/ZHeYAhE+KQEMUfMKtpFUjZiWvE5kNd+4xgUfXh9mRpwA/RuCJhipgzMTBPxHF4WHHC4pIQDJ5/KeF++L6xzweuBheGvOewWvjSWnPpBsEk3YK6X0SI52OcAApsk1KX+8h58ABU/85DObFpxLsAYTNnePWc6f4a1boV/K2f/RTZBp9U15h9z7474BXIZ7t/IFCZgPZp+v5a15CJrjBq/8g1Yi7f0KGj6ax5vctRaQK5pvIasT1Z0bZLWoRO8WWOkatymkUhBT/aPiVm3PbZeqLgp+tljiSzCF6oF7rQDjbCHsNOAr9Y1+SS/bIu0sLXCgQ4Ji4HGDYpub3v6vRWMW5sv23LR9YsgVXXaehZR1k4hScqbscu6/FfYy5N/2h8IeABNZW3mYnR6zmk1VM4uzHcMkD9iJAc07tPsmrxd4aOaNQ="
- secure: "fiI2ZFtZ1LRVcZizbbiHCiKrtKkcyzAPZse8quvuB3VumnfqhPTT1hyyRkV3vXP6R6oigdZG67pG2yGvMovHfyMfQfOfTyfyQw/i7xJTTgOkH2NmeTA+SPn/VnZjZPSwyvkDXgxOOjyoa3G7eSTpblzh13247tZppA4ERKyUUmHPDBVL0kktkabQjRltu6vuUT1vLh0j5Lg/A5HRWF22Bkt0NqrmkYbJaDaW40w/f62OonXS8g4LIKolmHFIfhyoMG4Pj1gUAPUl+0mJ6d/CkCYxnRmWSzTvodsYCp+y06e9MXLL95A+2wg/5oUrwOPigwMlWDp0rWgceU9X0wWK6+7I7nWCJb/+Hr+okL/UE62BxnDPWecyvXRsGbXHAx/VXv2dP3f4qi9GMn2NnpgswQZUdFLfBzZTghT9UftaLeUej6rWrnnlEJLr78ZyYPvXVtwXm0oAWH0iA39Nd6OCmV+YIqgnptUzWugHKCbR4/bgs02HvuIfw+bRGEYUshjD3gSZYNYKcuV8TYmFOnQqRXaYm4yosxwX/t6Zp1mH+s6N98OEEYHIwyaQ8hxBKrpAWmVYr0Nzu7rfITNwLkTB2LBHlv3K2zYP8vz8mz3gm5Y6VFEUqbnHLIfLDWw8jy0Q4oZSyzytgrKeQTeI0erku2uOJFzRce6bHmEjRfOLwDM="
branches:
except:
- gh-pages
notifications:
email: false
cache:
directories:
- $HOME/.gradle
================================================
FILE: CHANGELOG.md
================================================
Change Log
===
2.2 *(02-05-2018)*
---
* Usage selectString in FallbackEnumJsonAdapter. (#64)
* Fix FallbackOnNull naming bug with implicit user locale. (#66)
2.1 *(11-10-2017)*
---
* Fix: Forces WrappedJsonAdapter to re-throw all exceptions. (#60)
2.0 *(06-06-2017)*
---
* New: Move all adapter factories to their respective annotations. (#47)
* New: Added `DefaultOnDataMismatchAdapter`. (#54)
* New: Added `@FilterNulls` annotation and its respective adapter. (#52)
* New: Added `@LastElement` annotation plus adapter (#50)
* New: Added `@Transient` annotation and it's respective adapter. (#49)
* New: Makes `JsonAdapter` for `@Wrapped` more strict. (#45)
* Enhancement: Rely on moshi's resialize nulls functionality. (#46)
* Enhancement: Use moshi's Types.nextAnnotations() where possible. (#44)
* New: Upgrade to Moshi 1.5.0.
```
compile 'com.squareup.moshi:moshi:1.5.0'
```
1.3 *(04-01-2017)*
---
* New: Added `SerializeOnlyNonEmpty` for all collections and arrays.
* New: Allow `WrappedJsonAdapter` to fail on un-found value.
* New: Upgrade to Moshi 1.3.0.
```
compile 'com.squareup.moshi:moshi:1.3.0'
```
1.2 *(08-11-2016)*
---
* New: `@FallbackEnum` annotation and it's respective adapter.
* New: `@ElementAt` (similar to `@FirstElement` but more powerful) and it's respective adapter.
* Fix: `@SerializeNulls` adapter now maintains previous writer setting.
1.1 *(17-10-2016)*
---
* New: Rename `@UnwrapJson` to `@Wrapped` annotation and it's respective adapter to `WrappedJsonAdapter`.
* New: Allow manual creation of `@Wrapped` annotation via `Wrapped.Factory`.
* New: `@SerializeOnly` & `@DeserializeOnly` annotations and their respective adapters.
* New: Upgrade to Moshi 1.3.0.
```
compile 'com.squareup.moshi:moshi:1.3.0'
```
* Fix: Restrict `@Target` for each declared annotation.
1.0 *(14-09-2016)*
---
* Initial release.
* Introduce few handy adapters:
* **SerializeNullsJsonAdapter** - Instructs moshi to serialize a value even if it's `null`;
* **FirstElementJsonAdapter** - Instructs moshi to retrieve only the first element of a list;
* **UnwrapJsonAdapter** - Unwraps a json object under the specified path;
* **FallbackOnNullJsonAdapter** - Instructs moshi to fallback to a default value in case the json field is `null`.
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2016 Serj Lotutovici
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.
================================================
FILE: README.md
================================================
Moshi Lazy Adapters
===
[![Build Status][travis.svg]][travis]
[![codecov][codecov.svg]][codecov]
[![Latest Build][latestbuild.svg]][latestbuild]
[![Android Arsenal][arsenal.svd]][arsenal]
A collection of simple JsonAdapters for [Moshi][moshi].
This library acts as an extension to Moshi by providing general purpose, yet useful `JsonAdapter`s
that are not present in the main library. Most provided adapters are linked via specialized
`JsonQualifier` annotations that alter serialization/deserialization strategies.
##### How To Use It
This library is **not** forcing any of it's own adapters by default. To leverage from any of
the provided annotations/adapters add their respective factory to your `Moshi.Builder`:
```java
// Creates a Moshi instance with an adapter that will handle the @Wrapped annotations.
Moshi moshi = new Moshi.Builder()
.add(Wrapped.ADAPTER_FACTORY)
.build();
```
##### Overall contract
Every declared annotation/adapter in this library (unless specified in the class's documentation)
supports adapter composition. Which means that no `JsonAdapter.Factory` will short circuit adapter
construction on it's own annotation, and instead will delegate to the next adapter returned by
`Moshi`. Meaning that given the following type declaration:
```java
interface WebService {
@GET("/data")
@Wrapped(path={"result", "data"}) @FirstElement MyData getData();
}
```
The resulting `JsonAdapter` will unwrap a list of `MyData` from the json response and return only
the first element of that list.
One **important** concept to keep in mind, that the order of declared annotations in the example
above **does not have any influence** on the way how the final adapter will be constructed.
Instead **the order** of the `JsonAdapter.Factory`'s added to the `Moshi` instance is what plays
a major role in overall behavior. Meaning that in order for the example above to satisfy the
expected result, one must add the factories in the following order:
```
Moshi moshi = new Moshi.Builder()
.add(Wrapped.ADAPTER_FACTORY)
.add(FirstElement.ADAPTER_FACTORY)
.build()
```
Some Lazy Adapters
---
### `@Wrapped`
Some apis enjoy wrapping the response object inside other json objects. This creates a lot of inconvenience
when it comes to consuming the request. The following example contains a list of a users
favorite pokemon which is wrapped behind two keys:
```json
{
"favorite_pokemon": {
"pokemons": [
"Snorlax",
"Pikachu",
"Bulbasaur",
"Charmander",
"Squirtle"
]
}
}
```
In the end the consumer is just interested in the names of the pokemon, but the json object forces to create
a wrapping object, which will contain the list:
```java
class FavoritePokemonResponse {
FavoritePokemon favorite_pokemon;
}
class FavoritePokemon {
List<String> pokemons;
}
```
A custom adapter would be another option, and `WrappedJsonAdapter` is just the one. By annotating
the response type with `@Wrapped` and providing the path to the desired list, the need for
an additional object is dropped:
```java
// This assumes that Retrofit is used to obtain the response.
interface PokemonService {
@GET("/pokemon/favorite")
@Wrapped({"favorite_pokemon", "pokemons"}) Call<List<String>> getFavorite();
}
```
No need for a new class, which results in less code and less methods generated by the consumer code.
You can also annotate any field in your response entity and the same rules will apply.
### `@FallbackOnNull`
Primitives are simple and safe. Primitives have also a smaller memory footprint. Some apis may return
`null` for values that are normally processed as primitives. A safe alternative would be to use
their boxed counterparts, but that would result in redundant boxing and unboxing. By annotating
any primitive field with `@FallbackOnNull` the consumer can specify a default value for the field,
in case it's json representation is null.
```json
[
{
"name": "Pikachu",
"number_of_wins": 1
},
{
"name": "Magikarp",
"number_of_wins": null
}
]
```
The json above contains a list of pokemon of a user with their names and the number of wins the
respective pokemon has obtained during it's trainings of fights. Notice that the 'Magikarp' pokemon
has `null` wins. Normally the representing POJO would declare the filed as `Integer`, but #perfmatters.
With `@FallbackOnNull` the `Pokemon` object can be declared as:
```java
class Pokemon {
String name;
@FallbackOnNull int number_of_wins;
}
```
If the incoming value would be `null` the `number_of_wins` field would default to `Integer.MIN_VALUE`,
this can be altered by providing an alternative fallback:
```java
@FallbackOnNull(fallbackInt = -1) int number_of_wins;
```
See [FallbackOnNull's documentation](../master/src/main/java/com/serjltt/moshi/adapters/FallbackOnNull.java)
for a full reference.
List of provided Adapters
---
* **DefaultOnDataMismatchAdapter** - Allows the consumer to provided a default fallback value for any type.
* **SerializeNulls (annotation)** - Serializes a value even if it's `null`;
* **FirstElement (annotation)** - Deserializes only the first element of a list.
* **LastElement (annotation)** - Deserializes only the last element of a list.
* **ElementAt (annotation)** - Deserializes an element from a specified position of a list.
* **FallbackOnNull (annotation)** - Fallbacks to a default value in case the json field is `null`.
* **FallbackEnum (annotation)** - Fallbacks to a default enum value if the parsed value can not be matched to an existing one.
* **Wrapped (annotation)** - Unwraps a json object under the specified path when parsing, and wraps it when serializing to json.
* **SerializeOnly (annotation)** - Only serializes the annotated field, and ignores it during deserialization.
* **DeserializeOnly (annotation)** - Only deserializes the annotated field, and ignores it during serialization.
* **Transient (annotation)** - (Targets methods only) indicates that a field should be ignored for serialization/deserialization.
* **SerializeOnlyNonEmpty (annotation)** - Will serialize a collection or array only if it contains at-least one value.
Download
---
Download [the latest JAR][dl] or depend via Maven:
```xml
<dependency>
<groupId>com.serjltt.moshi</groupId>
<artifactId>moshi-lazy-adapters</artifactId>
<version>x.y</version>
</dependency>
```
or Gradle:
```groovy
compile 'com.serjltt.moshi:moshi-lazy-adapters:x.y'
```
Snapshots of the development version are available in [Sonatype's `snapshots` repository][sonatype].
License
===
Copyright 2017 Serj Lotutovici
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.
[moshi]: https://github.com/square/moshi
[travis]: https://travis-ci.org/serj-lotutovici/moshi-lazy-adapters
[travis.svg]: https://travis-ci.org/serj-lotutovici/moshi-lazy-adapters.svg?branch=master
[codecov]: https://codecov.io/gh/serj-lotutovici/moshi-lazy-adapters
[codecov.svg]: https://codecov.io/gh/serj-lotutovici/moshi-lazy-adapters/branch/master/graph/badge.svg
[latestbuild]: http://search.maven.org/#search%7Cga%7C1%7Ccom.serjltt.moshi
[latestbuild.svg]: https://img.shields.io/maven-central/v/com.serjltt.moshi/moshi-lazy-adapters.svg
[arsenal.svd]: https://img.shields.io/badge/Android%20Arsenal-Moshi%20Lazy%20Adapters-orange.svg?style=flat
[arsenal]: http://android-arsenal.com/details/1/4481
[sonatype]: https://oss.sonatype.org/content/repositories/snapshots/com/serjltt/moshi/
[dl]: https://search.maven.org/remote_content?g=com.serjltt.moshi&a=moshi-lazy-adapters&v=LATEST
================================================
FILE: build.gradle
================================================
buildscript {
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
// Needed to use auto-value-moshi in tests
dependencies {
classpath 'com.github.tbroyer:gradle-apt-plugin:v0.12'
}
}
repositories {
mavenCentral()
}
apply plugin: 'net.ltgt.apt-idea'
apply plugin: 'java'
apply from: rootProject.file('dependencies.gradle')
apply from: rootProject.file('checkstyle.gradle')
apply from: rootProject.file('jacoco.gradle')
apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
sourceSets {
// Setup source sets to split unit and integration tests
test {
java.srcDirs = ['src/unitTest/java', 'src/integrationTest/java']
resources.srcDirs = ['src/unitTest/resources', 'src/integrationTest/resources']
}
}
dependencies {
compile moshi
testCompile junit
testCompile assertJ
testCompile privateConstructorChecker
testCompile retrofit
testCompile retrofitMoshiConverter
testCompile mockWebServer
testCompile rxJava2
// This is needed to test integration with auto-value-moshi
testCompileOnly autoValueMoshiAnnotations
testCompileOnly autoValueAnnotations
testApt autoValueMoshi
}
================================================
FILE: checkstyle.gradle
================================================
apply plugin: 'checkstyle'
checkstyle {
toolVersion '6.0'
}
task checkstyle(type: Checkstyle) {
configFile rootProject.file('config/checkstyle/checkstyle.xml')
source = ['src']
ignoreFailures false
showViolations true
include '**/*.java'
classpath = files()
configProperties = [
'proj.module.dir' : projectDir.absolutePath,
'checkstyle.cache.file': './build/cache/checkstyle-cache'
]
}
afterEvaluate {
if (project.tasks.findByName('check')) {
check.dependsOn('checkstyle')
}
}
================================================
FILE: config/checkstyle/checkstyle.xml
================================================
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.2//EN"
"http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
<module name="Checker">
<!--module name="NewlineAtEndOfFile"/-->
<module name="FileLength"/>
<module name="FileTabCharacter"/>
<!-- Trailing spaces -->
<module name="RegexpSingleline">
<property name="format" value="\s+$"/>
<property name="message" value="Line has trailing spaces."/>
</module>
<!-- Space after 'for' and 'if' -->
<module name="RegexpSingleline">
<property name="format" value="^\s*(for|if)[^ ]"/>
<property name="message" value="Space needed before opening parenthesis."/>
</module>
<!-- For each spacing -->
<module name="RegexpSingleline">
<property name="format" value="^\s*for \(.*?([^ ]:|:[^ ])"/>
<property name="message" value="Space needed around ':' character."/>
</module>
<module name="TreeWalker">
<!--<property name="cacheFile" value="${checkstyle.cache.file}"/>-->
<!-- Checks for Javadoc comments. -->
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
<!--module name="JavadocMethod"/-->
<!--module name="JavadocType"/-->
<!--module name="JavadocVariable"/-->
<!--module name="JavadocStyle"/-->
<!-- Checks for Naming Conventions. -->
<!-- See http://checkstyle.sf.net/config_naming.html -->
<module name="ConstantName"/>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="MemberName"/>
<module name="MethodName">
<property name="format" value="^[a-z][a-zA-Z0-9_]*$"/>
</module>
<module name="PackageName"/>
<module name="ParameterName"/>
<module name="StaticVariableName"/>
<module name="TypeName">
<property name="format" value="^[A-Z][a-zA-Z0-9_]*$"/>
</module>
<!-- Checks for imports -->
<!-- See http://checkstyle.sf.net/config_import.html -->
<module name="AvoidStarImport"/>
<module name="IllegalImport"/>
<module name="RedundantImport"/>
<module name="UnusedImports">
<property name="processJavadoc" value="true"/>
</module>
<!-- Checks for Size Violations. -->
<!-- See http://checkstyle.sf.net/config_sizes.html -->
<module name="LineLength">
<property name="max" value="100"/>
</module>
<!--<module name="MethodLength"/>-->
<!--<module name="ParameterNumber"/>-->
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
<module name="GenericWhitespace"/>
<module name="EmptyForIteratorPad"/>
<module name="MethodParamPad"/>
<!--<module name="NoWhitespaceAfter"/>-->
<module name="NoWhitespaceBefore"/>
<module name="OperatorWrap"/>
<module name="ParenPad"/>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround"/>
<!-- Modifier Checks -->
<!-- See http://checkstyle.sf.net/config_modifiers.html -->
<!--module name="ModifierOrder"/-->
<module name="RedundantModifier"/>
<!-- Checks for blocks. You know, those {}'s -->
<!-- See http://checkstyle.sf.net/config_blocks.html -->
<module name="AvoidNestedBlocks"/>
<!--<module name="EmptyBlock"/>-->
<module name="LeftCurly"/>
<module name="NeedBraces">
<property name="tokens" value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE"/>
</module>
<module name="RightCurly"/>
<!-- Checks for common coding problems -->
<!-- See http://checkstyle.sf.net/config_coding.html -->
<!--<module name="AvoidInlineConditionals"/>-->
<module name="CovariantEquals"/>
<!--<module name="DoubleCheckedLocking"/>-->
<module name="EmptyStatement"/>
<module name="EqualsAvoidNull"/>
<module name="EqualsHashCode"/>
<!--<module name="HiddenField"/>-->
<module name="IllegalInstantiation"/>
<module name="InnerAssignment"/>
<!--<module name="MagicNumber"/>-->
<module name="MissingSwitchDefault"/>
<module name="RedundantThrows"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<!-- Checks for class design -->
<!-- See http://checkstyle.sf.net/config_design.html -->
<!--module name="DesignForExtension"/-->
<!--module name="FinalClass"/-->
<module name="HideUtilityClassConstructor"/>
<module name="InterfaceIsType"/>
<!--<module name="VisibilityModifier"/>-->
<!-- Miscellaneous other checks. -->
<!-- See http://checkstyle.sf.net/config_misc.html -->
<module name="ArrayTypeStyle"/>
<!--module name="FinalParameters"/-->
<!--<module name="TodoComment"/>-->
<module name="UpperEll"/>
<!-- Custom checks for JUnit's Assert usage. -->
<!-- From https://gist.github.com/vanniktech/e8117fb245e804971a83288c1498c9d7 -->
<module name="RegexpSinglelineJava">
<property name="format" value="import static org\.junit\.Assert\.assertEquals;"/>
<property name="message"
value="Don't use JUnits assertEquals, AssertJ is preferred: assertThat(foo).isEqualTo();"/>
</module>
<module name="RegexpSinglelineJava">
<property name="format" value="import static org\.junit\.Assert\.assertNotEquals;"/>
<property name="message"
value="Don't use JUnits assertNotEquals, AssertJ is preferred: assertThat(foo).isNotEqualTo();"/>
</module>
<module name="RegexpSinglelineJava">
<property name="format" value="import static org\.junit\.Assert\.assertSame;"/>
<property name="message"
value="Don't use JUnits assertSame, AssertJ is preferred: assertThat(foo).isSameAs();"/>
</module>
<module name="RegexpSinglelineJava">
<property name="format" value="import static org\.junit\.Assert\.assertNull;"/>
<property name="message"
value="Don't use JUnits assertNull, AssertJ is preferred: assertThat(foo).isNull()"/>
</module>
<module name="RegexpSinglelineJava">
<property name="format" value="import static org\.junit\.Assert\.assertNotNull;"/>
<property name="message"
value="Don't use JUnits assertNotNull, AssertJ is preferred: assertThat(foo).isNotNull()"/>
</module>
</module>
</module>
================================================
FILE: dependencies.gradle
================================================
ext {
javaVersion = JavaVersion.VERSION_1_7
ci = 'true'.equals(System.getenv('CI'))
/* Dependencies */
moshi = 'com.squareup.moshi:moshi:1.5.0'
/* Testing */
junit = 'junit:junit:4.12'
assertJ = 'org.assertj:assertj-core:2.5.0'
retrofit = 'com.squareup.retrofit2:retrofit:2.1.0'
retrofitMoshiConverter = 'com.squareup.retrofit2:converter-moshi:2.1.0'
mockWebServer = 'com.squareup.okhttp3:mockwebserver:3.4.1'
privateConstructorChecker = 'com.pushtorefresh.java-private-constructor-checker:checker:1.2.0'
rxJava2 = 'io.reactivex.rxjava2:rxjava:2.0.3'
autoValueMoshi = 'com.ryanharter.auto.value:auto-value-moshi:0.4.2'
autoValueMoshiAnnotations = 'com.ryanharter.auto.value:auto-value-moshi-annotations:0.4.2'
autoValueAnnotations = 'com.jakewharton.auto.value:auto-value-annotations:1.3'
}
================================================
FILE: gradle/gradle-mvn-push.gradle
================================================
/*
* Copyright (C) 2013 Chris Banes
*
* 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.
*/
import static org.gradle.internal.jvm.Jvm.current
apply plugin: 'maven'
apply plugin: 'signing'
version = VERSION_NAME
group = GROUP
def isSnapshot = VERSION_NAME.endsWith('-SNAPSHOT')
def ossrhUsername = hasProperty('nexusUsername') ? nexusUsername : ""
def ossrhPassword = hasProperty('nexusPassword') ? nexusPassword : ""
afterEvaluate { project ->
uploadArchives {
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
pom.groupId = GROUP
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
repository(url: 'https://oss.sonatype.org/service/local/staging/deploy/maven2/') {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots/') {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
pom.project {
name POM_NAME
packaging POM_PACKAGING
description POM_DESCRIPTION
url POM_URL
scm {
url POM_SCM_URL
connection POM_SCM_CONNECTION
developerConnection POM_SCM_DEV_CONNECTION
}
licenses {
license {
name POM_LICENCE_NAME
url POM_LICENCE_URL
distribution POM_LICENCE_DIST
}
}
developers {
developer {
id POM_DEVELOPER_ID
name POM_DEVELOPER_NAME
}
}
}
}
}
}
signing {
required { !isSnapshot && gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
}
if (project.getPlugins().hasPlugin('com.android.application')
|| project.getPlugins().hasPlugin('com.android.library')) {
uploadArchives.dependsOn(build)
task install(type: Upload, dependsOn: assemble) {
group 'upload'
repositories.mavenInstaller {
configuration = configurations.archives
pom.groupId = GROUP
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
pom.project {
name POM_NAME
packaging POM_PACKAGING
description POM_DESCRIPTION
url POM_URL
scm {
url POM_SCM_URL
connection POM_SCM_CONNECTION
developerConnection POM_SCM_DEV_CONNECTION
}
licenses {
license {
name POM_LICENCE_NAME
url POM_LICENCE_URL
distribution POM_LICENCE_DIST
}
}
developers {
developer {
id POM_DEVELOPER_ID
name POM_DEVELOPER_NAME
}
}
}
}
}
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java.source
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}
task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
classifier = 'javadoc'
from androidJavadocs.destinationDir
}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.source
}
} else {
install {
repositories.mavenInstaller {
pom.groupId = GROUP
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
pom.project {
name POM_NAME
packaging POM_PACKAGING
description POM_DESCRIPTION
url POM_URL
scm {
url POM_SCM_URL
connection POM_SCM_CONNECTION
developerConnection POM_SCM_DEV_CONNECTION
}
licenses {
license {
name POM_LICENCE_NAME
url POM_LICENCE_URL
distribution POM_LICENCE_DIST
}
}
developers {
developer {
id POM_DEVELOPER_ID
name POM_DEVELOPER_NAME
}
}
}
}
}
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
from sourceSets.main.allSource
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
//noinspection GroovyAccessibility
from javadoc.destinationDir
}
}
// JDK 1.8 is more strict then 1.7. Have JDK 1.8 behave like 1.7 for javadoc generation
if (current().getJavaVersion() == JavaVersion.VERSION_1_8) {
allprojects {
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
}
}
}
artifacts {
if (project.getPlugins().hasPlugin('com.android.application')
|| project.getPlugins().hasPlugin('com.android.library')) {
archives androidSourcesJar
archives androidJavadocsJar
} else {
archives sourcesJar
archives javadocJar
}
}
/** Task that allows to install archives to local maven. */
task installArchives(type: Upload) {
group 'upload'
description "Installs the artifacts to the local Maven repository."
configuration = configurations['archives']
repositories {
mavenDeployer {
pom.groupId = GROUP
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
repository url: "file://${System.properties['user.home']}/.m2/repository"
pom.project {
name POM_NAME
packaging POM_PACKAGING
description POM_DESCRIPTION
url POM_URL
}
}
}
}
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Mon Oct 09 10:31:26 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-all.zip
================================================
FILE: gradle.properties
================================================
GROUP=com.serjltt.moshi
VERSION_NAME=2.3-SNAPSHOT
POM_DESCRIPTION=A collection of simple JsonAdapters for Moshi.
POM_ARTIFACT_ID=moshi-lazy-adapters
POM_NAME=Moshi Lazy Adapters
POM_PACKAGING=jar
POM_URL=https://github.com/serj-lotutovici/moshi-lazy-adapters/
POM_SCM_URL=https://github.com/serj-lotutovici/moshi-lazy-adapters
POM_SCM_CONNECTION=scm:git:git@github.com:serj-lotutovici/moshi-lazy-adapters.git
POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com:serj-lotutovici/moshi-lazy-adapters.git
POM_LICENCE_NAME=The Apache Software License, Version 2.0
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=serj-lotutovici
POM_DEVELOPER_NAME=Serj Lotutovici
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save ( ) {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: jacoco.gradle
================================================
apply plugin: 'jacoco'
jacoco {
toolVersion = '0.7.7.201606060606' // See http://www.eclemma.org/jacoco/.
}
jacocoTestReport {
reports {
xml.enabled = true
html.enabled = true
}
}
check.dependsOn jacocoTestReport
================================================
FILE: settings.gradle
================================================
rootProject.name = 'moshi-lazy-adapters'
================================================
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/Data.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.google.auto.value.AutoValue;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
/** Data class for testing auto-value-moshi integration. */
@AutoValue
abstract class Data {
public static JsonAdapter<Data> jsonAdapter(Moshi moshi) {
return new AutoValue_Data.MoshiJsonAdapter(moshi);
}
abstract String name();
/**
* The name of the method will be taken as the first key, then the path provided with the
* annotation.
*/
@Wrapped(path = {"1"}) abstract Meta meta();
static class Meta {
String value1;
int value2;
}
}
================================================
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/DataFactories.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.ryanharter.auto.value.moshi.MoshiAdapterFactory;
import com.squareup.moshi.JsonAdapter;
/** Provided the factory for generated adapters. */
@MoshiAdapterFactory
abstract class DataFactories implements JsonAdapter.Factory {
public static JsonAdapter.Factory create() {
return new AutoValueMoshi_DataFactories();
}
}
================================================
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/DeserializeOnlyAutoValueTest.java
================================================
package com.serjltt.moshi.adapters;
import com.google.auto.value.AutoValue;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import java.io.IOException;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public final class DeserializeOnlyAutoValueTest {
private final Moshi moshi = new Moshi.Builder()
.add(DeserializeOnly.ADAPTER_FACTORY)
.add(DataFactories.create())
.build();
@Test public void serialize() {
final String json = AutoValueClass.jsonAdapter(moshi)
.toJson(new AutoValue_DeserializeOnlyAutoValueTest_AutoValueClass(1, 2));
assertThat(json).isEqualTo("{\"foo\":1}");
}
@Test public void deserialize() throws IOException {
final AutoValueClass autoValueClass =
AutoValueClass.jsonAdapter(moshi).fromJson("{\"foo\": 1,\"bar\": 2}");
assertThat(autoValueClass.foo()).isEqualTo(1);
assertThat(autoValueClass.bar()).isEqualTo(2);
}
@AutoValue abstract static class AutoValueClass {
public static JsonAdapter<AutoValueClass> jsonAdapter(Moshi moshi) {
return new AutoValue_DeserializeOnlyAutoValueTest_AutoValueClass.MoshiJsonAdapter(moshi);
}
abstract Integer foo();
@DeserializeOnly abstract Integer bar();
}
}
================================================
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/LazyAdaptersAutoValueTest.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import java.util.Collections;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/** Test all lazy adapters to work in integration with AutoValue extensions. */
public class LazyAdaptersAutoValueTest {
private final Moshi moshi = new Moshi.Builder()
.add(Wrapped.ADAPTER_FACTORY)
.add(DataFactories.create())
.build();
@Test public void unwrap() throws Exception {
JsonAdapter<Data> adapter = moshi.adapter(Data.class);
Data fromJson = adapter.fromJson("{\n"
+ " \"name\": \"data_name\",\n"
+ " \"meta\": {\n"
+ " \"1\": {\n"
+ " \"value1\": \"value1\",\n"
+ " \"value2\": 2\n"
+ " }\n"
+ " }\n"
+ "}");
assertThat(fromJson.name()).isEqualTo("data_name");
assertThat(fromJson.meta().value1).isEqualTo("value1");
assertThat(fromJson.meta().value2).isEqualTo(2);
}
@Test
public void wrap() throws Exception {
JsonAdapter<Data> adapter = moshi.adapter(Data.class,
Collections.singleton(Wrapped.Factory.create("foo")));
Data.Meta meta = new Data.Meta();
meta.value1 = "value1";
meta.value2 = 2;
Data data = new AutoValue_Data("data_name", meta);
String toJson = adapter.toJson(data);
assertThat(toJson).isEqualTo(
"{\"foo\":{\"name\":\"data_name\","
+ "\"meta\":{\"1\":{\"value1\":\"value1\",\"value2\":2}}}}");
}
}
================================================
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/LazyAdaptersRetrofitTest.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.Json;
import com.squareup.moshi.Moshi;
import java.io.IOException;
import okhttp3.ResponseBody;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.Rule;
import org.junit.Test;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.moshi.MoshiConverterFactory;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import static org.assertj.core.api.Assertions.assertThat;
/** Test all lazy adapters to work in integration with retrofit. */
public final class LazyAdaptersRetrofitTest {
@Rule public final MockWebServer server = new MockWebServer();
private final Moshi moshi = new Moshi.Builder()
.add(Wrapped.ADAPTER_FACTORY)
.add(FirstElement.ADAPTER_FACTORY)
.add(ElementAt.ADAPTER_FACTORY)
.build();
private final Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(server.url("/"))
.build();
private final Service service = retrofit.create(Service.class);
@Test public void unwrapJsonAdapter() throws Exception {
assertResponse(service.unwrap(), "{\n"
+ " \"one\": {\n"
+ " \"two\": \"works!\"\n"
+ " }\n"
+ "}", "works!");
}
@Test public void unwrapNestedJsonAdapter() throws Exception {
server.enqueue(new MockResponse().setBody("{\n"
+ " \"one\": {\n"
+ " \"two\": {\n"
+ " \"item\": {\n"
+ " \"foo\": \"this\"\n"
+ " },\n"
+ " \"item2\": {\n"
+ " \"bar\": 1234\n"
+ " },\n"
+ " \"foobar\": 567\n"
+ " }\n"
+ " }\n"
+ "}"));
Response<Nested> response = service.unwrapNested().execute();
assertThat(response.body().foobar).isEqualTo(567);
assertThat(response.body().foo).isEqualTo("this");
assertThat(response.body().bar).isEqualTo(1234);
}
@Test public void wrapPostBody() throws Exception {
server.enqueue(new MockResponse());
Call<ResponseBody> call = service.wrappedPost("one");
call.execute();
RecordedRequest recorded = server.takeRequest();
assertThat(recorded.getBody()
.readUtf8())
.isEqualTo("{\"1\":{\"2\":\"one\"}}");
}
@Test public void firstElementJsonAdapter() throws Exception {
assertResponse(service.firstElement(), "[\n"
+ " \"expected\",\n"
+ " \"ignored\"\n"
+ "]", "expected");
}
@Test public void elementAtJsonAdapter() throws Exception {
assertResponse(service.elementAt(), "[\n"
+ " \"one\",\n"
+ " \"two\",\n"
+ " \"three\"\n"
+ "]", "three");
}
@Test public void unwrapFirstElement() throws Exception {
assertResponse(service.unwrapFirstElement(), "{\n"
+ " \"one\": {\n"
+ " \"two\": [\n"
+ " \"first\"\n"
+ " ]\n"
+ " }\n"
+ "}", "first");
}
private <T> void assertResponse(Call<T> call, String input, T expected) throws IOException {
server.enqueue(new MockResponse().setBody(input));
Response<T> response = call.execute();
assertThat(response.body()).isEqualTo(expected);
}
/** Test service for all lazy adapters. */
private interface Service {
/** Helps to test the unwrap adapter. */
@GET("/")
@Wrapped(path = {"one", "two"}) Call<String> unwrap();
@GET("/")
@Wrapped(path = {"one", "two"}) Call<Nested> unwrapNested();
@POST("/") Call<ResponseBody> wrappedPost(@Body @Wrapped(path = {"1", "2"}) String value);
/** Helps to test the first element json adapter. */
@GET("/")
@FirstElement Call<String> firstElement();
/** Helps to test the first element json adapter. */
@GET("/")
@Wrapped(path = {"one", "two"})
@FirstElement Call<String> unwrapFirstElement();
@GET("/")
@ElementAt(index = 2) Call<String> elementAt();
}
static class Nested {
@Json(name = "item") @Wrapped(path = "foo") String foo;
@Json(name = "item2") @Wrapped(path = "bar") int bar;
int foobar;
}
}
================================================
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/LazyAdaptersRxJavaTest.java
================================================
package com.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonDataException;
import com.squareup.moshi.Moshi;
import io.reactivex.Flowable;
import io.reactivex.Maybe;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.observers.BaseTestConsumer;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.Callable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
/** Tests if specific adapters behave properly with RxJava2. */
@RunWith(Parameterized.class)
public final class LazyAdaptersRxJavaTest {
private static final Moshi MOSHI = new Moshi.Builder()
.add(Wrapped.ADAPTER_FACTORY)
.build();
private static Callable<String> failingCallable() {
Set<Annotation> annotations =
Collections.<Annotation>singleton(Wrapped.Factory.create(true, "one", "two", "three"));
final JsonAdapter<String> adapter = MOSHI.adapter(String.class, annotations);
return new Callable<String>() {
@Override public String call() throws Exception {
return adapter.fromJson("{\n"
+ " \"one\": {\n"
+ " \"two\": null\n"
+ " }\n"
+ "}");
}
};
}
@Parameterized.Parameters
public static BaseTestConsumer[] testData() {
return new BaseTestConsumer[] {
Single.fromCallable(failingCallable()).test(),
Observable.fromCallable(failingCallable()).test(),
Maybe.fromCallable(failingCallable()).test(),
Flowable.fromCallable(failingCallable()).test()
};
}
private final BaseTestConsumer testConsumer;
public LazyAdaptersRxJavaTest(BaseTestConsumer testConsumer) {
this.testConsumer = testConsumer;
}
@Test public void reactiveTypeYieldsAppropriateError() throws Exception {
//noinspection unchecked
testConsumer
.assertFailureAndMessage(JsonDataException.class,
"Wrapped Json expected at path: [one, two, three]. Found null at $.one.two");
}
}
================================================
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/Nullable.java
================================================
package com.serjltt.moshi.adapters;
/** For testing purposes. */
public @interface Nullable {
}
================================================
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/SerializeOnlyAutoValueTest.java
================================================
package com.serjltt.moshi.adapters;
import com.google.auto.value.AutoValue;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import java.io.IOException;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public final class SerializeOnlyAutoValueTest {
private final Moshi moshi = new Moshi.Builder()
.add(SerializeOnly.ADAPTER_FACTORY)
.add(DataFactories.create())
.build();
@Test public void serialize() {
final String json = AutoValueClass.jsonAdapter(moshi)
.toJson(new AutoValue_SerializeOnlyAutoValueTest_AutoValueClass(1, 2));
assertThat(json).isEqualTo("{\"foo\":1,\"bar\":2}");
}
@Test public void deserialize() throws IOException {
final AutoValueClass autoValueClass =
AutoValueClass.jsonAdapter(moshi).fromJson("{\"foo\": 1,\"bar\": 2}");
assertThat(autoValueClass.foo()).isEqualTo(1);
assertThat(autoValueClass.bar()).isNull();
}
@AutoValue abstract static class AutoValueClass {
public static JsonAdapter<AutoValueClass> jsonAdapter(Moshi moshi) {
return new AutoValue_SerializeOnlyAutoValueTest_AutoValueClass.MoshiJsonAdapter(moshi);
}
abstract Integer foo();
@SerializeOnly @Nullable abstract Integer bar();
}
}
================================================
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/TransientAutoValueTest.java
================================================
package com.serjltt.moshi.adapters;
import com.google.auto.value.AutoValue;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Collections;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public final class TransientAutoValueTest {
private final Moshi moshi = new Moshi.Builder()
.add(Transient.ADAPTER_FACTORY)
.add(DataFactories.create())
.build();
@Test public void serialize() {
final String json = AutoValueClass.jsonAdapter(moshi)
.toJson(new AutoValue_TransientAutoValueTest_AutoValueClass(1, 2));
assertThat(json).isEqualTo("{\"foo\":1}");
}
@Test public void deserialize() throws IOException {
final AutoValueClass autoValueClass =
AutoValueClass.jsonAdapter(moshi).fromJson("{\"foo\": 1,\"bar\": 2}");
assertThat(autoValueClass.foo()).isEqualTo(1);
assertThat(autoValueClass.bar()).isNull();
}
@Test public void toStringReflectsInnerAdapter() throws Exception {
JsonAdapter<String> adapter =
moshi.adapter(String.class, Collections.singleton(new SerializeOnly() {
@Override public Class<? extends Annotation> annotationType() {
return Transient.class;
}
}));
assertThat(adapter.toString()).isEqualTo("JsonAdapter(String).nullSafe().transient()");
}
@AutoValue abstract static class AutoValueClass {
public static JsonAdapter<AutoValueClass> jsonAdapter(Moshi moshi) {
return new AutoValue_TransientAutoValueTest_AutoValueClass.MoshiJsonAdapter(moshi);
}
abstract Integer foo();
@Transient @Nullable abstract Integer bar();
}
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/DefaultOnDataMismatchAdapter.java
================================================
/*
* Copyright (C) 2017 Square, 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.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonDataException;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Set;
/**
* Adapter that fallbacks to a default value in case there's a mismatch.
*/
public final class DefaultOnDataMismatchAdapter<T> extends JsonAdapter<T> {
private final JsonAdapter<T> delegate;
private final T defaultValue;
DefaultOnDataMismatchAdapter(JsonAdapter<T> delegate, T defaultValue) {
this.delegate = delegate;
this.defaultValue = defaultValue;
}
@Override public T fromJson(JsonReader reader) throws IOException {
Object jsonValue = reader.readJsonValue();
try {
return delegate.fromJsonValue(jsonValue);
} catch (JsonDataException ignore) {
return defaultValue;
}
}
@Override public void toJson(JsonWriter writer, T value) throws IOException {
delegate.toJson(writer, value);
}
@Override public String toString() {
return delegate + ".defaultOnDatMisMatch(" + defaultValue + ')';
}
/** Builds an adapter that fallbacks to a default value in case there's a mismatch. */
public static <T> JsonAdapter.Factory newFactory(final Type type, final T defaultValue) {
return new Factory() {
@Override public JsonAdapter<?> create(Type requestedType,
Set<? extends Annotation> annotations, Moshi moshi) {
if (Types.equals(type, requestedType)) {
JsonAdapter<T> delegate = moshi.nextAdapter(this, type, annotations);
return new DefaultOnDataMismatchAdapter<>(delegate, defaultValue);
}
return null;
}
};
}
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/DeserializeOnly.java
================================================
package com.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.util.Set;
/**
* Indicates that the annotated field may only be deserialized.
*
* <p>To leverage from {@link DeserializeOnly} {@link DeserializeOnly#ADAPTER_FACTORY} must be
* added to your {@linkplain Moshi Moshi instance}:
*
* <pre><code>
* Moshi moshi = new Moshi.Builder()
* .add(DeserializeOnly.ADAPTER_FACTORY)
* .build();
* </code></pre>
*/
@Documented
@JsonQualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface DeserializeOnly {
/** Builds an adapter that can process a types annotated with {@link DeserializeOnly}. */
JsonAdapter.Factory ADAPTER_FACTORY = new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations,
Moshi moshi) {
Set<? extends Annotation> nextAnnotations =
Types.nextAnnotations(annotations, DeserializeOnly.class);
if (nextAnnotations == null) return null;
return new TransientJsonAdapter<>(moshi.adapter(type, nextAnnotations), false, true);
}
};
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/ElementAt.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.Moshi;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.util.Set;
import static com.serjltt.moshi.adapters.Util.nextAnnotations;
/**
* Indicates that the annotated type/field is <strong>expected</strong> to be the element
* at the given index of a json array.
*
* <p>For example if a json object is:
* <pre>
* [
* {
* "some_field": "some_value",
* "other_field": "other_value"
* },
* {
* "some_field": "some_value_2",
* "other_field": "other_value_2"
* }
* ]
* </pre>
* And the consumer only cares about the second element, if using retrofit a service method would
* look like:
*
* <pre><code>
* {@literal @}GET("path/")
* {@literal @}ElementAt(index = 1) Call<DataObject> getData();
* </code></pre>
*
* The resulting response returned by {@code response.body()} will be an instance of {@code
* DataObject} with the respective values set.
*
* <p>To leverage from {@link ElementAt} {@link ElementAt#ADAPTER_FACTORY}
* must be added to your {@linkplain Moshi Moshi instance}:
*
* <pre><code>
* Moshi moshi = new Moshi.Builder()
* .add(ElementAt.ADAPTER_FACTORY)
* .build();
* </code></pre>
*/
@Documented
@JsonQualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface ElementAt {
/**
* Represents the index location at which the element will be expected to be.
* If the size of the array will be less then the provided index,
* the companion adapter will return {@code null}.
*/
int index();
/** Builds an adapter that can process a types annotated with {@link ElementAt}. */
JsonAdapter.Factory ADAPTER_FACTORY = new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations,
Moshi moshi) {
Pair<ElementAt, Set<Annotation>> nextAnnotations =
nextAnnotations(annotations, ElementAt.class);
if (nextAnnotations == null || !nextAnnotations.second.isEmpty()) return null;
return new ElementAtJsonAdapter<>(type, moshi, nextAnnotations.first.index());
}
};
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/ElementAtJsonAdapter.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
/**
* {@linkplain JsonAdapter} that extracts the element at the given index
* of an array of (a field) type annotated with {@linkplain ElementAt}.
*/
final class ElementAtJsonAdapter<T> extends JsonAdapter<T> {
private final JsonAdapter<List<T>> delegate;
private final int index;
ElementAtJsonAdapter(Type type, Moshi moshi, int index) {
Type listType = Types.newParameterizedType(List.class, type);
delegate = moshi.adapter(listType);
this.index = index;
}
@Override public T fromJson(JsonReader reader) throws IOException {
List<T> fromJson = delegate.fromJson(reader);
if (fromJson != null && index < fromJson.size()) return fromJson.get(index);
return null;
}
@Override public void toJson(JsonWriter writer, T value) throws IOException {
delegate.toJson(writer, Collections.singletonList(value));
}
@Override public String toString() {
return delegate + ".elementAt(" + index + ")";
}
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/FallbackEnum.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.util.Set;
/**
* Indicates that the annotated enum has a fallback value. The fallback must be set via
* {@link #name()}. If no enum constant with the provided name is declared in the annotated
* enum type an {@linkplain AssertionError assertion error} will be thrown.
*
* <p>To leverage from {@link FallbackEnum} {@link FallbackEnum#ADAPTER_FACTORY} must be added to
* your {@linkplain Moshi moshi instance}:
*
* <pre><code>
* Moshi moshi = new Moshi.Builder()
* .add(FallbackEnum.ADAPTER_FACTORY)
* .build();
* </code></pre>
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FallbackEnum {
String name();
/** Builds an adapter that can process enums annotated with {@link FallbackEnum}. */
JsonAdapter.Factory ADAPTER_FACTORY = new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations,
Moshi moshi) {
if (!annotations.isEmpty()) return null;
Class<?> rawType = Types.getRawType(type);
if (rawType.isEnum()) {
FallbackEnum annotation = rawType.getAnnotation(FallbackEnum.class);
if (annotation == null) return null;
//noinspection unchecked
return new FallbackEnumJsonAdapter<>((Class<? extends Enum>) rawType, annotation.name())
.nullSafe();
}
return null;
}
};
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/FallbackEnumJsonAdapter.java
================================================
/*
* Copyright 2016 Serj Lotutovici
* Copyright (C) 2014 Square, 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.serjltt.moshi.adapters;
import com.squareup.moshi.Json;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
import java.io.IOException;
/**
* {@linkplain JsonAdapter} that fallbacks to a default enum constant declared in the enum type
* annotated with {@linkplain FallbackEnum}.
*/
final class FallbackEnumJsonAdapter<T extends Enum<T>> extends JsonAdapter<T> {
private final Class<T> enumType;
private final String[] nameStrings;
private final T[] constants;
private final JsonReader.Options options;
private final T fallbackConstant;
FallbackEnumJsonAdapter(Class<T> enumType, String fallback) {
fallbackConstant = Enum.valueOf(enumType, fallback);
this.enumType = enumType;
try {
constants = enumType.getEnumConstants();
nameStrings = new String[constants.length];
for (int i = 0; i < constants.length; i++) {
T constant = constants[i];
Json annotation = enumType.getField(constant.name()).getAnnotation(Json.class);
String name = annotation != null ? annotation.name() : constant.name();
nameStrings[i] = name;
}
options = JsonReader.Options.of(nameStrings);
} catch (NoSuchFieldException e) {
throw new AssertionError(e);
}
}
@Override public T fromJson(JsonReader reader) throws IOException {
int index = reader.selectString(options);
if (index != -1) return constants[index];
reader.nextString();
return fallbackConstant;
}
@Override public void toJson(JsonWriter writer, T value) throws IOException {
writer.value(nameStrings[value.ordinal()]);
}
@Override public String toString() {
return "JsonAdapter(" + enumType.getName() + ").fallbackEnum(" + fallbackConstant + ")";
}
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/FallbackOnNull.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Set;
import java.util.Locale;
import static com.serjltt.moshi.adapters.Util.nextAnnotations;
/**
* Indicates that the annotated field may be {@code null} in the json source and thus requires a
* fallback value.
*
* <p>To leverage from {@linkplain FallbackOnNull} {@linkplain FallbackOnNull#ADAPTER_FACTORY}
* must be added to your {@linkplain Moshi Moshi instance}:
*
* <pre><code>
* Moshi moshi = new Moshi.Builder()
* .add(FallbackOnNull.ADAPTER_FACTORY)
* .build();
* </code></pre>
*/
@Documented
@JsonQualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface FallbackOnNull {
/** Fallback value for {@code boolean} primitives. Default: {@code false}. */
boolean fallbackBoolean() default false;
/** Fallback value for {@code byte} primitives. Default: {@code Byte.MIN_VALUE}. */
byte fallbackByte() default Byte.MIN_VALUE;
/** Fallback value for {@code char} primitives. Default: {@code Character.MIN_VALUE}. */
char fallbackChar() default Character.MIN_VALUE;
/** Fallback value for {@code double} primitives. Default: {@code Double.MIN_VALUE}. */
double fallbackDouble() default Double.MIN_VALUE;
/** Fallback value for {@code float} primitives. Default: {@code Float.MIN_VALUE}. */
float fallbackFloat() default Float.MIN_VALUE;
/** Fallback value for {@code int} primitives. Default: {@code Integer.MIN_VALUE}. */
int fallbackInt() default Integer.MIN_VALUE;
/** Fallback value for {@code long} primitives. Default: {@code Long.MIN_VALUE}. */
long fallbackLong() default Long.MIN_VALUE;
/** Fallback value for {@code short} primitives. Default: {@code Short.MIN_VALUE}. */
short fallbackShort() default Short.MIN_VALUE;
/** Builds an adapter that can process a types annotated with {@link FallbackOnNull}. */
JsonAdapter.Factory ADAPTER_FACTORY = new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations,
Moshi moshi) {
Pair<FallbackOnNull, Set<Annotation>> nextAnnotations =
nextAnnotations(annotations, FallbackOnNull.class);
if (nextAnnotations == null) return null;
Class<?> rawType = Types.getRawType(type);
if (!FallbackOnNullJsonAdapter.PRIMITIVE_CLASSES.contains(rawType)) return null;
String fallbackType = fallbackType(rawType);
Object fallback = retrieveFallback(nextAnnotations.first, fallbackType);
return new FallbackOnNullJsonAdapter<>(moshi.adapter(type, nextAnnotations.second),
fallback, fallbackType);
}
/** Invokes the appropriate fallback method based on the {@code fallbackType}. */
private Object retrieveFallback(FallbackOnNull annotation, String fallbackType) {
try {
Method fallbackMethod = FallbackOnNull.class.getMethod(fallbackType);
return fallbackMethod.invoke(annotation);
} catch (Exception e) {
throw new AssertionError(e);
}
}
/** Constructs the appropriate fallback method name based on the {@code rawType}. */
private String fallbackType(Class<?> rawType) {
String typeName = rawType.getSimpleName();
String methodSuffix = typeName.substring(0, 1).toUpperCase(Locale.US) + typeName.substring(1);
return "fallback" + methodSuffix;
}
};
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/FallbackOnNullJsonAdapter.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* {@linkplain JsonAdapter} that fallbacks to a default value of a primitive field annotated with
* {@linkplain FallbackOnNull}.
*/
final class FallbackOnNullJsonAdapter<T> extends JsonAdapter<T> {
/** Set of primitives classes that are supported by <strong>this</strong> adapter. */
static final Set<Class<?>> PRIMITIVE_CLASSES = new LinkedHashSet<>();
static {
PRIMITIVE_CLASSES.add(boolean.class);
PRIMITIVE_CLASSES.add(byte.class);
PRIMITIVE_CLASSES.add(char.class);
PRIMITIVE_CLASSES.add(double.class);
PRIMITIVE_CLASSES.add(float.class);
PRIMITIVE_CLASSES.add(int.class);
PRIMITIVE_CLASSES.add(long.class);
PRIMITIVE_CLASSES.add(short.class);
}
final JsonAdapter<T> delegate;
final T fallback;
final String fallbackType;
FallbackOnNullJsonAdapter(JsonAdapter<T> delegate, T fallback, String fallbackType) {
this.delegate = delegate;
this.fallback = fallback;
this.fallbackType = fallbackType;
}
@Override public T fromJson(JsonReader reader) throws IOException {
if (reader.peek() == JsonReader.Token.NULL) {
reader.nextNull(); // We need to consume the value.
return fallback;
}
return delegate.fromJson(reader);
}
@Override public void toJson(JsonWriter writer, T value) throws IOException {
delegate.toJson(writer, value);
}
@Override public String toString() {
return delegate + ".fallbackOnNull(" + fallbackType + '=' + fallback + ')';
}
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/FilterNulls.java
================================================
package com.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Set;
/**
* Indicates that the annotated field may not contain any null values.
* This annotation is applicable to all Collections.
*
* <p>To leverage from {@link FilterNulls} {@link FilterNulls#ADAPTER_FACTORY} must be
* added to your {@linkplain Moshi Moshi instance}:
*
* <pre><code>
* Moshi moshi = new Moshi.Builder()
* .add(FilterNulls.ADAPTER_FACTORY)
* .build();
* </code></pre>
*/
@Documented
@JsonQualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface FilterNulls {
/** Builds an adapter that can process types annotated with {@link FilterNulls}. */
JsonAdapter.Factory ADAPTER_FACTORY = new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations,
Moshi moshi) {
Set<? extends Annotation> nextAnnotations =
Types.nextAnnotations(annotations, FilterNulls.class);
if (nextAnnotations == null || !nextAnnotations.isEmpty()) return null;
Class<?> rawType = Types.getRawType(type);
if (Collection.class.isAssignableFrom(rawType)) {
return new FilterNullsJsonAdapter<>(moshi.adapter(type, nextAnnotations));
}
return null;
}
};
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/FilterNullsJsonAdapter.java
================================================
package com.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
/**
* {@link JsonAdapter} that filters null values out.
*/
final class FilterNullsJsonAdapter<T> extends JsonAdapter<T> {
private final JsonAdapter<T> delegate;
FilterNullsJsonAdapter(JsonAdapter<T> delegate) {
this.delegate = delegate;
}
@Override public T fromJson(JsonReader reader) throws IOException {
return removeNulls(delegate.fromJson(reader));
}
@Override public void toJson(JsonWriter writer, T value) throws IOException {
delegate.toJson(writer, removeNulls(value));
}
@Override public String toString() {
return delegate + ".filterNulls()";
}
private T removeNulls(final T value) {
if (value != null) {
final Iterator<?> it = ((Collection<?>) value).iterator();
while (it.hasNext()) {
if (it.next() == null) {
it.remove();
}
}
}
return value;
}
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/FirstElement.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.util.Set;
/**
* Indicates that the annotated type/field is <strong>expected</strong> to be the first element of a
* json array.
*
* <p>For example if a json object is returned as:
* <pre>
* [
* {
* "some_field": "some_value",
* "other_field": "other_value"
* }
* ]
* </pre>
* And the consumer only cares about the actual element, in the case of using a retrofit service
* method the code would look like:
*
* <pre><code>
* {@literal @}GET("path/")
* {@literal @}FirstElement Call<DataObject> getData();
* </code></pre>
*
* The resulting response returned by {@code response.body()} will be an instance of {@code
* DataObject} with the respective values set.
*
* <p>To leverage from {@link FirstElement} {@linkplain FirstElement#ADAPTER_FACTORY}
* must be added to a {@linkplain Moshi Moshi instance}:
*
* <pre><code>
* Moshi moshi = new Moshi.Builder()
* .add(FirstElement.ADAPTER_FACTORY)
* .build();
* </code></pre>
*/
@Documented
@JsonQualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface FirstElement {
/** Builds an adapter that can process a types annotated with {@link FirstElement}. */
JsonAdapter.Factory ADAPTER_FACTORY = new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations,
Moshi moshi) {
Set<? extends Annotation> nextAnnotations =
Types.nextAnnotations(annotations, FirstElement.class);
if (nextAnnotations == null || !nextAnnotations.isEmpty()) return null;
return new ElementAtJsonAdapter<>(type, moshi, 0);
}
};
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/LastElement.java
================================================
package com.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.util.Set;
/**
* Indicates that the annotated type/field is <strong>expected</strong> to be the last element of a
* json array.
*
* <p>For example if a json object is returned as:
* <pre>
* [
* {
* "some_field": "some_value",
* "other_field": "other_value"
* }
* ]
* </pre>
* And the consumer only cares about the last element, in the case of using a retrofit service
* method the code would look like:
*
* <pre><code>
* {@literal @}GET("path/")
* {@literal @}LastElement Call<DataObject> getData();
* </code></pre>
*
* The resulting response returned by {@code response.body()} will be an instance of {@code
* DataObject} with the respective values set.
*
* <p>To leverage from {@link LastElement} {@linkplain LastElement#ADAPTER_FACTORY}
* must be added to a {@linkplain Moshi Moshi instance}:
*
* <pre><code>
* Moshi moshi = new Moshi.Builder()
* .add(LastElement.ADAPTER_FACTORY)
* .build();
* </code></pre>
*/
@Documented
@JsonQualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface LastElement {
/** Builds an adapter that can process a type/field annotated with {@link LastElement}. */
JsonAdapter.Factory ADAPTER_FACTORY = new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations,
Moshi moshi) {
Set<? extends Annotation> nextAnnotations =
Types.nextAnnotations(annotations, LastElement.class);
if (nextAnnotations == null || !nextAnnotations.isEmpty()) return null;
return new LastElementJsonAdapter<>(type, moshi);
}
};
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/LastElementJsonAdapter.java
================================================
package com.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
/**
* {@linkplain JsonAdapter} that extracts the last element
* of an array of (a field) type or a method annotated with {@linkplain LastElement}.
*/
final class LastElementJsonAdapter<T> extends JsonAdapter<T> {
private final JsonAdapter<List<T>> delegate;
LastElementJsonAdapter(Type type, Moshi moshi) {
Type listType = Types.newParameterizedType(List.class, type);
delegate = moshi.adapter(listType);
}
@Override public T fromJson(JsonReader reader) throws IOException {
List<T> fromJson = delegate.fromJson(reader);
if (fromJson != null && !fromJson.isEmpty()) return fromJson.get(fromJson.size() - 1);
return null;
}
@Override public void toJson(JsonWriter writer, T value) throws IOException {
delegate.toJson(writer, Collections.singletonList(value));
}
@Override public String toString() {
return delegate + ".lastElement()";
}
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/Pair.java
================================================
package com.serjltt.moshi.adapters;
/** A simple pair data class. */
final class Pair<F, S> {
final F first;
final S second;
Pair(F first, S second) {
this.first = first;
this.second = second;
}
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/SerializeNulls.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.util.Set;
/**
* Indicates that the annotated type/field should be serialized as {@code null} in case of a
* empty/null value.
*
* <p>To leverage from {@link SerializeNulls} {@link SerializeNulls#ADAPTER_FACTORY}
* must be added to a {@linkplain Moshi Moshi instance}:
*
* <pre><code>
* Moshi moshi = new Moshi.Builder()
* .add(SerializeNulls.ADAPTER_FACTORY)
* .build();
* </code></pre>
*/
@Documented
@JsonQualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface SerializeNulls {
/** Builds an adapter that can process a types annotated with {@link SerializeNulls}. */
JsonAdapter.Factory ADAPTER_FACTORY = new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations,
Moshi moshi) {
Set<? extends Annotation> nextAnnotations =
Types.nextAnnotations(annotations, SerializeNulls.class);
if (nextAnnotations == null) return null;
return moshi.adapter(type, nextAnnotations).serializeNulls();
}
};
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/SerializeOnly.java
================================================
package com.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.util.Set;
/**
* Indicates that the annotated field may only be serialized.
*
* <p>To leverage from {@link SerializeOnly} {@link SerializeOnly#ADAPTER_FACTORY} must be added
* to your {@linkplain Moshi Moshi instance}:
*
* <pre><code>
* Moshi moshi = new Moshi.Builder()
* .add(SerializeOnly.ADAPTER_FACTORY)
* .build();
* </code></pre>
*/
@Documented
@JsonQualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface SerializeOnly {
/** Builds an adapter that can process a types annotated with {@link SerializeOnly}. */
JsonAdapter.Factory ADAPTER_FACTORY = new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations,
Moshi moshi) {
Set<? extends Annotation> nextAnnotations =
Types.nextAnnotations(annotations, SerializeOnly.class);
if (nextAnnotations == null) return null;
return new TransientJsonAdapter<>(moshi.adapter(type, nextAnnotations), true, false);
}
};
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/SerializeOnlyNonEmpty.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
/**
* Indicates that the annotated type/field should not be serialized in case of an
* empty value.
*
* <p>To leverage from {@link SerializeOnlyNonEmpty} {@link SerializeOnlyNonEmpty#ADAPTER_FACTORY}
* must be added to your {@linkplain Moshi Moshi instance}:
*
* <pre><code>
* Moshi moshi = new Moshi.Builder()
* .add(SerializeOnlyNonEmpty.ADAPTER_FACTORY)
* .build();
* </code></pre>
*/
@Documented
@JsonQualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface SerializeOnlyNonEmpty {
/** Builds an adapter that can process a types annotated with {@link SerializeOnlyNonEmpty}. */
JsonAdapter.Factory ADAPTER_FACTORY = new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations,
Moshi moshi) {
Set<? extends Annotation> nextAnnotations =
Types.nextAnnotations(annotations, SerializeOnlyNonEmpty.class);
if (nextAnnotations == null) return null;
Class<?> rawType = Types.getRawType(type);
if (rawType.isArray() || Collection.class.isAssignableFrom(rawType)
|| Map.class.isAssignableFrom(rawType)) {
return new SerializeOnlyNonEmptyJsonAdapter<>(moshi.adapter(type, nextAnnotations));
}
return null;
}
};
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/SerializeOnlyNonEmptyJsonAdapter.java
================================================
package com.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Map;
/**
* {@linkplain JsonAdapter} that will not serialize {@code T} when the passed value is empty.
*/
final class SerializeOnlyNonEmptyJsonAdapter<T> extends JsonAdapter<T> {
private final JsonAdapter<T> delegate;
SerializeOnlyNonEmptyJsonAdapter(JsonAdapter<T> delegate) {
this.delegate = delegate;
}
@Override public T fromJson(JsonReader reader) throws IOException {
return delegate.fromJson(reader);
}
@Override public void toJson(JsonWriter writer, T value) throws IOException {
if (isNotEmpty(value)) {
delegate.toJson(writer, value);
} else {
// We'll need to consume this property otherwise we'll get an IllegalArgumentException.
delegate.toJson(writer, null);
}
}
private boolean isNotEmpty(final T value) {
if (value instanceof Collection) {
Collection collection = (Collection) value;
return collection.size() > 0;
} else if (value instanceof Map) {
Map map = (Map) value;
return map.size() > 0;
} else if (value != null) {
return Array.getLength(value) > 0;
}
return false;
}
@Override public String toString() {
return delegate + ".serializeOnlyNonEmpty()";
}
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/Transient.java
================================================
package com.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.util.Set;
/**
* Indicates that the annotated method is transient.
*
* <p>To leverage from {@link Transient} {@link Transient#ADAPTER_FACTORY} must be added
* to your {@linkplain Moshi Moshi instance}:
*
* <pre><code>
* Moshi moshi = new Moshi.Builder()
* .add(Transient.ADAPTER_FACTORY)
* .build();
* </code></pre>
*/
@Documented
@JsonQualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface Transient {
/** Builds an adapter that can process a type annotated with {@link Transient}. */
JsonAdapter.Factory ADAPTER_FACTORY = new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations,
Moshi moshi) {
Set<? extends Annotation> nextAnnotations =
Types.nextAnnotations(annotations, Transient.class);
if (nextAnnotations == null) return null;
return new TransientJsonAdapter<>(moshi.adapter(type, nextAnnotations), false, false);
}
};
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/TransientJsonAdapter.java
================================================
package com.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
import java.io.IOException;
/**
* {@link JsonAdapter} with transient functionality. The consumer can decide to ether serialize or
* deserialize, or make the adapter completely transient.
*/
final class TransientJsonAdapter<T> extends JsonAdapter<T> {
private final JsonAdapter<T> delegate;
private final boolean serialize;
private final boolean deserialize;
TransientJsonAdapter(JsonAdapter<T> delegate, boolean serialize, boolean deserialize) {
this.delegate = delegate;
this.serialize = serialize;
this.deserialize = deserialize;
}
@Override public T fromJson(JsonReader reader) throws IOException {
if (deserialize) {
return delegate.fromJson(reader);
} else {
reader.skipValue();
return null;
}
}
@Override public void toJson(JsonWriter writer, T value) throws IOException {
if (serialize) {
delegate.toJson(writer, value);
} else {
// We'll need to consume this property otherwise we'll get an IllegalArgumentException.
delegate.toJson(writer, null);
}
}
@Override public String toString() {
return delegate + ((serialize && deserialize)
? "" : serialize
? ".serializeOnly()" : deserialize
? ".deserializeOnly()" : ".transient()");
}
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/Util.java
================================================
/*
* Copyright 2014 Square, Inc.
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.JsonQualifier;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
final class Util {
/**
* Checks if {@code annotations} contains {@code jsonQualifier}.
* Returns a pair containing the subset of {@code annotations} without {@code jsonQualifier}
* and the {@code jsonQualified} instance, or null if {@code annotations} does not contain
* {@code jsonQualifier}.
*/
public static <A extends Annotation> Pair<A, Set<Annotation>> nextAnnotations(
Set<? extends Annotation> annotations, Class<A> jsonQualifier) {
if (!jsonQualifier.isAnnotationPresent(JsonQualifier.class)) {
throw new IllegalArgumentException(jsonQualifier + " is not a JsonQualifier.");
}
if (annotations.isEmpty()) {
return null;
}
for (Annotation annotation : annotations) {
if (jsonQualifier.equals(annotation.annotationType())) {
Set<? extends Annotation> delegateAnnotations = new LinkedHashSet<>(annotations);
delegateAnnotations.remove(annotation);
//noinspection unchecked Protected by the if statment.
return new Pair<>((A) annotation, Collections.unmodifiableSet(delegateAnnotations));
}
A delegate = findDelegatedAnnotation(annotation, jsonQualifier);
if (delegate != null) {
Set<? extends Annotation> delegateAnnotations = new LinkedHashSet<>(annotations);
delegateAnnotations.remove(annotation);
return new Pair<>(delegate, Collections.unmodifiableSet(delegateAnnotations));
}
}
return null;
}
private static <A extends Annotation> A findDelegatedAnnotation(
Annotation annotation, Class<A> jsonQualifier) {
for (Annotation delegatedAnnotation : annotation.annotationType().getAnnotations()) {
if (jsonQualifier.equals(delegatedAnnotation.annotationType())) {
//noinspection unchecked
return (A) delegatedAnnotation;
}
}
return null;
}
private Util() {
throw new AssertionError("No instances.");
}
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/Wrapped.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.Moshi;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Set;
import static com.serjltt.moshi.adapters.Util.nextAnnotations;
/**
* Indicates that the annotated type/field should be unwrapped from the provided {@code path}.
*
* <p>For example if a json object is:
* <pre>
* {
* "response": {
* "status": "OK"
* }
* }
* </pre>
* And the consumer only cares about the value of {@code status}, if using retrofit, a service
* method would look like:
*
* <pre><code>
* {@literal @}GET("path/")
* {@literal @}Wrapped({"response", "status"}) Call<String> getStatus();
* </code></pre>
*
* The resulting response returned by {@code response.body()} will be a {@code String} with the
* value {@code "OK"}.
*
* <p>To leverage from {@link Wrapped} {@link Wrapped#ADAPTER_FACTORY} must be
* added to your {@linkplain Moshi Moshi instance}:
*
* <pre><code>
* Moshi moshi = new Moshi.Builder()
* .add(Wrapped.ADAPTER_FACTORY)
* .build();
* </code></pre>
*
* <b>DISCLAIMER: </b> The order of {@linkplain JsonAdapter added json adapters} matters, to ensure
* {@linkplain Wrapped correct un-wrapping} behaviour the adapter factory must be the
* <strong>first</strong> custom adapter added to the {@link Moshi.Builder}.
*/
@Documented
@JsonQualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.FIELD,
ElementType.METHOD,
ElementType.PARAMETER,
ElementType.ANNOTATION_TYPE
})
public @interface Wrapped {
/** The path to the wrapped json path. */
String[] path();
/**
* Indicates if the adapter should fail when the json object was not found at the indicated path.
* Default {@code true}.
*/
boolean failOnNotFound() default true;
/** Builds an adapter that can process a types annotated with {@link Wrapped}. */
JsonAdapter.Factory ADAPTER_FACTORY = new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations,
Moshi moshi) {
Pair<Wrapped, Set<Annotation>> nextAnnotations = nextAnnotations(annotations, Wrapped.class);
if (nextAnnotations == null) return null;
JsonAdapter<Object> adapter = moshi.adapter(type, nextAnnotations.second);
Wrapped wrapped = nextAnnotations.first;
return new WrappedJsonAdapter<>(adapter, wrapped.path(), wrapped.failOnNotFound());
}
};
/** Allows to easily create a new instance of {@link Wrapped} annotation. */
final class Factory {
/** Create a new instance of {@link Wrapped} with the specified JSON path. */
public static Wrapped create(final String... path) {
return create(true, path);
}
/** Create a new instance of {@link Wrapped} with the specified JSON path. */
public static Wrapped create(final boolean failOnNotFound, final String... path) {
return new Wrapped() {
@Override public Class<? extends Annotation> annotationType() {
return Wrapped.class;
}
@Override public String[] path() {
return path;
}
@Override public boolean failOnNotFound() {
return failOnNotFound;
}
@Override public int hashCode() {
int result = Arrays.hashCode(path);
result = 43 * result + (failOnNotFound ? 1 : 0);
return result;
}
@Override public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Wrapped wrapped = (Wrapped) obj;
return Arrays.equals(path, wrapped.path())
&& failOnNotFound == wrapped.failOnNotFound();
}
@Override public String toString() {
return "Wrapped("
+ "path=" + Arrays.asList(path)
+ ", failOnNotFound=" + failOnNotFound
+ ")";
}
};
}
private Factory() {
throw new AssertionError("No instances.");
}
}
}
================================================
FILE: src/main/java/com/serjltt/moshi/adapters/WrappedJsonAdapter.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonDataException;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
import java.io.IOException;
import java.util.Arrays;
/** {@linkplain JsonAdapter} that unwraps the type/field annotated with {@linkplain Wrapped}. */
final class WrappedJsonAdapter<T> extends JsonAdapter<T> {
private final JsonAdapter<T> delegate;
private final String[] path;
private final boolean failOnNotFound;
WrappedJsonAdapter(JsonAdapter<T> delegate, String[] path, boolean failOnNotFound) {
this.delegate = delegate;
this.path = path;
this.failOnNotFound = failOnNotFound;
}
@Override public T fromJson(JsonReader reader) throws IOException {
return fromJson(delegate, reader, path, 0, failOnNotFound);
}
@Override public void toJson(JsonWriter writer, T value) throws IOException {
toJson(delegate, writer, value, path, 0);
}
@Override public String toString() {
return delegate + String.format(".wrapped(%s)", Arrays.asList(path))
+ (failOnNotFound ? ".failOnNotFound()" : "");
}
/**
* Recursively goes through the json and finds the given root. Returns the object wrapped by the
* provided {@code path}.
*/
private static <T> T fromJson(JsonAdapter<T> adapter, JsonReader reader, String[] path,
int index, boolean failOnNotFound) throws IOException {
if (index == path.length) {
//noinspection unchecked This puts full responsibility on the caller.
return adapter.fromJson(reader);
} else {
reader.beginObject();
Exception caughtException = null;
try {
String root = path[index];
while (reader.hasNext()) {
if (reader.nextName().equals(root)) {
if (reader.peek() == JsonReader.Token.NULL) {
// Consumer expects a value, not a null.
if (failOnNotFound) {
throw new JsonDataException(String.format(
"Wrapped Json expected at path: %s. Found null at %s",
Arrays.asList(path), reader.getPath()
));
}
return reader.nextNull();
}
return fromJson(adapter, reader, path, ++index, failOnNotFound);
} else {
reader.skipValue();
}
}
} catch (Exception e) {
caughtException = e;
} finally {
// If the try block throw an exception, rethrow it up the stack.
if (caughtException instanceof IOException) {
//noinspection ThrowFromFinallyBlock
throw (IOException) caughtException;
} else if (caughtException instanceof JsonDataException) {
//noinspection ThrowFromFinallyBlock
throw (JsonDataException) caughtException;
} else if (caughtException != null) {
//noinspection ThrowFromFinallyBlock
throw new AssertionError(caughtException);
}
// If the json has an additional key, that was not red, we ignore it.
while (reader.hasNext()) {
reader.skipValue();
}
// End object, so that other adapters (if any) can proceed.
reader.endObject();
}
throw new JsonDataException(String.format(
"Wrapped Json expected at path: %s. Actual: %s",
Arrays.asList(path), reader.getPath()));
}
}
/**
* Recursively writes the respective roots forming a json object that resembles the {@code path}
* wrapping the type of the {@code adapter}.
*/
private static <T> void toJson(JsonAdapter<T> adapter, JsonWriter writer, T value,
String[] path, int index) throws IOException {
if (value != null || writer.getSerializeNulls()) {
if (index == path.length) {
adapter.toJson(writer, value);
} else {
writer.beginObject();
writer.name(path[index]);
toJson(adapter, writer, value, path, ++index);
writer.endObject();
}
} else {
// If we don't propagate the null value the writer will throw.
writer.nullValue();
}
}
}
================================================
FILE: src/unitTest/java/com/serjltt/moshi/adapters/Custom.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.FromJson;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.ToJson;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Helper annotation (to test if JsonQualifier annotations are propagated further down the stream).
*/
@JsonQualifier
@Retention(RetentionPolicy.RUNTIME) @interface Custom {
/** String adapter, that will append "Custom" on read, and exclude it on write. */
final class CustomAdapter {
@Custom @FromJson String fromJson(String str) {
return str + "Custom";
}
@ToJson String toJson(@Custom String str) {
return str.substring(0, str.length() - "Custom".length());
}
}
}
================================================
FILE: src/unitTest/java/com/serjltt/moshi/adapters/DefaultOnDataMismatchAdapterTest.java
================================================
package com.serjltt.moshi.adapters;
import com.squareup.moshi.Json;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonDataException;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.junit.Test;
import static com.serjltt.moshi.adapters.DefaultOnDataMismatchAdapterTest.Fruit.APPLE;
import static com.serjltt.moshi.adapters.DefaultOnDataMismatchAdapterTest.Fruit.BANANA;
import static org.assertj.core.api.Java6Assertions.assertThat;
public final class DefaultOnDataMismatchAdapterTest {
@Test public void deserializeMismatch() throws IOException {
Fruit fruit = buildMoshi(newFruitFactory()).adapter(Fruit.class).fromJson("\"mango\"");
assertThat(fruit).isNull();
}
@Test public void deserializeMatch() throws IOException {
Fruit fruit = buildMoshi(newFruitFactory()).adapter(Fruit.class).fromJson("\"banana\"");
assertThat(fruit).isEqualTo(BANANA);
}
@Test public void serialize() {
String fruit = buildMoshi(newFruitFactory()).adapter(Fruit.class).toJson(APPLE);
assertThat(fruit).isEqualTo("\"apple\"");
}
@Test public void factorySupportsType() throws Exception {
Type parameterized = Types.newParameterizedType(List.class, String.class);
List<String> fallback = Collections.singletonList("test");
// Build a moshi instance using the adapter under test and one that will throw on each read.
Moshi moshi = buildMoshi(DefaultOnDataMismatchAdapter.newFactory(parameterized, fallback))
.newBuilder()
.add(new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations,
Moshi moshi) {
final JsonAdapter<Object> next = moshi.nextAdapter(this, type, annotations);
return new JsonAdapter<Object>() {
@Override public Object fromJson(JsonReader reader) throws IOException {
throw new JsonDataException("Fail for all types");
}
@Override public void toJson(JsonWriter writer, Object value) throws IOException {
next.toJson(writer, value);
}
};
}
})
.build();
JsonAdapter<List<String>> adapter = moshi.adapter(parameterized);
List<String> fromJson = adapter.fromJson("[]");
assertThat(fromJson)
.isEqualTo(fallback)
.containsExactly("test");
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo("[\"test\"]");
}
@Test public void toStringReflectsInner() {
assertThat(buildMoshi(newFruitFactory()).adapter(Fruit.class).toString())
.isEqualTo("JsonAdapter(com.serjltt.moshi.adapters.DefaultOnDataMismatchAdapterTest$Fruit)"
+ ".nullSafe().defaultOnDatMisMatch(null)");
}
private JsonAdapter.Factory newFruitFactory() {
return DefaultOnDataMismatchAdapter.newFactory(Fruit.class, null);
}
private Moshi buildMoshi(JsonAdapter.Factory factory) {
return new Moshi.Builder()
.add(factory)
.build();
}
enum Fruit {
@Json(name = "banana")BANANA,
@Json(name = "apple")APPLE
}
}
================================================
FILE: src/unitTest/java/com/serjltt/moshi/adapters/DeserializeOnlyJsonAdapterTest.java
================================================
package com.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public final class DeserializeOnlyJsonAdapterTest {
// Lazy adapters work only within the context of moshi.
private final Moshi moshi = new Moshi.Builder()
.add(DeserializeOnly.ADAPTER_FACTORY)
.add(new Custom.CustomAdapter()) // We need to check that other annotations are not lost.
.build();
@Test public void deserializeOnly() throws Exception {
JsonAdapter<Data1> adapter = moshi.adapter(Data1.class);
Data1 fromJson = adapter.fromJson("{\"data\": \"test\"}");
assertThat(fromJson.data).isEqualTo("test");
assertThat(adapter.toJson(fromJson)).isEqualTo("{}");
}
@Test public void factoryMaintainsOtherAnnotations() throws Exception {
JsonAdapter<Data2> adapter = moshi.adapter(Data2.class);
Data2 fromJson = adapter.fromJson("{\"data\": \"test\"}");
assertThat(fromJson.data).isEqualTo("testCustom");
assertThat(adapter.toJson(fromJson)).isEqualTo("{}");
}
@Test public void toStringReflectsInnerAdapter() throws Exception {
JsonAdapter<String> adapter = moshi.adapter(String.class, DeserializeOnly.class);
assertThat(adapter.toString()).isEqualTo("JsonAdapter(String).nullSafe().deserializeOnly()");
}
private static class Data1 {
@DeserializeOnly String data;
}
private static class Data2 {
@DeserializeOnly @Custom String data;
}
}
================================================
FILE: src/unitTest/java/com/serjltt/moshi/adapters/ElementAtJsonAdapterTest.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.Json;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonDataException;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.Moshi;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.junit.Ignore;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
public final class ElementAtJsonAdapterTest {
// Lazy adapters work only within the context of moshi.
private final Moshi moshi = new Moshi.Builder()
.add(ElementAt.ADAPTER_FACTORY)
.add(new Custom.CustomAdapter()) // We need to check that other annotations are not lost.
.build();
@Test public void elementAt() throws Exception {
JsonAdapter<Data> adapter = moshi.adapter(Data.class);
Data fromJson = adapter.fromJson("{\n"
+ " \"obj\": [\n"
+ " \"one\",\n"
+ " \"two\"\n"
+ " ]\n"
+ "}");
assertThat(fromJson.str).isEqualTo("two");
String toJson = adapter.toJson(fromJson);
// The excluded data is lost during parsing
// Adapter under test assumes that the consumer doesn't need that data
assertThat(toJson).isEqualTo("{\"obj\":[\"two\"]}");
}
@Test public void fromJsonOnEmptyArrayReturnsNull() throws Exception {
assertNullReturn("{\n"
+ " \"obj\": []\n"
+ "}");
}
@Test public void fromJsonOnArrayOfSizeOneReturnsNull() throws Exception {
assertNullReturn("{\n"
+ " \"obj\": [\"test\"]\n"
+ "}");
}
@Test public void fromJsonOnNullArrayReturnsNull() throws Exception {
assertNullReturn("{\n"
+ " \"obj\": null\n"
+ "}");
}
@Test public void fromJsonExpectsAnArray() throws Exception {
JsonAdapter<Data> adapter = moshi.adapter(Data.class);
try {
adapter.fromJson("{\n"
+ " \"obj\": \"this_will_throw\"\n"
+ "}");
fail();
} catch (JsonDataException e) {
// Moshi's Collection adapter will throw
assertThat(e).hasMessage("Expected BEGIN_ARRAY but was STRING at path $.obj");
}
}
// Currently there is no way to create an adapter
@Test @Ignore public void factoryMaintainsOtherAnnotations() throws Exception {
JsonAdapter<Data2> adapter = moshi.adapter(Data2.class);
Data2 fromJson = adapter.fromJson("{\n"
+ " \"str\": [\n"
+ " \"test\"\n"
+ " ]\n"
+ "}");
assertThat(fromJson.str).isEqualTo("testCustom");
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo("{\"str\":[\"test\"]}");
}
// This one is redundant, but keeps JaCoCo quiet
@Test public void factoryExpectsOnlyOneAnnotation() {
// A list of fake annotations.
Set<Annotation> annotations = new LinkedHashSet<Annotation>() {
{
add(new Annotation() {
@Override public Class<? extends Annotation> annotationType() {
return Test.class;
}
});
add(new Annotation() {
@Override public Class<? extends Annotation> annotationType() {
return Custom.class;
}
});
}
};
assertThat(ElementAt.ADAPTER_FACTORY.create(String.class, annotations, moshi)).isNull();
// Emulate existing annotation (should also return null).
annotations.add(new ElementAt() {
@Override public Class<? extends Annotation> annotationType() {
return ElementAt.class;
}
@Override public int index() {
return 0;
}
});
assertThat(ElementAt.ADAPTER_FACTORY.create(String.class, annotations, moshi)).isNull();
}
@Test public void toStringReflectsInnerAdapter() throws Exception {
JsonAdapter<String> adapter = moshi.adapter(String.class,
Collections.singleton(new ElementAt() {
@Override public Class<? extends Annotation> annotationType() {
return ElementAt.class;
}
@Override public int index() {
return 2;
}
}));
assertThat(adapter.toString())
.isEqualTo("JsonAdapter(String).nullSafe().collection().nullSafe().elementAt(2)");
}
@Test public void elementAtDelegated() throws Exception {
JsonAdapter<Data3> adapter = moshi.adapter(Data3.class);
Data3 fromJson = adapter.fromJson("{\n"
+ " \"obj\": [\n"
+ " \"one\",\n"
+ " \"two\"\n"
+ " ]\n"
+ "}");
assertThat(fromJson.str).isEqualTo("two");
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo("{\"obj\":[\"two\"]}");
}
@JsonQualifier
@ElementAt(index = 1)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
@interface AlwaysElementAtIndexOne { }
private void assertNullReturn(String string) throws IOException {
JsonAdapter<Data> adapter = moshi.adapter(Data.class);
Data fromJson = adapter.fromJson(string);
assertThat(fromJson.str).isNull();
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo("{\"obj\":[null]}");
}
private static class Data {
@ElementAt(index = 1)
@Json(name = "obj") String str;
}
private static class Data2 {
@ElementAt(index = 0)
@Custom String str;
}
private static class Data3 {
@AlwaysElementAtIndexOne
@Json(name = "obj") String str;
}
}
================================================
FILE: src/unitTest/java/com/serjltt/moshi/adapters/FallbackEnumJsonAdapterTest.java
================================================
/*
* Copyright 2016 Serj Lotutovici
* Copyright (C) 2014 Square, 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.serjltt.moshi.adapters;
import com.squareup.moshi.Json;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonDataException;
import com.squareup.moshi.Moshi;
import java.lang.annotation.Annotation;
import java.util.Collections;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
public final class FallbackEnumJsonAdapterTest {
// Lazy adapters work only within the context of moshi.
private final Moshi moshi = new Moshi.Builder()
.add(FallbackEnum.ADAPTER_FACTORY)
.build();
@Test public void asRegularEnumAdapter() throws Exception {
JsonAdapter<Roshambo> adapter = moshi.adapter(Roshambo.class).lenient();
assertThat(adapter.fromJson("\"ROCK\"")).isEqualTo(Roshambo.ROCK);
assertThat(adapter.toJson(Roshambo.PAPER)).isEqualTo("\"PAPER\"");
// Check annotated value
assertThat(adapter.fromJson("\"scr\"")).isEqualTo(Roshambo.SCISSORS);
assertThat(adapter.toJson(Roshambo.SCISSORS)).isEqualTo("\"scr\"");
}
@Test public void fallbackEnum() throws Exception {
JsonAdapter<Roshambo> adapter = moshi.adapter(Roshambo.class).lenient();
assertThat(adapter.fromJson("\"SPOCK\"")).isEqualTo(Roshambo.UNKNOWN);
}
@Test public void nullEnum() throws Exception {
JsonAdapter<Roshambo> adapter = moshi.adapter(Roshambo.class).lenient();
assertThat(adapter.fromJson("null")).isNull();
assertThat(adapter.toJson(null)).isEqualTo("null");
}
@Test public void throwsOnInvalidFallback() throws Exception {
try {
moshi.adapter(Value.class);
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage(
"No enum constant com.serjltt.moshi.adapters.FallbackEnumJsonAdapterTest.Value.UNK");
}
}
@Test public void toStringReflectsInnerAdapter() throws Exception {
JsonAdapter<Roshambo> adapter = moshi.adapter(Roshambo.class);
assertThat(adapter.toString()).isEqualTo(
"JsonAdapter(com.serjltt.moshi.adapters.FallbackEnumJsonAdapterTest$Roshambo)"
+ ".fallbackEnum(UNKNOWN).nullSafe()");
}
@Test public void ignoresUnannotatedEnums() throws Exception {
JsonAdapter<Regular> adapter = moshi.adapter(Regular.class).lenient();
assertThat(adapter.fromJson("\"ONE\"")).isEqualTo(Regular.ONE);
try {
adapter.fromJson("\"TWO\"");
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected one of [ONE] but was TWO at path $");
}
}
@Test public void factoryIgnoresUnsupportedTypes() throws Exception {
JsonAdapter<?> adapter1 = FallbackEnum.ADAPTER_FACTORY
.create(String.class, Collections.<Annotation>emptySet(), moshi);
assertThat(adapter1).isNull();
JsonAdapter<?> adapter2 = FallbackEnum.ADAPTER_FACTORY
.create(Roshambo.class, Collections.singleton(Wrapped.Factory.create("")), moshi);
assertThat(adapter2).isNull();
}
@FallbackEnum(name = "UNKNOWN") enum Roshambo {
ROCK,
PAPER,
@Json(name = "scr") SCISSORS,
UNKNOWN
}
@FallbackEnum(name = "UNK") enum Value {
@SuppressWarnings("unused") UNKNOWN
}
enum Regular {
ONE
}
}
================================================
FILE: src/unitTest/java/com/serjltt/moshi/adapters/FallbackOnNullJsonAdapterTest.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.FromJson;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.ToJson;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public final class FallbackOnNullJsonAdapterTest {
// Lazy adapters work only within the context of moshi.
private final Moshi moshi = new Moshi.Builder()
.add(FallbackOnNull.ADAPTER_FACTORY)
.add(new Multiply.MultiplyAdapter())
.build();
@Test public void booleanFallbacks() throws Exception {
assertForClass(WrapsBool.class, false, true, "{\"first\":false,\"second\":true}");
}
private static class WrapsBool implements Wrapper<Boolean> {
@FallbackOnNull boolean first;
@FallbackOnNull(fallbackBoolean = true) boolean second;
@Override public Boolean first() {
return first;
}
@Override public Boolean second() {
return second;
}
}
@Test public void byteFallbacks() throws Exception {
assertForClass(WrapsByte.class, Byte.MIN_VALUE, (byte) 42, "{\"first\":128,\"second\":42}");
}
private static class WrapsByte implements Wrapper<Byte> {
@FallbackOnNull byte first;
@FallbackOnNull(fallbackByte = 42) byte second;
@Override public Byte first() {
return first;
}
@Override public Byte second() {
return second;
}
}
@Test public void charFallbacks() throws Exception {
assertForClass(WrapsChar.class, '\u0000', 'a', "{\"first\":\"\\u0000\",\"second\":\"a\"}");
}
private static class WrapsChar implements Wrapper<Character> {
@FallbackOnNull char first;
@FallbackOnNull(fallbackChar = 'a') char second;
@Override public Character first() {
return first;
}
@Override public Character second() {
return second;
}
}
@Test public void doubleFallbacks() throws Exception {
assertForClass(WrapsDouble.class, Double.MIN_VALUE, 12.0,
"{\"first\":4.9E-324,\"second\":12.0}");
}
private static class WrapsDouble implements Wrapper<Double> {
@FallbackOnNull double first;
@FallbackOnNull(fallbackDouble = 12.0) double second;
@Override public Double first() {
return first;
}
@Override public Double second() {
return second;
}
}
@Test public void floatFallbacks() throws Exception {
assertForClass(WrapsFloat.class, Float.MIN_VALUE, 16.0f,
"{\"first\":1.4E-45,\"second\":16.0}");
}
private static class WrapsFloat implements Wrapper<Float> {
@FallbackOnNull float first;
@FallbackOnNull(fallbackFloat = 16.0f) float second;
@Override public Float first() {
return first;
}
@Override public Float second() {
return second;
}
}
@Test public void intFallbacks() throws Exception {
assertForClass(WrapsInt.class, Integer.MIN_VALUE, -1, "{\"first\":-2147483648,\"second\":-1}");
}
@Test public void intFallbacksNoLocaleInfluence() throws Exception {
Locale defaultLocale = Locale.getDefault();
Locale.setDefault(new Locale("tr", "TR"));
assertForClass(WrapsInt.class, Integer.MIN_VALUE, -1, "{\"first\":-2147483648,\"second\":-1}");
Locale.setDefault(defaultLocale);
}
private static class WrapsInt implements Wrapper<Integer> {
@FallbackOnNull int first;
@FallbackOnNull(fallbackInt = -1) int second;
@Override public Integer first() {
return first;
}
@Override public Integer second() {
return second;
}
}
@Test public void longFallbacks() throws Exception {
assertForClass(WrapsLong.class, Long.MIN_VALUE, -113L,
"{\"first\":-9223372036854775808,\"second\":-113}");
}
private static class WrapsLong implements Wrapper<Long> {
@FallbackOnNull long first;
@FallbackOnNull(fallbackLong = -113) long second;
@Override public Long first() {
return first;
}
@Override public Long second() {
return second;
}
}
@Test public void shortFallbacks() throws Exception {
assertForClass(WrapsShort.class, Short.MIN_VALUE, (short) 121,
"{\"first\":-32768,\"second\":121}");
}
private static class WrapsShort implements Wrapper<Short> {
@FallbackOnNull short first;
@FallbackOnNull(fallbackShort = 121) short second;
@Override public Short first() {
return first;
}
@Override public Short second() {
return second;
}
}
@Test public void factoryMaintainsOtherAnnotations() throws Exception {
JsonAdapter<AnotherInt> adapter = moshi.adapter(AnotherInt.class);
AnotherInt fromJson = adapter.fromJson("{\n"
+ " \"willFallback\": null,\n"
+ " \"willMultiply\": 3\n"
+ "}");
assertThat(fromJson.willFallback).isEqualTo(2);
assertThat(fromJson.willMultiply).isEqualTo(6);
String toJson = adapter.toJson(fromJson);
// Both values should be serialized by the Multiply json adapter.
assertThat(toJson).isEqualTo("{\"willFallback\":1,\"willMultiply\":3}");
}
private static class AnotherInt {
@FallbackOnNull(fallbackInt = 2) @Multiply int willFallback;
@FallbackOnNull(fallbackInt = 2) @Multiply int willMultiply;
}
@Test public void factoryIgnoresNonPrimitiveTypes() {
List<Class<?>> classes = new ArrayList<Class<?>>() {
{
add(Boolean.class);
add(Byte.class);
add(Character.class);
add(Double.class);
add(Float.class);
add(Integer.class);
add(Long.class);
add(Short.class);
add(String.class);
add(Object.class);
}
};
for (Class<?> cls : classes) {
assertThat(FallbackOnNull.ADAPTER_FACTORY.create(cls, ANNOTATIONS, moshi)).isNull();
}
}
@Test public void fallbackOnNullIsDelegated() throws Exception {
JsonAdapter<AndAnotherInt> adapter = moshi.adapter(AndAnotherInt.class);
AndAnotherInt fromJson = adapter.fromJson("{\n"
+ " \"willFallback\": null\n"
+ "}");
assertThat(fromJson.willFallback).isEqualTo(2);
}
private static class AndAnotherInt {
@AlwaysFallBackToTwoOnNull int willFallback;
}
@JsonQualifier
@FallbackOnNull(fallbackInt = 2)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
@interface AlwaysFallBackToTwoOnNull { }
@Test public void toStringReflectsInnerAdapter() throws Exception {
JsonAdapter<Integer> adapter = moshi.adapter(int.class, ANNOTATIONS);
assertThat(adapter.toString())
.isEqualTo("JsonAdapter(Integer).fallbackOnNull(fallbackInt=-1)");
}
private static final Set<? extends Annotation> ANNOTATIONS = Collections.singleton(
new FallbackOnNull() {
@Override public Class<? extends Annotation> annotationType() {
return FallbackOnNull.class;
}
@Override public boolean fallbackBoolean() {
return false;
}
@Override public byte fallbackByte() {
return 0;
}
@Override public char fallbackChar() {
return 0;
}
@Override public double fallbackDouble() {
return 0;
}
@Override public float fallbackFloat() {
return 0;
}
@Override public int fallbackInt() {
return -1; // Only this method will be taken into account
}
@Override public long fallbackLong() {
return 0;
}
@Override public short fallbackShort() {
return 0;
}
});
private <T extends Wrapper<P>, P> void assertForClass(Class<T> cls, P first, P second,
String asJson) throws IOException {
JsonAdapter<T> adapter = moshi.adapter(cls);
T fromJson = adapter.fromJson("{\n"
+ " \"first\": null,\n"
+ " \"second\": null\n"
+ "}");
assertThat(fromJson.first()).isEqualTo(first);
assertThat(fromJson.second()).isEqualTo(second);
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo(asJson);
}
private interface Wrapper<P> {
P first();
P second();
}
@JsonQualifier
@Retention(RetentionPolicy.RUNTIME) private @interface Multiply {
final class MultiplyAdapter {
@Multiply @FromJson int fromJson(int val) {
return val * 2;
}
@ToJson int toJson(@Multiply int val) {
return val / 2;
}
}
}
}
================================================
FILE: src/unitTest/java/com/serjltt/moshi/adapters/FilterNullsJsonAdapterTest.java
================================================
package com.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.junit.Test;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Java6Assertions.assertThat;
public final class FilterNullsJsonAdapterTest {
// Lazy adapters work only within the context of moshi.
private final Moshi moshi = new Moshi.Builder()
.add(FilterNulls.ADAPTER_FACTORY)
.add(new Custom.CustomAdapter()) // We need to check that other annotations are not lost.
.build();
@Test public void noNullValues() throws Exception {
JsonAdapter<List<String>> adapter = moshi.adapter(Types.newParameterizedType(List.class,
String.class), FilterNulls.class);
List<String> fromJson = adapter.fromJson("[\"apple\",\"banana\"]");
assertThat(fromJson).containsExactly("apple", "banana");
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo("[\"apple\",\"banana\"]");
}
@Test public void nullValues() throws Exception {
JsonAdapter<List<String>> adapter = moshi.adapter(Types.newParameterizedType(List.class,
String.class), FilterNulls.class);
List<String> fromJson = adapter.fromJson("[\"apple\",\"banana\",null]");
assertThat(fromJson).containsExactly("apple", "banana");
String toJson = adapter.toJson(new ArrayList<>(asList("apple", "banana", null)));
assertThat(toJson).isEqualTo("[\"apple\",\"banana\"]");
}
@Test public void nullList() throws Exception {
JsonAdapter<List<String>> adapter = moshi.adapter(Types.newParameterizedType(List.class,
String.class), FilterNulls.class);
List<String> fromJson = adapter.fromJson("null");
assertThat(fromJson).isNull();
String toJson = adapter.toJson(null);
assertThat(toJson).isEqualTo("null");
}
@Test public void toStringReflectsInnerAdapter() throws Exception {
JsonAdapter<String> adapter = moshi.adapter(Types.newParameterizedType(Collection.class,
String.class), FilterNulls.class);
assertThat(adapter.toString())
.isEqualTo("JsonAdapter(String).nullSafe().collection().nullSafe().filterNulls()");
}
}
================================================
FILE: src/unitTest/java/com/serjltt/moshi/adapters/FirstElementJsonAdapterTest.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.Json;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonDataException;
import com.squareup.moshi.Moshi;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.LinkedHashSet;
import java.util.Set;
import org.junit.Ignore;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
public final class FirstElementJsonAdapterTest {
// Lazy adapters work only within the context of moshi.
private final Moshi moshi = new Moshi.Builder()
.add(FirstElement.ADAPTER_FACTORY)
.add(new Custom.CustomAdapter()) // We need to check that other annotations are not lost.
.build();
@Test public void first() throws Exception {
JsonAdapter<Data> adapter = moshi.adapter(Data.class);
Data fromJson = adapter.fromJson("{\n"
+ " \"obj\": [\n"
+ " \"one\",\n"
+ " \"two\"\n"
+ " ]\n"
+ "}");
assertThat(fromJson.str).isEqualTo("one");
String toJson = adapter.toJson(fromJson);
// The excluded data is lost during parsing
// Adapter under test assumes that the consumer doesn't need that data
assertThat(toJson).isEqualTo("{\"obj\":[\"one\"]}");
}
@Test public void fromJsonOnEmptyArrayReturnsNull() throws Exception {
assertNullReturn("{\n"
+ " \"obj\": []\n"
+ "}");
}
@Test public void fromJsonOnNullArrayReturnsNull() throws Exception {
assertNullReturn("{\n"
+ " \"obj\": null\n"
+ "}");
}
@Test public void fromJsonExpectsAnArray() throws Exception {
JsonAdapter<Data> adapter = moshi.adapter(Data.class);
try {
adapter.fromJson("{\n"
+ " \"obj\": \"this_will_throw\"\n"
+ "}");
fail();
} catch (JsonDataException e) {
// Moshi's Collection adapter will throw
assertThat(e).hasMessage("Expected BEGIN_ARRAY but was STRING at path $.obj");
}
}
// Currently there is no way to create an adapter
@Test @Ignore public void factoryMaintainsOtherAnnotations() throws Exception {
JsonAdapter<Data2> adapter = moshi.adapter(Data2.class);
Data2 fromJson = adapter.fromJson("{\n"
+ " \"str\": [\n"
+ " \"test\"\n"
+ " ]\n"
+ "}");
assertThat(fromJson.str).isEqualTo("testCustom");
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo("{\"str\":[\"test\"]}");
}
// This one is redundant, but keeps JaCoCo quiet
@Test public void factoryExpectsOnlyOneAnnotation() throws Exception {
// A list of fake annotations.
Set<Annotation> annotations = new LinkedHashSet<Annotation>() {
{
add(new Annotation() {
@Override public Class<? extends Annotation> annotationType() {
return Test.class;
}
});
add(new Annotation() {
@Override public Class<? extends Annotation> annotationType() {
return Custom.class;
}
});
}
};
assertThat(FirstElement.ADAPTER_FACTORY.create(String.class, annotations, moshi)).isNull();
// Emulate existing annotation (should also return null).
annotations.add(new Annotation() {
@Override public Class<? extends Annotation> annotationType() {
return FirstElement.class;
}
});
assertThat(FirstElement.ADAPTER_FACTORY.create(String.class, annotations, moshi)).isNull();
}
@Test public void toStringReflectsInnerAdapter() throws Exception {
JsonAdapter<String> adapter = moshi.adapter(String.class, FirstElement.class);
assertThat(adapter.toString())
.isEqualTo("JsonAdapter(String).nullSafe().collection().nullSafe().elementAt(0)");
}
private void assertNullReturn(String string) throws IOException {
JsonAdapter<Data> adapter = moshi.adapter(Data.class);
Data fromJson = adapter.fromJson(string);
assertThat(fromJson.str).isNull();
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo("{\"obj\":[null]}");
}
private static class Data {
@FirstElement
@Json(name = "obj") String str;
}
private static class Data2 {
@FirstElement
@Custom String str;
}
}
================================================
FILE: src/unitTest/java/com/serjltt/moshi/adapters/LastElementJsonAdapterTest.java
================================================
package com.serjltt.moshi.adapters;
import com.squareup.moshi.Json;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonDataException;
import com.squareup.moshi.Moshi;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.LinkedHashSet;
import java.util.Set;
import org.junit.Ignore;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
public final class LastElementJsonAdapterTest {
// Lazy adapters work only within the context of moshi.
private final Moshi moshi = new Moshi.Builder()
.add(LastElement.ADAPTER_FACTORY)
.add(new Custom.CustomAdapter()) // We need to check that other annotations are not lost.
.build();
@Test public void last() throws Exception {
JsonAdapter<LastElementJsonAdapterTest.Data>
adapter = moshi.adapter(LastElementJsonAdapterTest.Data.class);
LastElementJsonAdapterTest.Data fromJson = adapter.fromJson("{\n"
+ " \"obj\": [\n"
+ " \"one\",\n"
+ " \"two\"\n"
+ " ]\n"
+ "}");
assertThat(fromJson.str).isEqualTo("two");
String toJson = adapter.toJson(fromJson);
// The excluded data is lost during parsing
// Adapter under test assumes that the consumer doesn't need that data
assertThat(toJson).isEqualTo("{\"obj\":[\"two\"]}");
}
@Test public void fromJsonOnEmptyArrayReturnsNull() throws Exception {
assertNullReturn("{\n"
+ " \"obj\": []\n"
+ "}");
}
@Test public void fromJsonOnNullArrayReturnsNull() throws Exception {
assertNullReturn("{\n"
+ " \"obj\": null\n"
+ "}");
}
@Test public void fromJsonExpectsAnArray() throws Exception {
JsonAdapter<LastElementJsonAdapterTest.Data> adapter = moshi.adapter(Data.class);
try {
adapter.fromJson("{\n"
+ " \"obj\": \"this_will_throw\"\n"
+ "}");
fail();
} catch (JsonDataException e) {
// Moshi's Collection adapter will throw
assertThat(e).hasMessage("Expected BEGIN_ARRAY but was STRING at path $.obj");
}
}
// Currently there is no way to create an adapter
@Test @Ignore public void factoryMaintainsOtherAnnotations() throws Exception {
JsonAdapter<LastElementJsonAdapterTest.Data2> adapter = moshi.adapter(Data2.class);
LastElementJsonAdapterTest.Data2 fromJson = adapter.fromJson("{\n"
+ " \"str\": [\n"
+ " \"test\"\n"
+ " ]\n"
+ "}");
assertThat(fromJson.str).isEqualTo("testCustom");
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo("{\"str\":[\"test\"]}");
}
// This one is redundant, but keeps JaCoCo quiet
@Test public void factoryExpectsOnlyOneAnnotation() throws Exception {
// A list of fake annotations.
Set<Annotation> annotations = new LinkedHashSet<Annotation>() {
{
add(new Annotation() {
@Override public Class<? extends Annotation> annotationType() {
return Test.class;
}
});
add(new Annotation() {
@Override public Class<? extends Annotation> annotationType() {
return Custom.class;
}
});
}
};
assertThat(LastElement.ADAPTER_FACTORY.create(String.class, annotations, moshi)).isNull();
// Emulate existing annotation (should also return null).
annotations.add(new Annotation() {
@Override public Class<? extends Annotation> annotationType() {
return LastElement.class;
}
});
assertThat(LastElement.ADAPTER_FACTORY.create(String.class, annotations, moshi)).isNull();
}
@Test public void toStringReflectsInnerAdapter() throws Exception {
JsonAdapter<String> adapter = moshi.adapter(String.class, LastElement.class);
assertThat(adapter.toString())
.isEqualTo("JsonAdapter(String).nullSafe().collection().nullSafe().lastElement()");
}
private void assertNullReturn(String string) throws IOException {
JsonAdapter<LastElementJsonAdapterTest.Data> adapter = moshi.adapter(Data.class);
LastElementJsonAdapterTest.Data fromJson = adapter.fromJson(string);
assertThat(fromJson.str).isNull();
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo("{\"obj\":[null]}");
}
private static class Data {
@LastElement
@Json(name = "obj") String str;
}
private static class Data2 {
@LastElement
@Custom String str;
}
}
================================================
FILE: src/unitTest/java/com/serjltt/moshi/adapters/SerializeNullsJsonAdapterTest.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonWriter;
import com.squareup.moshi.Moshi;
import okio.Buffer;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public final class SerializeNullsJsonAdapterTest {
// Lazy adapters work only within the context of moshi.
private final Moshi moshi = new Moshi.Builder()
.add(SerializeNulls.ADAPTER_FACTORY)
.add(new Custom.CustomAdapter()) // We need to check that other annotations are not lost.
.build();
@Test public void serializesNulls() throws Exception {
JsonAdapter<Data1> adapter = moshi.adapter(Data1.class);
Data1 fromJson = adapter.fromJson("{\n"
+ " \"data\": null\n"
+ "}");
assertThat(fromJson.data).isNull();
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo("{\"data\":null}");
}
@Test public void factoryMaintainsOtherAnnotations() throws Exception {
JsonAdapter<Data2> adapter = moshi.adapter(Data2.class);
Data2 fromJson = adapter.fromJson("{\n"
+ " \"data\": \"val\"\n"
+ "}");
assertThat(fromJson.data).isEqualTo("valCustom");
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo("{\"data\":\"val\"}");
Data2 data = new Data2();
data.data = null;
toJson = adapter.toJson(data);
assertThat(toJson).isEqualTo("{\"data\":null}");
}
@Test public void maintainsPreviousSerializationValue() throws Exception {
JsonAdapter<Data1> adapter = moshi.adapter(Data1.class);
Data1 data1 = new Data1();
JsonWriter writer1 = JsonWriter.of(new Buffer());
writer1.setSerializeNulls(true);
adapter.toJson(writer1, data1);
assertThat(writer1.getSerializeNulls()).isTrue();
JsonWriter writer2 = JsonWriter.of(new Buffer());
writer2.setSerializeNulls(false);
adapter.toJson(writer2, data1);
assertThat(writer2.getSerializeNulls()).isFalse();
}
@Test public void toStringReflectsInnerAdapter() throws Exception {
JsonAdapter<String> adapter = moshi.adapter(String.class, SerializeNulls.class);
assertThat(adapter.toString())
.isEqualTo("JsonAdapter(String).nullSafe().serializeNulls()");
}
private static class Data1 {
@SerializeNulls String data;
}
private static class Data2 {
@Custom
@SerializeNulls String data;
}
}
================================================
FILE: src/unitTest/java/com/serjltt/moshi/adapters/SerializeOnlyJsonAdapterTest.java
================================================
package com.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public final class SerializeOnlyJsonAdapterTest {
// Lazy adapters work only within the context of moshi.
private final Moshi moshi = new Moshi.Builder()
.add(SerializeOnly.ADAPTER_FACTORY)
.add(SerializeNulls.ADAPTER_FACTORY)
.add(new Custom.CustomAdapter()) // We need to check that other annotations are not lost.
.build();
@Test public void serializeOnly() throws Exception {
JsonAdapter<Data1> adapter = moshi.adapter(Data1.class);
Data1 fromJson = adapter.fromJson("{\"data\": \"test\"}");
assertThat(fromJson.data).isNull();
fromJson.data = "1234";
assertThat(adapter.toJson(fromJson)).isEqualTo("{\"data\":\"1234\"}");
}
@Test public void factoryMaintainsOtherAnnotations() throws Exception {
JsonAdapter<Data2> adapter = moshi.adapter(Data2.class);
Data2 fromJson = adapter.fromJson("{\"data\": \"test\"}");
assertThat(fromJson.data).isNull();
fromJson.data = "1234Custom";
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo("{\"data\":\"1234\"}");
Data2 data = new Data2();
data.data = null;
toJson = adapter.toJson(data);
assertThat(toJson).isEqualTo("{\"data\":null}");
}
@Test public void toStringReflectsInnerAdapter() throws Exception {
JsonAdapter<String> adapter = moshi.adapter(String.class, SerializeOnly.class);
assertThat(adapter.toString()).isEqualTo("JsonAdapter(String).nullSafe().serializeOnly()");
}
private static class Data1 {
@SerializeOnly String data;
}
private static class Data2 {
@Custom @SerializeOnly @SerializeNulls String data;
}
}
================================================
FILE: src/unitTest/java/com/serjltt/moshi/adapters/SerializeOnlyNonEmptyJsonAdapterTest.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public final class SerializeOnlyNonEmptyJsonAdapterTest {
// Lazy adapters work only within the context of moshi.
private final Moshi moshi = new Moshi.Builder()
.add(SerializeOnlyNonEmpty.ADAPTER_FACTORY)
.add(new Custom.CustomAdapter()) // We need to check that other annotations are not lost.
.build();
@Test public void serializesOnlyNonEmptyCustomArray() throws Exception {
JsonAdapter<Data1> adapter = moshi.adapter(Data1.class);
Data1 fromJson = adapter.fromJson("{\n"
+ "\"customArray\": [{"
+ "\"data\":\"blub\""
+ "}]\n"
+ "}");
assertThat(fromJson.customArray).isNotNull().hasSize(1);
assertThat(fromJson.customArray[0].data).isEqualTo("blub");
fromJson.customArray = new CustomType[0];
assertThat(adapter.toJson(fromJson)).isEqualTo("{}");
fromJson.customArray = new CustomType[] { new CustomType("blub") };
assertThat(adapter.toJson(fromJson)).isEqualTo("{\"customArray\":[{\"data\":\"blub\"}]}");
}
@Test public void serializesOnlyNonEmptyByteArray() throws Exception {
JsonAdapter<Data1> adapter = moshi.adapter(Data1.class);
Data1 fromJson = adapter.fromJson("{\n"
+ "\"byteArray\": [1]\n"
+ "}");
assertThat(fromJson.byteArray).containsExactly((byte) 1);
fromJson.byteArray = new byte[0];
assertThat(adapter.toJson(fromJson)).isEqualTo("{}");
fromJson.byteArray = new byte[] { 5 };
assertThat(adapter.toJson(fromJson)).isEqualTo("{\"byteArray\":[5]}");
}
@Test public void serializesOnlyNonEmptyCharArray() throws Exception {
JsonAdapter<Data1> adapter = moshi.adapter(Data1.class);
Data1 fromJson = adapter.fromJson("{\n"
+ "\"charArray\": [\"A\"]\n"
+ "}");
assertThat(fromJson.charArray).containsExactly((char) 65);
fromJson.charArray = new char[0];
assertThat(adapter.toJson(fromJson)).isEqualTo("{}");
fromJson.charArray = new char[] { 65 };
assertThat(adapter.toJson(fromJson)).isEqualTo("{\"charArray\":[\"A\"]}");
}
@Test public void serializesOnlyNonEmptyShortArray() throws Exception {
JsonAdapter<Data1> adapter = moshi.adapter(Data1.class);
Data1 fromJson = adapter.fromJson("{\n"
+ "\"shortArray\": [1]\n"
+ "}");
assertThat(fromJson.shortArray).containsExactly((short) 1);
fromJson.shortArray = new short[0];
assertThat(adapter.toJson(fromJson)).isEqualTo("{}");
fromJson.shortArray = new short[] { 5 };
assertThat(adapter.toJson(fromJson)).isEqualTo("{\"shortArray\":[5]}");
}
@Test public void serializesOnlyNonEmptyIntArray() throws Exception {
JsonAdapter<Data1> adapter = moshi.adapter(Data1.class);
Data1 fromJson = adapter.fromJson("{\n"
+ "\"intArray\": [1]\n"
+ "}");
assertThat(fromJson.intArray).containsExactly(1);
fromJson.intArray = new int[0];
assertThat(adapter.toJson(fromJson)).isEqualTo("{}");
fromJson.intArray = new int[] { 5 };
assertThat(adapter.toJson(fromJson)).isEqualTo("{\"intArray\":[5]}");
}
@Test public void serializesOnlyNonEmptyLongArray() throws Exception {
JsonAdapter<Data1> adapter = moshi.adapter(Data1.class);
Data1 fromJson = adapter.fromJson("{\n"
+ "\"longArray\": [1]\n"
+ "}");
assertThat(fromJson.longArray).containsExactly(1L);
fromJson.longArray = new long[0];
assertThat(adapter.toJson(fromJson)).isEqualTo("{}");
fromJson.longArray = new long[] { 5L };
assertThat(adapter.toJson(fromJson)).isEqualTo("{\"longArray\":[5]}");
}
@Test public void serializesOnlyNonEmptyFloatArray() throws Exception {
JsonAdapter<Data1> adapter = moshi.adapter(Data1.class);
Data1 fromJson = adapter.fromJson("{\n"
+ "\"floatArray\": [1.0]\n"
+ "}");
assertThat(fromJson.floatArray).containsExactly(1.f);
fromJson.floatArray = new float[0];
assertThat(adapter.toJson(fromJson)).isEqualTo("{}");
fromJson.floatArray = new float[] { 5f };
assertThat(adapter.toJson(fromJson)).isEqualTo("{\"floatArray\":[5.0]}");
}
@Test public void serializesOnlyNonEmptyDoubleArray() throws Exception {
JsonAdapter<Data1> adapter = moshi.adapter(Data1.class);
Data1 fromJson = adapter.fromJson("{\n"
+ "\"doubleArray\": [1.0]\n"
+ "}");
assertThat(fromJson.doubleArray).containsExactly(1.f);
fromJson.doubleArray = new double[0];
assertThat(adapter.toJson(fromJson)).isEqualTo("{}");
fromJson.doubleArray = new double[] { 5f };
assertThat(adapter.toJson(fromJson)).isEqualTo("{\"doubleArray\":[5.0]}");
}
@Test public void serializesOnlyNonEmptyBooleanArray() throws Exception {
JsonAdapter<Data1> adapter = moshi.adapter(Data1.class);
Data1 fromJson = adapter.fromJson("{\n"
+ "\"booleanArray\": [false]\n"
+ "}");
assertThat(fromJson.booleanArray).containsExactly(false);
fromJson.booleanArray = new boolean[0];
assertThat(adapter.toJson(fromJson)).isEqualTo("{}");
fromJson.booleanArray = new boolean[] { false };
assertThat(adapter.toJson(fromJson)).isEqualTo("{\"booleanArray\":[false]}");
}
@Test public void serializesOnlyNonEmptyStringArray() throws Exception {
JsonAdapter<Data1> adapter = moshi.adapter(Data1.class);
Data1 fromJson = adapter.fromJson("{\n"
+ "\"stringArray\": [\"blub\"]\n"
+ "}");
assertThat(fromJson.stringArray).containsExactly("blub");
fromJson.stringArray = new String[0];
assertThat(adapter.toJson(fromJson)).isEqualTo("{}");
fromJson.stringArray = new String[] { "blub" };
assertThat(adapter.toJson(fromJson)).isEqualTo("{\"stringArray\":[\"blub\"]}");
}
@Test public void serializesOnlyNonEmptyCollection() throws Exception {
JsonAdapter<Data1> adapter = moshi.adapter(Data1.class);
Data1 fromJson = adapter.fromJson("{\n"
+ "\"collection\": [\"blub\"]\n"
+ "}");
assertThat(fromJson.collection).containsExactly("blub");
fromJson.collection = new ArrayList<>(0);
assertThat(adapter.toJson(fromJson)).isEqualTo("{}");
fromJson.collection.add("blub");
assertThat(adapter.toJson(fromJson)).isEqualTo("{\"collection\":[\"blub\"]}");
}
@Test public void serializesOnlyNonEmptyMap() throws Exception {
JsonAdapter<Data1> adapter = moshi.adapter(Data1.class);
Data1 fromJson = adapter.fromJson("{\n"
+ "\"map\": {"
+ "\"email\":\"blub\"\n"
+ "}\n"
+ "}");
assertThat(fromJson.map).containsEntry("email", "blub");
fromJson.map = new HashMap<>();
assertThat(adapter.toJson(fromJson)).isEqualTo("{}");
fromJson.map.put("email", "blub");
assertThat(adapter.toJson(fromJson)).isEqualTo("{\"map\":{\"email\":\"blub\"}}");
}
@Test public void toStringReflectsInnerAdapter() throws Exception {
JsonAdapter<String> adapter = moshi.adapter(String[].class, SerializeOnlyNonEmpty.class);
assertThat(adapter.toString())
.isEqualTo("JsonAdapter(String).nullSafe().array().nullSafe().serializeOnlyNonEmpty()");
}
static class Data1 {
@SerializeOnlyNonEmpty CustomType[] customArray;
@SerializeOnlyNonEmpty byte[] byteArray;
@SerializeOnlyNonEmpty char[] charArray;
@SerializeOnlyNonEmpty short[] shortArray;
@SerializeOnlyNonEmpty int[] intArray;
@SerializeOnlyNonEmpty long[] longArray;
@SerializeOnlyNonEmpty float[] floatArray;
@SerializeOnlyNonEmpty double[] doubleArray;
@SerializeOnlyNonEmpty boolean[] booleanArray;
@SerializeOnlyNonEmpty String[] stringArray;
@SerializeOnlyNonEmpty Collection<String> collection;
@SerializeOnlyNonEmpty Map<String, String> map;
}
static class CustomType {
final String data;
CustomType(final String data) {
this.data = data;
}
}
}
================================================
FILE: src/unitTest/java/com/serjltt/moshi/adapters/WrappedJsonAdapterTest.java
================================================
/*
* Copyright 2016 Serj Lotutovici
*
* 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.serjltt.moshi.adapters;
import com.pushtorefresh.private_constructor_checker.PrivateConstructorChecker;
import com.squareup.moshi.FromJson;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonDataException;
import com.squareup.moshi.JsonEncodingException;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.ToJson;
import com.squareup.moshi.Types;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
public final class WrappedJsonAdapterTest {
// Lazy adapters work only within the context of moshi.
private final Moshi moshi = new Moshi.Builder()
.add(Wrapped.ADAPTER_FACTORY)
.add(new Custom.CustomAdapter()) // We need to check that other annotations are not lost.
.add(new ThrowingAdapter()) // We need to check that exceptions are propagated correctly.
.build();
@Test public void oneObject() throws Exception {
JsonAdapter<Data2> adapter = moshi.adapter(Data2.class);
Data2 fromJson = adapter.fromJson("{\n"
+ " \"data\": {\n"
+ " \"1\": {\n"
+ " \"2\": {\n"
+ " \"str\": \"test\",\n"
+ " \"val\": 42\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}");
assertThat(fromJson).isNotNull();
assertThat(fromJson.data.str).isEqualTo("test");
assertThat(fromJson.data.val).isEqualTo(42);
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo("{\"data\":{\"1\":{\"2\":{\"str\":\"test\",\"val\":42}}}}");
}
@Test public void arrayOfObjects() throws Exception {
JsonAdapter<List<Data2>> adapter = moshi.adapter(
Types.newParameterizedType(List.class, Data2.class));
List<Data2> fromJson = adapter.fromJson("[\n"
+ " {\n"
+ " \"data\": {\n"
+ " \"1\": {\n"
+ " \"2\": {\n"
+ " \"str\": \"funny\",\n"
+ " \"val\": 42\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"data\": {\n"
+ " \"1\": {\n"
+ " \"2\": {\n"
+ " \"str\": \"prime\",\n"
+ " \"val\": 43\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "]");
assertThat(fromJson.get(0).data.str).isEqualTo("funny");
assertThat(fromJson.get(0).data.val).isEqualTo(42);
assertThat(fromJson.get(1).data.str).isEqualTo("prime");
assertThat(fromJson.get(1).data.val).isEqualTo(43);
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo("["
+ "{\"data\":{\"1\":{\"2\":{\"str\":\"funny\",\"val\":42}}}},"
+ "{\"data\":{\"1\":{\"2\":{\"str\":\"prime\",\"val\":43}}}}"
+ "]");
}
@Test public void failOnNotFound() throws Exception {
JsonAdapter<Data2> adapter = moshi.adapter(Data2.class);
try {
adapter.fromJson("{\n"
+ " \"data\": {\n"
+ " \"1\": {\n"
+ " \"2\": null\n"
+ " }\n"
+ " }\n"
+ "}");
fail();
} catch (JsonDataException ex) {
assertThat(ex).hasMessage(
"Wrapped Json expected at path: [1, 2]. Found null at $.data.1.2");
}
}
@Test public void failOnNotFound2() throws Exception {
JsonAdapter<Data2> adapter = moshi.adapter(Data2.class);
try {
adapter.fromJson("{\n"
+ " \"data\": {\n"
+ " \"1\": null\n"
+ " }\n"
+ "}");
fail();
} catch (JsonDataException ex) {
assertThat(ex).hasMessage(
"Wrapped Json expected at path: [1, 2]. Found null at $.data.1");
}
}
@Test public void failOnNotFoundFalse() throws Exception {
JsonAdapter<String> adapter = moshi.adapter(String.class,
Collections.singleton(Wrapped.Factory.create(false, "one")));
String fromJson = adapter.fromJson("{\"one\":null}");
assertThat(fromJson).isEqualTo(null);
}
@Test public void notNullSafe() throws Exception {
JsonAdapter<Data2> adapter = moshi.adapter(Data2.class);
try {
adapter.fromJson("{\n"
+ " \"data\": null\n"
+ "}");
fail();
} catch (JsonDataException expected) {
}
Data2 data2 = new Data2();
String toJson = adapter.toJson(data2);
assertThat(toJson).isEqualTo("{}");
toJson = adapter.serializeNulls().toJson(data2);
assertThat(toJson).isEqualTo("{\"data\":{\"1\":{\"2\":null}}}");
}
@Test public void fromJsonSkipsNonPathValues() throws Exception {
JsonAdapter<Data2> adapter = moshi.adapter(Data2.class);
Data2 fromJson = adapter.fromJson("{\n"
+ " \"data\": {\n"
+ " \"should_be_skipped\": null,\n"
+ " \"1\": {\n"
+ " \"2\": {\n"
+ " \"str\": \"works\",\n"
+ " \"val\": 11\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " }\n"
+ "}");
assertThat(fromJson.data.str).isEqualTo("works");
assertThat(fromJson.data.val).isEqualTo(11);
}
@Test public void fromJsonRemainingPathValues() throws Exception {
JsonAdapter<Data2> adapter = moshi.adapter(Data2.class);
Data2 fromJson = adapter.fromJson("{\n"
+ " \"data\": {\n"
+ " \"1\": {\n"
+ " \"2\": {\n"
+ " \"str\": \"works\",\n"
+ " \"val\": 11\n"
+ " }\n"
+ " },\n"
+ " \"should_be_skipped1\": null,\n"
+ " \"should_be_skipped2\": null\n"
+ " }\n"
+ "}");
assertThat(fromJson.data.str).isEqualTo("works");
assertThat(fromJson.data.val).isEqualTo(11);
}
@Test public void fromJsonOnIncorrectPath() throws Exception {
JsonAdapter<Data2> adapter = moshi.adapter(Data2.class);
try {
adapter.fromJson("{\n"
+ " \"data\": {\n"
+ " \"2\": {\n"
+ " \"1\": null\n"
+ " }\n"
+ " }\n"
+ "}");
fail();
} catch (JsonDataException e) {
assertThat(e).hasMessage("Wrapped Json expected at path: [1, 2]. Actual: $.data");
}
}
@Test public void fromJsonDoesNotSwallowIOExceptions() throws Exception {
JsonAdapter<Data4> adapter = moshi.adapter(Data4.class);
try {
adapter.fromJson("{\n"
+ " \"th\": {\n"
+ " \"1\": \"this_will_throw\"\n"
+ " }\n"
+ "}");
fail();
} catch (IOException e) {
assertThat(e).hasMessage("ThrowingAdapter.fromJson");
}
}
@Test public void fromJsonDoesNotSwallowJsonEncodingExceptions() throws Exception {
JsonAdapter<Data2> adapter = moshi.adapter(Data2.class);
try {
adapter.fromJson("{\n"
+ " \"data\": {\n"
+ " \"1\": {\n"
+ " \"2\": {\n"
+ " \"str\": \"valid\",\n"
+ " \"val\": NaN\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}");
fail();
} catch (JsonEncodingException e) {
assertThat(e).hasMessage(
"Use JsonReader.setLenient(true) to accept malformed JSON at path $.data.1.2.val");
}
}
@Test public void fromJsonDoesNotSwallowJsonDataExceptions() throws Exception {
JsonAdapter<Data3> adapter = moshi.adapter(Data3.class);
try {
adapter.fromJson("{\n"
+ " \"str\": {\n"
+ " \"1\": false\n"
+ " }\n"
+ "}");
fail();
} catch (JsonDataException e) {
assertThat(e).hasMessage("Expected a string but was BOOLEAN at path $.str.1");
}
}
@Test public void toJsonDoesNotSwallowExceptions() throws Exception {
JsonAdapter<Data4> adapter = moshi.adapter(Data4.class);
Data4 data4 = new Data4();
data4.th = new Throws();
try {
adapter.toJson(data4);
fail();
} catch (Throwable e) {
// Moshi wraps write exceptions in an AssertionError
assertThat(e.getCause()).hasMessage("ThrowingAdapter.toJson");
}
}
@Test public void factoryMaintainsOtherAnnotations() throws Exception {
JsonAdapter<Data3> adapter = moshi.adapter(Data3.class);
Data3 fromJson = adapter.fromJson("{\n"
+ " \"str\": {\n"
+ " \"1\": \"test\"\n"
+ " }\n"
+ "}");
assertThat(fromJson.str).isEqualTo("testCustom");
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo("{\"str\":{\"1\":\"test\"}}");
}
@Test public void toStringReflectsInnerAdapter() throws Exception {
JsonAdapter<String> adapter = moshi.adapter(String.class,
Collections.singleton(Wrapped.Factory.create("1", "2")));
assertThat(adapter.toString())
.isEqualTo("JsonAdapter(String).nullSafe().wrapped([1, 2]).failOnNotFound()");
JsonAdapter<String> failingAdapter = moshi.adapter(String.class,
Collections.singleton(Wrapped.Factory.create(false, "1", "2")));
assertThat(failingAdapter.toString())
.isEqualTo("JsonAdapter(String).nullSafe().wrapped([1, 2])");
}
@Test
public void wrappedFactoryRespectsEquals() throws Exception {
Wrapped wrapped1 = Wrapped.Factory.create("one", "two", "three");
Wrapped wrapped2 = Wrapped.Factory.create("one", "two", "three");
Wrapped wrapped3 = Wrapped.Factory.create("one", "two", "four");
assertThat(wrapped1).isEqualTo(wrapped2);
assertThat(wrapped1.hashCode()).isEqualTo(wrapped2.hashCode());
assertThat(wrapped1.toString()).isEqualTo(wrapped2.toString());
assertThat(wrapped1).isNotEqualTo(wrapped3);
assertThat(wrapped1.hashCode()).isNotEqualTo(wrapped3.hashCode());
assertThat(wrapped1.toString()).isNotEqualTo(wrapped3.toString());
}
@Test public void checkWrappedFactoryConstructorThrows() throws Exception {
PrivateConstructorChecker
.forClass(Wrapped.Factory.class)
.expectedTypeOfException(AssertionError.class)
.expectedExceptionMessage("No instances.")
.check();
}
@Test public void factoryFetchesWrappedFromDelegate() throws Exception {
JsonAdapter<Data5> adapter = moshi.adapter(Data5.class);
Data5 fromJson = adapter.fromJson("{\n"
+ " \"str\": {\n"
+ " \"1\": \"test\"\n"
+ " }\n"
+ "}");
assertThat(fromJson.str).isEqualTo("test");
String toJson = adapter.toJson(fromJson);
assertThat(toJson).isEqualTo("{\"str\":{\"1\":\"test\"}}");
}
private static class Data1 {
String str;
int val;
}
private static class Data2 {
@Wrapped(path = { "1", "2" }) Data1 data;
}
private static class Data3 {
@Custom
@Wrapped(path = "1") String str;
}
private static class Data4 {
@Wrapped(path = "1") Throws th;
}
private static class Data5 {
@WrappedDelegate String str;
}
private static class Throws {
}
@JsonQualifier
@Wrapped(path = "1")
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
@interface WrappedDelegate { }
/** String adapter, that will throw on read and write. */
private static final class ThrowingAdapter {
@FromJson Throws fromJson(String str) throws IOException {
throw new IOException("ThrowingAdapter.fromJson");
}
@ToJson String toJson(Throws th) throws IOException {
throw new IOException("ThrowingAdapter.toJson");
}
}
}
gitextract_k8ld05jz/
├── .buildscript/
│ └── deploy_snapshot.sh
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── build.gradle
├── checkstyle.gradle
├── config/
│ └── checkstyle/
│ └── checkstyle.xml
├── dependencies.gradle
├── gradle/
│ ├── gradle-mvn-push.gradle
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── jacoco.gradle
├── settings.gradle
└── src/
├── integrationTest/
│ └── java/
│ └── com/
│ └── serjltt/
│ └── moshi/
│ └── adapters/
│ ├── Data.java
│ ├── DataFactories.java
│ ├── DeserializeOnlyAutoValueTest.java
│ ├── LazyAdaptersAutoValueTest.java
│ ├── LazyAdaptersRetrofitTest.java
│ ├── LazyAdaptersRxJavaTest.java
│ ├── Nullable.java
│ ├── SerializeOnlyAutoValueTest.java
│ └── TransientAutoValueTest.java
├── main/
│ └── java/
│ └── com/
│ └── serjltt/
│ └── moshi/
│ └── adapters/
│ ├── DefaultOnDataMismatchAdapter.java
│ ├── DeserializeOnly.java
│ ├── ElementAt.java
│ ├── ElementAtJsonAdapter.java
│ ├── FallbackEnum.java
│ ├── FallbackEnumJsonAdapter.java
│ ├── FallbackOnNull.java
│ ├── FallbackOnNullJsonAdapter.java
│ ├── FilterNulls.java
│ ├── FilterNullsJsonAdapter.java
│ ├── FirstElement.java
│ ├── LastElement.java
│ ├── LastElementJsonAdapter.java
│ ├── Pair.java
│ ├── SerializeNulls.java
│ ├── SerializeOnly.java
│ ├── SerializeOnlyNonEmpty.java
│ ├── SerializeOnlyNonEmptyJsonAdapter.java
│ ├── Transient.java
│ ├── TransientJsonAdapter.java
│ ├── Util.java
│ ├── Wrapped.java
│ └── WrappedJsonAdapter.java
└── unitTest/
└── java/
└── com/
└── serjltt/
└── moshi/
└── adapters/
├── Custom.java
├── DefaultOnDataMismatchAdapterTest.java
├── DeserializeOnlyJsonAdapterTest.java
├── ElementAtJsonAdapterTest.java
├── FallbackEnumJsonAdapterTest.java
├── FallbackOnNullJsonAdapterTest.java
├── FilterNullsJsonAdapterTest.java
├── FirstElementJsonAdapterTest.java
├── LastElementJsonAdapterTest.java
├── SerializeNullsJsonAdapterTest.java
├── SerializeOnlyJsonAdapterTest.java
├── SerializeOnlyNonEmptyJsonAdapterTest.java
└── WrappedJsonAdapterTest.java
SYMBOL INDEX (311 symbols across 44 files)
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/Data.java
class Data (line 23) | @AutoValue
method jsonAdapter (line 25) | public static JsonAdapter<Data> jsonAdapter(Moshi moshi) {
method name (line 29) | abstract String name();
method meta (line 35) | @Wrapped(path = {"1"}) abstract Meta meta();
class Meta (line 37) | static class Meta {
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/DataFactories.java
class DataFactories (line 22) | @MoshiAdapterFactory
method create (line 24) | public static JsonAdapter.Factory create() {
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/DeserializeOnlyAutoValueTest.java
class DeserializeOnlyAutoValueTest (line 11) | public final class DeserializeOnlyAutoValueTest {
method serialize (line 17) | @Test public void serialize() {
method deserialize (line 23) | @Test public void deserialize() throws IOException {
class AutoValueClass (line 31) | @AutoValue abstract static class AutoValueClass {
method jsonAdapter (line 32) | public static JsonAdapter<AutoValueClass> jsonAdapter(Moshi moshi) {
method foo (line 36) | abstract Integer foo();
method bar (line 38) | @DeserializeOnly abstract Integer bar();
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/LazyAdaptersAutoValueTest.java
class LazyAdaptersAutoValueTest (line 26) | public class LazyAdaptersAutoValueTest {
method unwrap (line 32) | @Test public void unwrap() throws Exception {
method wrap (line 50) | @Test
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/LazyAdaptersRetrofitTest.java
class LazyAdaptersRetrofitTest (line 38) | public final class LazyAdaptersRetrofitTest {
method unwrapJsonAdapter (line 54) | @Test public void unwrapJsonAdapter() throws Exception {
method unwrapNestedJsonAdapter (line 62) | @Test public void unwrapNestedJsonAdapter() throws Exception {
method wrapPostBody (line 84) | @Test public void wrapPostBody() throws Exception {
method firstElementJsonAdapter (line 96) | @Test public void firstElementJsonAdapter() throws Exception {
method elementAtJsonAdapter (line 103) | @Test public void elementAtJsonAdapter() throws Exception {
method unwrapFirstElement (line 111) | @Test public void unwrapFirstElement() throws Exception {
method assertResponse (line 121) | private <T> void assertResponse(Call<T> call, String input, T expected...
type Service (line 129) | private interface Service {
method unwrap (line 131) | @GET("/")
method unwrapNested (line 134) | @GET("/")
method wrappedPost (line 137) | @POST("/") Call<ResponseBody> wrappedPost(@Body @Wrapped(path = {"1"...
method firstElement (line 140) | @GET("/")
method unwrapFirstElement (line 144) | @GET("/")
method elementAt (line 148) | @GET("/")
class Nested (line 152) | static class Nested {
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/LazyAdaptersRxJavaTest.java
class LazyAdaptersRxJavaTest (line 20) | @RunWith(Parameterized.class)
method failingCallable (line 26) | private static Callable<String> failingCallable() {
method testData (line 42) | @Parameterized.Parameters
method LazyAdaptersRxJavaTest (line 54) | public LazyAdaptersRxJavaTest(BaseTestConsumer testConsumer) {
method reactiveTypeYieldsAppropriateError (line 58) | @Test public void reactiveTypeYieldsAppropriateError() throws Exception {
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/SerializeOnlyAutoValueTest.java
class SerializeOnlyAutoValueTest (line 11) | public final class SerializeOnlyAutoValueTest {
method serialize (line 17) | @Test public void serialize() {
method deserialize (line 23) | @Test public void deserialize() throws IOException {
class AutoValueClass (line 31) | @AutoValue abstract static class AutoValueClass {
method jsonAdapter (line 32) | public static JsonAdapter<AutoValueClass> jsonAdapter(Moshi moshi) {
method foo (line 36) | abstract Integer foo();
method bar (line 38) | @SerializeOnly @Nullable abstract Integer bar();
FILE: src/integrationTest/java/com/serjltt/moshi/adapters/TransientAutoValueTest.java
class TransientAutoValueTest (line 13) | public final class TransientAutoValueTest {
method serialize (line 19) | @Test public void serialize() {
method deserialize (line 25) | @Test public void deserialize() throws IOException {
method toStringReflectsInnerAdapter (line 33) | @Test public void toStringReflectsInnerAdapter() throws Exception {
class AutoValueClass (line 44) | @AutoValue abstract static class AutoValueClass {
method jsonAdapter (line 45) | public static JsonAdapter<AutoValueClass> jsonAdapter(Moshi moshi) {
method foo (line 49) | abstract Integer foo();
method bar (line 51) | @Transient @Nullable abstract Integer bar();
FILE: src/main/java/com/serjltt/moshi/adapters/DefaultOnDataMismatchAdapter.java
class DefaultOnDataMismatchAdapter (line 32) | public final class DefaultOnDataMismatchAdapter<T> extends JsonAdapter<T> {
method DefaultOnDataMismatchAdapter (line 36) | DefaultOnDataMismatchAdapter(JsonAdapter<T> delegate, T defaultValue) {
method fromJson (line 41) | @Override public T fromJson(JsonReader reader) throws IOException {
method toJson (line 51) | @Override public void toJson(JsonWriter writer, T value) throws IOExce...
method toString (line 55) | @Override public String toString() {
method newFactory (line 60) | public static <T> JsonAdapter.Factory newFactory(final Type type, fina...
FILE: src/main/java/com/serjltt/moshi/adapters/DeserializeOnly.java
method create (line 35) | @Override public JsonAdapter<?> create(Type type, Set<? extends Annotati...
FILE: src/main/java/com/serjltt/moshi/adapters/ElementAt.java
method create (line 84) | @Override public JsonAdapter<?> create(Type type, Set<? extends Annotati...
FILE: src/main/java/com/serjltt/moshi/adapters/ElementAtJsonAdapter.java
class ElementAtJsonAdapter (line 33) | final class ElementAtJsonAdapter<T> extends JsonAdapter<T> {
method ElementAtJsonAdapter (line 37) | ElementAtJsonAdapter(Type type, Moshi moshi, int index) {
method fromJson (line 43) | @Override public T fromJson(JsonReader reader) throws IOException {
method toJson (line 49) | @Override public void toJson(JsonWriter writer, T value) throws IOExce...
method toString (line 53) | @Override public String toString() {
FILE: src/main/java/com/serjltt/moshi/adapters/FallbackEnum.java
method create (line 52) | @Override public JsonAdapter<?> create(Type type, Set<? extends Annotati...
FILE: src/main/java/com/serjltt/moshi/adapters/FallbackEnumJsonAdapter.java
class FallbackEnumJsonAdapter (line 29) | final class FallbackEnumJsonAdapter<T extends Enum<T>> extends JsonAdapt...
method FallbackEnumJsonAdapter (line 36) | FallbackEnumJsonAdapter(Class<T> enumType, String fallback) {
method fromJson (line 54) | @Override public T fromJson(JsonReader reader) throws IOException {
method toJson (line 61) | @Override public void toJson(JsonWriter writer, T value) throws IOExce...
method toString (line 65) | @Override public String toString() {
FILE: src/main/java/com/serjltt/moshi/adapters/FallbackOnNull.java
method create (line 79) | @Override public JsonAdapter<?> create(Type type, Set<? extends Annotati...
method retrieveFallback (line 96) | private Object retrieveFallback(FallbackOnNull annotation, String fallba...
method fallbackType (line 106) | private String fallbackType(Class<?> rawType) {
FILE: src/main/java/com/serjltt/moshi/adapters/FallbackOnNullJsonAdapter.java
class FallbackOnNullJsonAdapter (line 29) | final class FallbackOnNullJsonAdapter<T> extends JsonAdapter<T> {
method FallbackOnNullJsonAdapter (line 48) | FallbackOnNullJsonAdapter(JsonAdapter<T> delegate, T fallback, String ...
method fromJson (line 54) | @Override public T fromJson(JsonReader reader) throws IOException {
method toJson (line 62) | @Override public void toJson(JsonWriter writer, T value) throws IOExce...
method toString (line 66) | @Override public String toString() {
FILE: src/main/java/com/serjltt/moshi/adapters/FilterNulls.java
method create (line 37) | @Override public JsonAdapter<?> create(Type type, Set<? extends Annotati...
FILE: src/main/java/com/serjltt/moshi/adapters/FilterNullsJsonAdapter.java
class FilterNullsJsonAdapter (line 13) | final class FilterNullsJsonAdapter<T> extends JsonAdapter<T> {
method FilterNullsJsonAdapter (line 16) | FilterNullsJsonAdapter(JsonAdapter<T> delegate) {
method fromJson (line 20) | @Override public T fromJson(JsonReader reader) throws IOException {
method toJson (line 24) | @Override public void toJson(JsonWriter writer, T value) throws IOExce...
method toString (line 28) | @Override public String toString() {
method removeNulls (line 32) | private T removeNulls(final T value) {
FILE: src/main/java/com/serjltt/moshi/adapters/FirstElement.java
method create (line 71) | @Override public JsonAdapter<?> create(Type type, Set<? extends Annotati...
FILE: src/main/java/com/serjltt/moshi/adapters/LastElement.java
method create (line 56) | @Override public JsonAdapter<?> create(Type type, Set<? extends Annotati...
FILE: src/main/java/com/serjltt/moshi/adapters/LastElementJsonAdapter.java
class LastElementJsonAdapter (line 17) | final class LastElementJsonAdapter<T> extends JsonAdapter<T> {
method LastElementJsonAdapter (line 20) | LastElementJsonAdapter(Type type, Moshi moshi) {
method fromJson (line 25) | @Override public T fromJson(JsonReader reader) throws IOException {
method toJson (line 31) | @Override public void toJson(JsonWriter writer, T value) throws IOExce...
method toString (line 35) | @Override public String toString() {
FILE: src/main/java/com/serjltt/moshi/adapters/Pair.java
class Pair (line 4) | final class Pair<F, S> {
method Pair (line 8) | Pair(F first, S second) {
FILE: src/main/java/com/serjltt/moshi/adapters/SerializeNulls.java
method create (line 51) | @Override public JsonAdapter<?> create(Type type, Set<? extends Annotati...
FILE: src/main/java/com/serjltt/moshi/adapters/SerializeOnly.java
method create (line 35) | @Override public JsonAdapter<?> create(Type type, Set<? extends Annotati...
FILE: src/main/java/com/serjltt/moshi/adapters/SerializeOnlyNonEmpty.java
method create (line 54) | @Override public JsonAdapter<?> create(Type type, Set<? extends Annotati...
FILE: src/main/java/com/serjltt/moshi/adapters/SerializeOnlyNonEmptyJsonAdapter.java
class SerializeOnlyNonEmptyJsonAdapter (line 14) | final class SerializeOnlyNonEmptyJsonAdapter<T> extends JsonAdapter<T> {
method SerializeOnlyNonEmptyJsonAdapter (line 17) | SerializeOnlyNonEmptyJsonAdapter(JsonAdapter<T> delegate) {
method fromJson (line 21) | @Override public T fromJson(JsonReader reader) throws IOException {
method toJson (line 25) | @Override public void toJson(JsonWriter writer, T value) throws IOExce...
method isNotEmpty (line 34) | private boolean isNotEmpty(final T value) {
method toString (line 48) | @Override public String toString() {
FILE: src/main/java/com/serjltt/moshi/adapters/Transient.java
method create (line 35) | @Override public JsonAdapter<?> create(Type type, Set<? extends Annotati...
FILE: src/main/java/com/serjltt/moshi/adapters/TransientJsonAdapter.java
class TransientJsonAdapter (line 12) | final class TransientJsonAdapter<T> extends JsonAdapter<T> {
method TransientJsonAdapter (line 17) | TransientJsonAdapter(JsonAdapter<T> delegate, boolean serialize, boole...
method fromJson (line 23) | @Override public T fromJson(JsonReader reader) throws IOException {
method toJson (line 32) | @Override public void toJson(JsonWriter writer, T value) throws IOExce...
method toString (line 41) | @Override public String toString() {
FILE: src/main/java/com/serjltt/moshi/adapters/Util.java
class Util (line 25) | final class Util {
method nextAnnotations (line 32) | public static <A extends Annotation> Pair<A, Set<Annotation>> nextAnno...
method findDelegatedAnnotation (line 57) | private static <A extends Annotation> A findDelegatedAnnotation(
method Util (line 68) | private Util() {
FILE: src/main/java/com/serjltt/moshi/adapters/Wrapped.java
method create (line 89) | @Override public JsonAdapter<?> create(Type type, Set<? extends Annotati...
class Factory (line 101) | final class Factory {
method create (line 103) | public static Wrapped create(final String... path) {
method create (line 108) | public static Wrapped create(final boolean failOnNotFound, final Strin...
method Factory (line 146) | private Factory() {
FILE: src/main/java/com/serjltt/moshi/adapters/WrappedJsonAdapter.java
class WrappedJsonAdapter (line 26) | final class WrappedJsonAdapter<T> extends JsonAdapter<T> {
method WrappedJsonAdapter (line 31) | WrappedJsonAdapter(JsonAdapter<T> delegate, String[] path, boolean fai...
method fromJson (line 37) | @Override public T fromJson(JsonReader reader) throws IOException {
method toJson (line 41) | @Override public void toJson(JsonWriter writer, T value) throws IOExce...
method toString (line 45) | @Override public String toString() {
method fromJson (line 54) | private static <T> T fromJson(JsonAdapter<T> adapter, JsonReader reade...
method toJson (line 113) | private static <T> void toJson(JsonAdapter<T> adapter, JsonWriter writ...
FILE: src/unitTest/java/com/serjltt/moshi/adapters/Custom.java
class CustomAdapter (line 30) | final class CustomAdapter {
method fromJson (line 31) | @Custom @FromJson String fromJson(String str) {
method toJson (line 35) | @ToJson String toJson(@Custom String str) {
FILE: src/unitTest/java/com/serjltt/moshi/adapters/DefaultOnDataMismatchAdapterTest.java
class DefaultOnDataMismatchAdapterTest (line 22) | public final class DefaultOnDataMismatchAdapterTest {
method deserializeMismatch (line 23) | @Test public void deserializeMismatch() throws IOException {
method deserializeMatch (line 28) | @Test public void deserializeMatch() throws IOException {
method serialize (line 33) | @Test public void serialize() {
method factorySupportsType (line 38) | @Test public void factorySupportsType() throws Exception {
method toStringReflectsInner (line 72) | @Test public void toStringReflectsInner() {
method newFruitFactory (line 78) | private JsonAdapter.Factory newFruitFactory() {
method buildMoshi (line 82) | private Moshi buildMoshi(JsonAdapter.Factory factory) {
type Fruit (line 88) | enum Fruit {
FILE: src/unitTest/java/com/serjltt/moshi/adapters/DeserializeOnlyJsonAdapterTest.java
class DeserializeOnlyJsonAdapterTest (line 9) | public final class DeserializeOnlyJsonAdapterTest {
method deserializeOnly (line 16) | @Test public void deserializeOnly() throws Exception {
method factoryMaintainsOtherAnnotations (line 25) | @Test public void factoryMaintainsOtherAnnotations() throws Exception {
method toStringReflectsInnerAdapter (line 34) | @Test public void toStringReflectsInnerAdapter() throws Exception {
class Data1 (line 40) | private static class Data1 {
class Data2 (line 44) | private static class Data2 {
FILE: src/unitTest/java/com/serjltt/moshi/adapters/ElementAtJsonAdapterTest.java
class ElementAtJsonAdapterTest (line 39) | public final class ElementAtJsonAdapterTest {
method elementAt (line 46) | @Test public void elementAt() throws Exception {
method fromJsonOnEmptyArrayReturnsNull (line 63) | @Test public void fromJsonOnEmptyArrayReturnsNull() throws Exception {
method fromJsonOnArrayOfSizeOneReturnsNull (line 69) | @Test public void fromJsonOnArrayOfSizeOneReturnsNull() throws Excepti...
method fromJsonOnNullArrayReturnsNull (line 75) | @Test public void fromJsonOnNullArrayReturnsNull() throws Exception {
method fromJsonExpectsAnArray (line 81) | @Test public void fromJsonExpectsAnArray() throws Exception {
method factoryMaintainsOtherAnnotations (line 96) | @Test @Ignore public void factoryMaintainsOtherAnnotations() throws Ex...
method factoryExpectsOnlyOneAnnotation (line 111) | @Test public void factoryExpectsOnlyOneAnnotation() {
method toStringReflectsInnerAdapter (line 142) | @Test public void toStringReflectsInnerAdapter() throws Exception {
method elementAtDelegated (line 158) | @Test public void elementAtDelegated() throws Exception {
method assertNullReturn (line 179) | private void assertNullReturn(String string) throws IOException {
class Data (line 189) | private static class Data {
class Data2 (line 194) | private static class Data2 {
class Data3 (line 199) | private static class Data3 {
FILE: src/unitTest/java/com/serjltt/moshi/adapters/FallbackEnumJsonAdapterTest.java
class FallbackEnumJsonAdapterTest (line 30) | public final class FallbackEnumJsonAdapterTest {
method asRegularEnumAdapter (line 36) | @Test public void asRegularEnumAdapter() throws Exception {
method fallbackEnum (line 45) | @Test public void fallbackEnum() throws Exception {
method nullEnum (line 50) | @Test public void nullEnum() throws Exception {
method throwsOnInvalidFallback (line 56) | @Test public void throwsOnInvalidFallback() throws Exception {
method toStringReflectsInnerAdapter (line 66) | @Test public void toStringReflectsInnerAdapter() throws Exception {
method ignoresUnannotatedEnums (line 74) | @Test public void ignoresUnannotatedEnums() throws Exception {
method factoryIgnoresUnsupportedTypes (line 87) | @Test public void factoryIgnoresUnsupportedTypes() throws Exception {
type Roshambo (line 97) | @FallbackEnum(name = "UNKNOWN") enum Roshambo {
type Value (line 104) | @FallbackEnum(name = "UNK") enum Value {
type Regular (line 108) | enum Regular {
FILE: src/unitTest/java/com/serjltt/moshi/adapters/FallbackOnNullJsonAdapterTest.java
class FallbackOnNullJsonAdapterTest (line 38) | public final class FallbackOnNullJsonAdapterTest {
method booleanFallbacks (line 45) | @Test public void booleanFallbacks() throws Exception {
class WrapsBool (line 49) | private static class WrapsBool implements Wrapper<Boolean> {
method first (line 53) | @Override public Boolean first() {
method second (line 57) | @Override public Boolean second() {
method byteFallbacks (line 62) | @Test public void byteFallbacks() throws Exception {
class WrapsByte (line 66) | private static class WrapsByte implements Wrapper<Byte> {
method first (line 70) | @Override public Byte first() {
method second (line 74) | @Override public Byte second() {
method charFallbacks (line 79) | @Test public void charFallbacks() throws Exception {
class WrapsChar (line 83) | private static class WrapsChar implements Wrapper<Character> {
method first (line 87) | @Override public Character first() {
method second (line 91) | @Override public Character second() {
method doubleFallbacks (line 96) | @Test public void doubleFallbacks() throws Exception {
class WrapsDouble (line 101) | private static class WrapsDouble implements Wrapper<Double> {
method first (line 105) | @Override public Double first() {
method second (line 109) | @Override public Double second() {
method floatFallbacks (line 114) | @Test public void floatFallbacks() throws Exception {
class WrapsFloat (line 119) | private static class WrapsFloat implements Wrapper<Float> {
method first (line 123) | @Override public Float first() {
method second (line 127) | @Override public Float second() {
method intFallbacks (line 132) | @Test public void intFallbacks() throws Exception {
method intFallbacksNoLocaleInfluence (line 136) | @Test public void intFallbacksNoLocaleInfluence() throws Exception {
class WrapsInt (line 145) | private static class WrapsInt implements Wrapper<Integer> {
method first (line 149) | @Override public Integer first() {
method second (line 153) | @Override public Integer second() {
method longFallbacks (line 158) | @Test public void longFallbacks() throws Exception {
class WrapsLong (line 163) | private static class WrapsLong implements Wrapper<Long> {
method first (line 167) | @Override public Long first() {
method second (line 171) | @Override public Long second() {
method shortFallbacks (line 176) | @Test public void shortFallbacks() throws Exception {
class WrapsShort (line 181) | private static class WrapsShort implements Wrapper<Short> {
method first (line 185) | @Override public Short first() {
method second (line 189) | @Override public Short second() {
method factoryMaintainsOtherAnnotations (line 194) | @Test public void factoryMaintainsOtherAnnotations() throws Exception {
class AnotherInt (line 209) | private static class AnotherInt {
method factoryIgnoresNonPrimitiveTypes (line 214) | @Test public void factoryIgnoresNonPrimitiveTypes() {
method fallbackOnNullIsDelegated (line 235) | @Test public void fallbackOnNullIsDelegated() throws Exception {
class AndAnotherInt (line 244) | private static class AndAnotherInt {
method toStringReflectsInnerAdapter (line 254) | @Test public void toStringReflectsInnerAdapter() throws Exception {
method annotationType (line 264) | @Override public Class<? extends Annotation> annotationType() {
method fallbackBoolean (line 268) | @Override public boolean fallbackBoolean() {
method fallbackByte (line 272) | @Override public byte fallbackByte() {
method fallbackChar (line 276) | @Override public char fallbackChar() {
method fallbackDouble (line 280) | @Override public double fallbackDouble() {
method fallbackFloat (line 284) | @Override public float fallbackFloat() {
method fallbackInt (line 288) | @Override public int fallbackInt() {
method fallbackLong (line 292) | @Override public long fallbackLong() {
method fallbackShort (line 296) | @Override public short fallbackShort() {
method assertForClass (line 301) | private <T extends Wrapper<P>, P> void assertForClass(Class<T> cls, P ...
type Wrapper (line 316) | private interface Wrapper<P> {
method first (line 317) | P first();
method second (line 319) | P second();
class MultiplyAdapter (line 324) | final class MultiplyAdapter {
method fromJson (line 325) | @Multiply @FromJson int fromJson(int val) {
method toJson (line 329) | @ToJson int toJson(@Multiply int val) {
FILE: src/unitTest/java/com/serjltt/moshi/adapters/FilterNullsJsonAdapterTest.java
class FilterNullsJsonAdapterTest (line 14) | public final class FilterNullsJsonAdapterTest {
method noNullValues (line 21) | @Test public void noNullValues() throws Exception {
method nullValues (line 32) | @Test public void nullValues() throws Exception {
method nullList (line 43) | @Test public void nullList() throws Exception {
method toStringReflectsInnerAdapter (line 54) | @Test public void toStringReflectsInnerAdapter() throws Exception {
FILE: src/unitTest/java/com/serjltt/moshi/adapters/FirstElementJsonAdapterTest.java
class FirstElementJsonAdapterTest (line 32) | public final class FirstElementJsonAdapterTest {
method first (line 39) | @Test public void first() throws Exception {
method fromJsonOnEmptyArrayReturnsNull (line 56) | @Test public void fromJsonOnEmptyArrayReturnsNull() throws Exception {
method fromJsonOnNullArrayReturnsNull (line 62) | @Test public void fromJsonOnNullArrayReturnsNull() throws Exception {
method fromJsonExpectsAnArray (line 68) | @Test public void fromJsonExpectsAnArray() throws Exception {
method factoryMaintainsOtherAnnotations (line 83) | @Test @Ignore public void factoryMaintainsOtherAnnotations() throws Ex...
method factoryExpectsOnlyOneAnnotation (line 98) | @Test public void factoryExpectsOnlyOneAnnotation() throws Exception {
method toStringReflectsInnerAdapter (line 125) | @Test public void toStringReflectsInnerAdapter() throws Exception {
method assertNullReturn (line 132) | private void assertNullReturn(String string) throws IOException {
class Data (line 142) | private static class Data {
class Data2 (line 147) | private static class Data2 {
FILE: src/unitTest/java/com/serjltt/moshi/adapters/LastElementJsonAdapterTest.java
class LastElementJsonAdapterTest (line 17) | public final class LastElementJsonAdapterTest {
method last (line 24) | @Test public void last() throws Exception {
method fromJsonOnEmptyArrayReturnsNull (line 42) | @Test public void fromJsonOnEmptyArrayReturnsNull() throws Exception {
method fromJsonOnNullArrayReturnsNull (line 48) | @Test public void fromJsonOnNullArrayReturnsNull() throws Exception {
method fromJsonExpectsAnArray (line 54) | @Test public void fromJsonExpectsAnArray() throws Exception {
method factoryMaintainsOtherAnnotations (line 69) | @Test @Ignore public void factoryMaintainsOtherAnnotations() throws Ex...
method factoryExpectsOnlyOneAnnotation (line 84) | @Test public void factoryExpectsOnlyOneAnnotation() throws Exception {
method toStringReflectsInnerAdapter (line 111) | @Test public void toStringReflectsInnerAdapter() throws Exception {
method assertNullReturn (line 118) | private void assertNullReturn(String string) throws IOException {
class Data (line 128) | private static class Data {
class Data2 (line 133) | private static class Data2 {
FILE: src/unitTest/java/com/serjltt/moshi/adapters/SerializeNullsJsonAdapterTest.java
class SerializeNullsJsonAdapterTest (line 26) | public final class SerializeNullsJsonAdapterTest {
method serializesNulls (line 33) | @Test public void serializesNulls() throws Exception {
method factoryMaintainsOtherAnnotations (line 45) | @Test public void factoryMaintainsOtherAnnotations() throws Exception {
method maintainsPreviousSerializationValue (line 62) | @Test public void maintainsPreviousSerializationValue() throws Excepti...
method toStringReflectsInnerAdapter (line 77) | @Test public void toStringReflectsInnerAdapter() throws Exception {
class Data1 (line 84) | private static class Data1 {
class Data2 (line 88) | private static class Data2 {
FILE: src/unitTest/java/com/serjltt/moshi/adapters/SerializeOnlyJsonAdapterTest.java
class SerializeOnlyJsonAdapterTest (line 9) | public final class SerializeOnlyJsonAdapterTest {
method serializeOnly (line 17) | @Test public void serializeOnly() throws Exception {
method factoryMaintainsOtherAnnotations (line 27) | @Test public void factoryMaintainsOtherAnnotations() throws Exception {
method toStringReflectsInnerAdapter (line 43) | @Test public void toStringReflectsInnerAdapter() throws Exception {
class Data1 (line 49) | private static class Data1 {
class Data2 (line 53) | private static class Data2 {
FILE: src/unitTest/java/com/serjltt/moshi/adapters/SerializeOnlyNonEmptyJsonAdapterTest.java
class SerializeOnlyNonEmptyJsonAdapterTest (line 29) | public final class SerializeOnlyNonEmptyJsonAdapterTest {
method serializesOnlyNonEmptyCustomArray (line 36) | @Test public void serializesOnlyNonEmptyCustomArray() throws Exception {
method serializesOnlyNonEmptyByteArray (line 54) | @Test public void serializesOnlyNonEmptyByteArray() throws Exception {
method serializesOnlyNonEmptyCharArray (line 69) | @Test public void serializesOnlyNonEmptyCharArray() throws Exception {
method serializesOnlyNonEmptyShortArray (line 84) | @Test public void serializesOnlyNonEmptyShortArray() throws Exception {
method serializesOnlyNonEmptyIntArray (line 99) | @Test public void serializesOnlyNonEmptyIntArray() throws Exception {
method serializesOnlyNonEmptyLongArray (line 114) | @Test public void serializesOnlyNonEmptyLongArray() throws Exception {
method serializesOnlyNonEmptyFloatArray (line 129) | @Test public void serializesOnlyNonEmptyFloatArray() throws Exception {
method serializesOnlyNonEmptyDoubleArray (line 144) | @Test public void serializesOnlyNonEmptyDoubleArray() throws Exception {
method serializesOnlyNonEmptyBooleanArray (line 159) | @Test public void serializesOnlyNonEmptyBooleanArray() throws Exception {
method serializesOnlyNonEmptyStringArray (line 174) | @Test public void serializesOnlyNonEmptyStringArray() throws Exception {
method serializesOnlyNonEmptyCollection (line 189) | @Test public void serializesOnlyNonEmptyCollection() throws Exception {
method serializesOnlyNonEmptyMap (line 204) | @Test public void serializesOnlyNonEmptyMap() throws Exception {
method toStringReflectsInnerAdapter (line 221) | @Test public void toStringReflectsInnerAdapter() throws Exception {
class Data1 (line 228) | static class Data1 {
class CustomType (line 243) | static class CustomType {
method CustomType (line 246) | CustomType(final String data) {
FILE: src/unitTest/java/com/serjltt/moshi/adapters/WrappedJsonAdapterTest.java
class WrappedJsonAdapterTest (line 39) | public final class WrappedJsonAdapterTest {
method oneObject (line 47) | @Test public void oneObject() throws Exception {
method arrayOfObjects (line 68) | @Test public void arrayOfObjects() throws Exception {
method failOnNotFound (line 106) | @Test public void failOnNotFound() throws Exception {
method failOnNotFound2 (line 124) | @Test public void failOnNotFound2() throws Exception {
method failOnNotFoundFalse (line 140) | @Test public void failOnNotFoundFalse() throws Exception {
method notNullSafe (line 148) | @Test public void notNullSafe() throws Exception {
method fromJsonSkipsNonPathValues (line 167) | @Test public void fromJsonSkipsNonPathValues() throws Exception {
method fromJsonRemainingPathValues (line 187) | @Test public void fromJsonRemainingPathValues() throws Exception {
method fromJsonOnIncorrectPath (line 207) | @Test public void fromJsonOnIncorrectPath() throws Exception {
method fromJsonDoesNotSwallowIOExceptions (line 224) | @Test public void fromJsonDoesNotSwallowIOExceptions() throws Exception {
method fromJsonDoesNotSwallowJsonEncodingExceptions (line 239) | @Test public void fromJsonDoesNotSwallowJsonEncodingExceptions() throw...
method fromJsonDoesNotSwallowJsonDataExceptions (line 260) | @Test public void fromJsonDoesNotSwallowJsonDataExceptions() throws Ex...
method toJsonDoesNotSwallowExceptions (line 275) | @Test public void toJsonDoesNotSwallowExceptions() throws Exception {
method factoryMaintainsOtherAnnotations (line 289) | @Test public void factoryMaintainsOtherAnnotations() throws Exception {
method toStringReflectsInnerAdapter (line 303) | @Test public void toStringReflectsInnerAdapter() throws Exception {
method wrappedFactoryRespectsEquals (line 315) | @Test
method checkWrappedFactoryConstructorThrows (line 330) | @Test public void checkWrappedFactoryConstructorThrows() throws Except...
method factoryFetchesWrappedFromDelegate (line 338) | @Test public void factoryFetchesWrappedFromDelegate() throws Exception {
class Data1 (line 352) | private static class Data1 {
class Data2 (line 357) | private static class Data2 {
class Data3 (line 361) | private static class Data3 {
class Data4 (line 366) | private static class Data4 {
class Data5 (line 370) | private static class Data5 {
class Throws (line 373) | private static class Throws {
class ThrowingAdapter (line 383) | private static final class ThrowingAdapter {
method fromJson (line 384) | @FromJson Throws fromJson(String str) throws IOException {
method toJson (line 388) | @ToJson String toJson(Throws th) throws IOException {
Condensed preview — 63 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (196K chars).
[
{
"path": ".buildscript/deploy_snapshot.sh",
"chars": 866,
"preview": "#!/bin/bash\n#\n# Deploy a jar, source jar, and javadoc jar to Sonatype's snapshot repo.\n#\n# Adapted from https://coderwal"
},
{
"path": ".gitignore",
"chars": 102,
"preview": "# Gradle junk\n.gradle/\nbuild/\nlocal.properties\n\n# Idea junk\n.idea/\n*.iml\nout/\n\n# MacOS junk\n.DS_Store\n"
},
{
"path": ".travis.yml",
"chars": 1708,
"preview": "language: java\n\njdk:\n - oraclejdk8\n\nscript:\n - ./gradlew clean check --stacktrace\n\nafter_success:\n - bash <(curl -s h"
},
{
"path": "CHANGELOG.md",
"chars": 2303,
"preview": "Change Log\n===\n\n2.2 *(02-05-2018)*\n---\n* Usage selectString in FallbackEnumJsonAdapter. (#64)\n* Fix FallbackOnNull namin"
},
{
"path": "LICENSE",
"chars": 11345,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 8125,
"preview": "Moshi Lazy Adapters\n===\n\n\n[![Build Status][travis.svg]][travis]\n[![codecov][codecov.svg]][codecov]\n[![Latest Build][late"
},
{
"path": "build.gradle",
"chars": 1231,
"preview": "buildscript {\n repositories {\n mavenCentral()\n maven { url 'https://jitpack.io' }\n }\n\n // Needed to use auto-va"
},
{
"path": "checkstyle.gradle",
"chars": 532,
"preview": "apply plugin: 'checkstyle'\n\ncheckstyle {\n toolVersion '6.0'\n}\n\ntask checkstyle(type: Checkstyle) {\n configFile rootPro"
},
{
"path": "config/checkstyle/checkstyle.xml",
"chars": 6495,
"preview": "<?xml version=\"1.0\"?>\n<!DOCTYPE module PUBLIC\n \"-//Puppy Crawl//DTD Check Configuration 1.2//EN\"\n \"http://www.pupp"
},
{
"path": "dependencies.gradle",
"chars": 825,
"preview": "ext {\n javaVersion = JavaVersion.VERSION_1_7\n\n ci = 'true'.equals(System.getenv('CI'))\n\n /* Dependencies */\n moshi ="
},
{
"path": "gradle/gradle-mvn-push.gradle",
"chars": 7561,
"preview": "/*\n * Copyright (C) 2013 Chris Banes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 233,
"preview": "#Mon Oct 09 10:31:26 CEST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER"
},
{
"path": "gradle.properties",
"chars": 716,
"preview": "GROUP=com.serjltt.moshi\nVERSION_NAME=2.3-SNAPSHOT\n\nPOM_DESCRIPTION=A collection of simple JsonAdapters for Moshi.\nPOM_AR"
},
{
"path": "gradlew",
"chars": 5299,
"preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n## Gradle start up"
},
{
"path": "gradlew.bat",
"chars": 2260,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
},
{
"path": "jacoco.gradle",
"chars": 230,
"preview": "apply plugin: 'jacoco'\n\njacoco {\n toolVersion = '0.7.7.201606060606' // See http://www.eclemma.org/jacoco/.\n}\n\njacocoTe"
},
{
"path": "settings.gradle",
"chars": 42,
"preview": "rootProject.name = 'moshi-lazy-adapters'\n\n"
},
{
"path": "src/integrationTest/java/com/serjltt/moshi/adapters/Data.java",
"chars": 1223,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/integrationTest/java/com/serjltt/moshi/adapters/DataFactories.java",
"chars": 972,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/integrationTest/java/com/serjltt/moshi/adapters/DeserializeOnlyAutoValueTest.java",
"chars": 1274,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.google.auto.value.AutoValue;\nimport com.squareup.moshi.JsonAdapter;\nimpo"
},
{
"path": "src/integrationTest/java/com/serjltt/moshi/adapters/LazyAdaptersAutoValueTest.java",
"chars": 2148,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/integrationTest/java/com/serjltt/moshi/adapters/LazyAdaptersRetrofitTest.java",
"chars": 4848,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/integrationTest/java/com/serjltt/moshi/adapters/LazyAdaptersRxJavaTest.java",
"chars": 2100,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.JsonDataException;"
},
{
"path": "src/integrationTest/java/com/serjltt/moshi/adapters/Nullable.java",
"chars": 97,
"preview": "package com.serjltt.moshi.adapters;\n\n/** For testing purposes. */\npublic @interface Nullable {\n}\n"
},
{
"path": "src/integrationTest/java/com/serjltt/moshi/adapters/SerializeOnlyAutoValueTest.java",
"chars": 1280,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.google.auto.value.AutoValue;\nimport com.squareup.moshi.JsonAdapter;\nimpo"
},
{
"path": "src/integrationTest/java/com/serjltt/moshi/adapters/TransientAutoValueTest.java",
"chars": 1737,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.google.auto.value.AutoValue;\nimport com.squareup.moshi.JsonAdapter;\nimpo"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/DefaultOnDataMismatchAdapter.java",
"chars": 2449,
"preview": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/DeserializeOnly.java",
"chars": 1537,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.JsonQualifier;\nimp"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/ElementAt.java",
"chars": 3136,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/ElementAtJsonAdapter.java",
"chars": 1883,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/FallbackEnum.java",
"chars": 2422,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/FallbackEnumJsonAdapter.java",
"chars": 2442,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/FallbackOnNull.java",
"chars": 4471,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/FallbackOnNullJsonAdapter.java",
"chars": 2304,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/FilterNulls.java",
"chars": 1755,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.JsonQualifier;\nimp"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/FilterNullsJsonAdapter.java",
"chars": 1100,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.JsonReader;\nimport"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/FirstElement.java",
"chars": 2748,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/LastElement.java",
"chars": 2142,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.JsonQualifier;\nimp"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/LastElementJsonAdapter.java",
"chars": 1223,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.JsonReader;\nimport"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/Pair.java",
"chars": 215,
"preview": "package com.serjltt.moshi.adapters;\n\n/** A simple pair data class. */\nfinal class Pair<F, S> {\n final F first;\n final "
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/SerializeNulls.java",
"chars": 2154,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/SerializeOnly.java",
"chars": 1523,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.JsonQualifier;\nimp"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/SerializeOnlyNonEmpty.java",
"chars": 2464,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/SerializeOnlyNonEmptyJsonAdapter.java",
"chars": 1459,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.JsonReader;\nimport"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/Transient.java",
"chars": 1471,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.JsonQualifier;\nimp"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/TransientJsonAdapter.java",
"chars": 1423,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.JsonReader;\nimport"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/Util.java",
"chars": 2755,
"preview": "/*\n * Copyright 2014 Square, Inc.\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/Wrapped.java",
"chars": 4971,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/main/java/com/serjltt/moshi/adapters/WrappedJsonAdapter.java",
"chars": 4741,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/unitTest/java/com/serjltt/moshi/adapters/Custom.java",
"chars": 1340,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/unitTest/java/com/serjltt/moshi/adapters/DefaultOnDataMismatchAdapterTest.java",
"chars": 3397,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.squareup.moshi.Json;\nimport com.squareup.moshi.JsonAdapter;\nimport com.s"
},
{
"path": "src/unitTest/java/com/serjltt/moshi/adapters/DeserializeOnlyJsonAdapterTest.java",
"chars": 1531,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.Moshi;\nimport org."
},
{
"path": "src/unitTest/java/com/serjltt/moshi/adapters/ElementAtJsonAdapterTest.java",
"chars": 6320,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/unitTest/java/com/serjltt/moshi/adapters/FallbackEnumJsonAdapterTest.java",
"chars": 3875,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n * Copyright (C) 2014 Square, Inc.\n *\n * Licensed under the Apache License, Version"
},
{
"path": "src/unitTest/java/com/serjltt/moshi/adapters/FallbackOnNullJsonAdapterTest.java",
"chars": 9428,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/unitTest/java/com/serjltt/moshi/adapters/FilterNullsJsonAdapterTest.java",
"chars": 2279,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.Moshi;\nimport com."
},
{
"path": "src/unitTest/java/com/serjltt/moshi/adapters/FirstElementJsonAdapterTest.java",
"chars": 4883,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/unitTest/java/com/serjltt/moshi/adapters/LastElementJsonAdapterTest.java",
"chars": 4499,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.squareup.moshi.Json;\nimport com.squareup.moshi.JsonAdapter;\nimport com.s"
},
{
"path": "src/unitTest/java/com/serjltt/moshi/adapters/SerializeNullsJsonAdapterTest.java",
"chars": 3011,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/unitTest/java/com/serjltt/moshi/adapters/SerializeOnlyJsonAdapterTest.java",
"chars": 1817,
"preview": "package com.serjltt.moshi.adapters;\n\nimport com.squareup.moshi.JsonAdapter;\nimport com.squareup.moshi.Moshi;\nimport org."
},
{
"path": "src/unitTest/java/com/serjltt/moshi/adapters/SerializeOnlyNonEmptyJsonAdapterTest.java",
"chars": 8709,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
},
{
"path": "src/unitTest/java/com/serjltt/moshi/adapters/WrappedJsonAdapterTest.java",
"chars": 12373,
"preview": "/*\n * Copyright 2016 Serj Lotutovici\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may no"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the serj-lotutovici/moshi-lazy-adapters GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 63 files (177.5 KB), approximately 45.4k tokens, and a symbol index with 311 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.