Commit 3b40792f by 郑娜伟

gson 1.1.1

parent 5b523bc3
......@@ -12,7 +12,7 @@ android {
//这里就是打jar包
task makeJar(type: Copy) {
//删除旧的jar包
delete 'build/libs/sobot_json_1.1.jar'
delete 'build/libs/sobot_gson_1.1.1.jar'
//原地址
from('build/intermediates/packaged-classes/release/')
//导出jar包的地址
......@@ -20,7 +20,7 @@ android {
//包含的jar包
include('classes.jar')
//重命名jar包为mysdk
rename ('classes.jar', 'sobot_json_1.1.jar')
rename ('classes.jar', 'sobot_gson_1.1.1.jar')
}
makeJar.dependsOn(build)
}
......@@ -30,4 +30,4 @@ dependencies {
}
//添加发布到mavenCentral脚本
apply from: 'sobot-json-publish-mavencentral.gradle'
\ No newline at end of file
apply from: 'sobot-gson-publish-mavencentral.gradle'
\ No newline at end of file
......@@ -11,8 +11,8 @@ task androidSourcesJar(type: Jar) {
ext {
PUBLISH_GROUP_ID = "com.sobot.library" //项目包名
PUBLISH_ARTIFACT_ID = 'json' //项目名
PUBLISH_VERSION = '1.1' //版本号
PUBLISH_ARTIFACT_ID = 'gson' //项目名
PUBLISH_VERSION = '1.1.1' //版本号 1.2 已经被使用了,需要跳过它
}
......@@ -115,7 +115,7 @@ publishing {
// This is an arbitrary name, you may also use "mavencentral" or
// any other name that's descriptive for you
//项目名称
name = "json"
name = "gson"
def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
......
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import com.sobot.gson.GsonBuilder;
/**
* A strategy (or policy) definition that is used to decide whether or not a field or top-level
* class should be serialized or deserialized as part of the JSON output/input. For serialization,
* if the {@link #shouldSkipClass(Class)} method returns true then that class or field type
* will not be part of the JSON output. For deserialization, if {@link #shouldSkipClass(Class)}
* returns true, then it will not be set as part of the Java object structure.
*
* <p>The following are a few examples that shows how you can use this exclusion mechanism.
*
* <p><strong>Exclude fields and objects based on a particular class type:</strong>
* <pre class="code">
* private static class SpecificClassExclusionStrategy implements ExclusionStrategy {
* private final Class&lt;?&gt; excludedThisClass;
*
* public SpecificClassExclusionStrategy(Class&lt;?&gt; excludedThisClass) {
* this.excludedThisClass = excludedThisClass;
* }
*
* public boolean shouldSkipClass(Class&lt;?&gt; clazz) {
* return excludedThisClass.equals(clazz);
* }
*
* public boolean shouldSkipField(FieldAttributes f) {
* return excludedThisClass.equals(f.getDeclaredClass());
* }
* }
* </pre>
*
* <p><strong>Excludes fields and objects based on a particular annotation:</strong>
* <pre class="code">
* public &#64;interface FooAnnotation {
* // some implementation here
* }
*
* // Excludes any field (or class) that is tagged with an "&#64;FooAnnotation"
* private static class FooAnnotationExclusionStrategy implements ExclusionStrategy {
* public boolean shouldSkipClass(Class&lt;?&gt; clazz) {
* return clazz.getAnnotation(FooAnnotation.class) != null;
* }
*
* public boolean shouldSkipField(FieldAttributes f) {
* return f.getAnnotation(FooAnnotation.class) != null;
* }
* }
* </pre>
*
* <p>Now if you want to configure {@code Gson} to use a user defined exclusion strategy, then
* the {@code GsonBuilder} is required. The following is an example of how you can use the
* {@code GsonBuilder} to configure Gson to use one of the above sample:
* <pre class="code">
* ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
* Gson gson = new GsonBuilder()
* .setExclusionStrategies(excludeStrings)
* .create();
* </pre>
*
* <p>For certain model classes, you may only want to serialize a field, but exclude it for
* deserialization. To do that, you can write an {@code ExclusionStrategy} as per normal;
* however, you would register it with the
* {@link GsonBuilder#addDeserializationExclusionStrategy(ExclusionStrategy)} method.
* For example:
* <pre class="code">
* ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
* Gson gson = new GsonBuilder()
* .addDeserializationExclusionStrategy(excludeStrings)
* .create();
* </pre>
*
* @author Inderjeet Singh
* @author Joel Leitch
*
* @see GsonBuilder#setExclusionStrategies(ExclusionStrategy...)
* @see GsonBuilder#addDeserializationExclusionStrategy(ExclusionStrategy)
* @see GsonBuilder#addSerializationExclusionStrategy(ExclusionStrategy)
*
* @since 1.4
*/
public interface ExclusionStrategy {
/**
* @param f the field object that is under test
* @return true if the field should be ignored; otherwise false
*/
public boolean shouldSkipField(FieldAttributes f);
/**
* @param clazz the class object that is under test
* @return true if the class should be ignored; otherwise false
*/
public boolean shouldSkipClass(Class<?> clazz);
}
/*
* Copyright (C) 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import com.sobot.gson.internal.$Gson$Preconditions;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
/**
* A data object that stores attributes of a field.
*
* <p>This class is immutable; therefore, it can be safely shared across threads.
*
* @author Inderjeet Singh
* @author Joel Leitch
*
* @since 1.4
*/
public final class FieldAttributes {
private final Field field;
/**
* Constructs a Field Attributes object from the {@code f}.
*
* @param f the field to pull attributes from
*/
public FieldAttributes(Field f) {
$Gson$Preconditions.checkNotNull(f);
this.field = f;
}
/**
* @return the declaring class that contains this field
*/
public Class<?> getDeclaringClass() {
return field.getDeclaringClass();
}
/**
* @return the name of the field
*/
public String getName() {
return field.getName();
}
/**
* <p>For example, assume the following class definition:
* <pre class="code">
* public class Foo {
* private String bar;
* private List&lt;String&gt; red;
* }
*
* Type listParameterizedType = new TypeToken&lt;List&lt;String&gt;&gt;() {}.getType();
* </pre>
*
* <p>This method would return {@code String.class} for the {@code bar} field and
* {@code listParameterizedType} for the {@code red} field.
*
* @return the specific type declared for this field
*/
public Type getDeclaredType() {
return field.getGenericType();
}
/**
* Returns the {@code Class} object that was declared for this field.
*
* <p>For example, assume the following class definition:
* <pre class="code">
* public class Foo {
* private String bar;
* private List&lt;String&gt; red;
* }
* </pre>
*
* <p>This method would return {@code String.class} for the {@code bar} field and
* {@code List.class} for the {@code red} field.
*
* @return the specific class object that was declared for the field
*/
public Class<?> getDeclaredClass() {
return field.getType();
}
/**
* Return the {@code T} annotation object from this field if it exist; otherwise returns
* {@code null}.
*
* @param annotation the class of the annotation that will be retrieved
* @return the annotation instance if it is bound to the field; otherwise {@code null}
*/
public <T extends Annotation> T getAnnotation(Class<T> annotation) {
return field.getAnnotation(annotation);
}
/**
* Return the annotations that are present on this field.
*
* @return an array of all the annotations set on the field
* @since 1.4
*/
public Collection<Annotation> getAnnotations() {
return Arrays.asList(field.getAnnotations());
}
/**
* Returns {@code true} if the field is defined with the {@code modifier}.
*
* <p>This method is meant to be called as:
* <pre class="code">
* boolean hasPublicModifier = fieldAttribute.hasModifier(java.lang.reflect.Modifier.PUBLIC);
* </pre>
*
* @see java.lang.reflect.Modifier
*/
public boolean hasModifier(int modifier) {
return (field.getModifiers() & modifier) != 0;
}
/**
* Returns the value of the field represented by this {@code Field}, on
* the specified object. The value is automatically wrapped in an
* object if it has a primitive type.
*
* @return the value of the represented field in object
* {@code obj}; primitive values are wrapped in an appropriate
* object before being returned
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
Object get(Object instance) throws IllegalAccessException {
return field.get(instance);
}
/**
* This is exposed internally only for the removing synthetic fields from the JSON output.
*
* @return true if the field is synthetic; otherwise false
*/
boolean isSynthetic() {
return field.isSynthetic();
}
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import com.sobot.gson.FieldNamingStrategy;
import java.lang.reflect.Field;
import java.util.Locale;
/**
* An enumeration that defines a few standard naming conventions for JSON field names.
* This enumeration should be used in conjunction with {@link com.sobot.gson.GsonBuilder}
* to configure a {@link com.sobot.gson.Gson} instance to properly translate Java field
* names into the desired JSON field names.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public enum FieldNamingPolicy implements FieldNamingStrategy {
/**
* Using this naming policy with Gson will ensure that the field name is
* unchanged.
*/
IDENTITY() {
@Override public String translateName(Field f) {
return f.getName();
}
},
/**
* Using this naming policy with Gson will ensure that the first "letter" of the Java
* field name is capitalized when serialized to its JSON form.
*
* <p>Here are a few examples of the form "Java Field Name" ---&gt; "JSON Field Name":</p>
* <ul>
* <li>someFieldName ---&gt; SomeFieldName</li>
* <li>_someFieldName ---&gt; _SomeFieldName</li>
* </ul>
*/
UPPER_CAMEL_CASE() {
@Override public String translateName(Field f) {
return upperCaseFirstLetter(f.getName());
}
},
/**
* Using this naming policy with Gson will ensure that the first "letter" of the Java
* field name is capitalized when serialized to its JSON form and the words will be
* separated by a space.
*
* <p>Here are a few examples of the form "Java Field Name" ---&gt; "JSON Field Name":</p>
* <ul>
* <li>someFieldName ---&gt; Some Field Name</li>
* <li>_someFieldName ---&gt; _Some Field Name</li>
* </ul>
*
* @since 1.4
*/
UPPER_CAMEL_CASE_WITH_SPACES() {
@Override public String translateName(Field f) {
return upperCaseFirstLetter(separateCamelCase(f.getName(), ' '));
}
},
/**
* Using this naming policy with Gson will modify the Java Field name from its camel cased
* form to an upper case field name where each word is separated by an underscore (_).
*
* <p>Here are a few examples of the form "Java Field Name" ---&gt; "JSON Field Name":</p>
* <ul>
* <li>someFieldName ---&gt; SOME_FIELD_NAME</li>
* <li>_someFieldName ---&gt; _SOME_FIELD_NAME</li>
* <li>aStringField ---&gt; A_STRING_FIELD</li>
* <li>aURL ---&gt; A_U_R_L</li>
* </ul>
*/
UPPER_CASE_WITH_UNDERSCORES() {
@Override public String translateName(Field f) {
return separateCamelCase(f.getName(), '_').toUpperCase(Locale.ENGLISH);
}
},
/**
* Using this naming policy with Gson will modify the Java Field name from its camel cased
* form to a lower case field name where each word is separated by an underscore (_).
*
* <p>Here are a few examples of the form "Java Field Name" ---&gt; "JSON Field Name":</p>
* <ul>
* <li>someFieldName ---&gt; some_field_name</li>
* <li>_someFieldName ---&gt; _some_field_name</li>
* <li>aStringField ---&gt; a_string_field</li>
* <li>aURL ---&gt; a_u_r_l</li>
* </ul>
*/
LOWER_CASE_WITH_UNDERSCORES() {
@Override public String translateName(Field f) {
return separateCamelCase(f.getName(), '_').toLowerCase(Locale.ENGLISH);
}
},
/**
* Using this naming policy with Gson will modify the Java Field name from its camel cased
* form to a lower case field name where each word is separated by a dash (-).
*
* <p>Here are a few examples of the form "Java Field Name" ---&gt; "JSON Field Name":</p>
* <ul>
* <li>someFieldName ---&gt; some-field-name</li>
* <li>_someFieldName ---&gt; _some-field-name</li>
* <li>aStringField ---&gt; a-string-field</li>
* <li>aURL ---&gt; a-u-r-l</li>
* </ul>
* Using dashes in JavaScript is not recommended since dash is also used for a minus sign in
* expressions. This requires that a field named with dashes is always accessed as a quoted
* property like {@code myobject['my-field']}. Accessing it as an object field
* {@code myobject.my-field} will result in an unintended javascript expression.
* @since 1.4
*/
LOWER_CASE_WITH_DASHES() {
@Override public String translateName(Field f) {
return separateCamelCase(f.getName(), '-').toLowerCase(Locale.ENGLISH);
}
},
/**
* Using this naming policy with Gson will modify the Java Field name from its camel cased
* form to a lower case field name where each word is separated by a dot (.).
*
* <p>Here are a few examples of the form "Java Field Name" ---&gt; "JSON Field Name":</p>
* <ul>
* <li>someFieldName ---&gt; some.field.name</li>
* <li>_someFieldName ---&gt; _some.field.name</li>
* <li>aStringField ---&gt; a.string.field</li>
* <li>aURL ---&gt; a.u.r.l</li>
* </ul>
* Using dots in JavaScript is not recommended since dot is also used for a member sign in
* expressions. This requires that a field named with dots is always accessed as a quoted
* property like {@code myobject['my.field']}. Accessing it as an object field
* {@code myobject.my.field} will result in an unintended javascript expression.
* @since 2.8
*/
LOWER_CASE_WITH_DOTS() {
@Override public String translateName(Field f) {
return separateCamelCase(f.getName(), '.').toLowerCase(Locale.ENGLISH);
}
};
/**
* Converts the field name that uses camel-case define word separation into
* separate words that are separated by the provided {@code separator}.
*/
static String separateCamelCase(String name, char separator) {
StringBuilder translation = new StringBuilder();
for (int i = 0, length = name.length(); i < length; i++) {
char character = name.charAt(i);
if (Character.isUpperCase(character) && translation.length() != 0) {
translation.append(separator);
}
translation.append(character);
}
return translation.toString();
}
/**
* Ensures the JSON field names begins with an upper case letter.
*/
static String upperCaseFirstLetter(String s) {
int length = s.length();
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
if (Character.isLetter(c)) {
if (Character.isUpperCase(c)) {
return s;
}
char uppercased = Character.toUpperCase(c);
// For leading letter only need one substring
if (i == 0) {
return uppercased + s.substring(1);
} else {
return s.substring(0, i) + uppercased + s.substring(i + 1);
}
}
}
return s;
}
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import java.lang.reflect.Field;
/**
* A mechanism for providing custom field naming in Gson. This allows the client code to translate
* field names into a particular convention that is not supported as a normal Java field
* declaration rules. For example, Java does not support "-" characters in a field name.
*
* @author Inderjeet Singh
* @author Joel Leitch
* @since 1.3
*/
public interface FieldNamingStrategy {
/**
* Translates the field name into its JSON field name representation.
*
* @param f the field object that we are translating
* @return the translated field name.
* @since 1.3
*/
public String translateName(Field f);
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import java.lang.reflect.Type;
/**
* This interface is implemented to create instances of a class that does not define a no-args
* constructor. If you can modify the class, you should instead add a private, or public
* no-args constructor. However, that is not possible for library classes, such as JDK classes, or
* a third-party library that you do not have source-code of. In such cases, you should define an
* instance creator for the class. Implementations of this interface should be registered with
* {@link GsonBuilder#registerTypeAdapter(Type, Object)} method before Gson will be able to use
* them.
* <p>Let us look at an example where defining an InstanceCreator might be useful. The
* {@code Id} class defined below does not have a default no-args constructor.</p>
*
* <pre>
* public class Id&lt;T&gt; {
* private final Class&lt;T&gt; clazz;
* private final long value;
* public Id(Class&lt;T&gt; clazz, long value) {
* this.clazz = clazz;
* this.value = value;
* }
* }
* </pre>
*
* <p>If Gson encounters an object of type {@code Id} during deserialization, it will throw an
* exception. The easiest way to solve this problem will be to add a (public or private) no-args
* constructor as follows:</p>
*
* <pre>
* private Id() {
* this(Object.class, 0L);
* }
* </pre>
*
* <p>However, let us assume that the developer does not have access to the source-code of the
* {@code Id} class, or does not want to define a no-args constructor for it. The developer
* can solve this problem by defining an {@code InstanceCreator} for {@code Id}:</p>
*
* <pre>
* class IdInstanceCreator implements InstanceCreator&lt;Id&gt; {
* public Id createInstance(Type type) {
* return new Id(Object.class, 0L);
* }
* }
* </pre>
*
* <p>Note that it does not matter what the fields of the created instance contain since Gson will
* overwrite them with the deserialized values specified in Json. You should also ensure that a
* <i>new</i> object is returned, not a common object since its fields will be overwritten.
* The developer will need to register {@code IdInstanceCreator} with Gson as follows:</p>
*
* <pre>
* Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdInstanceCreator()).create();
* </pre>
*
* @param <T> the type of object that will be created by this implementation.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public interface InstanceCreator<T> {
/**
* Gson invokes this call-back method during deserialization to create an instance of the
* specified type. The fields of the returned instance are overwritten with the data present
* in the Json. Since the prior contents of the object are destroyed and overwritten, do not
* return an instance that is useful elsewhere. In particular, do not return a common instance,
* always use {@code new} to create a new instance.
*
* @param type the parameterized T represented as a {@link Type}.
* @return a default object instance of type T.
*/
public T createInstance(Type type);
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import com.sobot.gson.JsonDeserializer;
import com.sobot.gson.JsonElement;
import com.sobot.gson.JsonParseException;
import java.lang.reflect.Type;
/**
* Context for deserialization that is passed to a custom deserializer during invocation of its
* {@link JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)}
* method.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public interface JsonDeserializationContext {
/**
* Invokes default deserialization on the specified object. It should never be invoked on
* the element received as a parameter of the
* {@link JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)} method. Doing
* so will result in an infinite loop since Gson will in-turn call the custom deserializer again.
*
* @param json the parse tree.
* @param typeOfT type of the expected return value.
* @param <T> The type of the deserialized object.
* @return An object of type typeOfT.
* @throws JsonParseException if the parse tree does not contain expected data.
*/
public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException;
}
\ No newline at end of file
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import com.sobot.gson.JsonElement;
import com.sobot.gson.JsonParseException;
import com.sobot.gson.TypeAdapter;
import java.lang.reflect.Type;
/**
* <p>Interface representing a custom deserializer for Json. You should write a custom
* deserializer, if you are not happy with the default deserialization done by Gson. You will
* also need to register this deserializer through
* {@link GsonBuilder#registerTypeAdapter(Type, Object)}.</p>
*
* <p>Let us look at example where defining a deserializer will be useful. The {@code Id} class
* defined below has two fields: {@code clazz} and {@code value}.</p>
*
* <pre>
* public class Id&lt;T&gt; {
* private final Class&lt;T&gt; clazz;
* private final long value;
* public Id(Class&lt;T&gt; clazz, long value) {
* this.clazz = clazz;
* this.value = value;
* }
* public long getValue() {
* return value;
* }
* }
* </pre>
*
* <p>The default deserialization of {@code Id(com.foo.MyObject.class, 20L)} will require the
* Json string to be <code>{"clazz":com.foo.MyObject,"value":20}</code>. Suppose, you already know
* the type of the field that the {@code Id} will be deserialized into, and hence just want to
* deserialize it from a Json string {@code 20}. You can achieve that by writing a custom
* deserializer:</p>
*
* <pre>
* class IdDeserializer implements JsonDeserializer&lt;Id&gt;() {
* public Id deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
* throws JsonParseException {
* return new Id((Class)typeOfT, id.getValue());
* }
* </pre>
*
* <p>You will also need to register {@code IdDeserializer} with Gson as follows:</p>
*
* <pre>
* Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdDeserializer()).create();
* </pre>
*
* <p>New applications should prefer {@link TypeAdapter}, whose streaming API
* is more efficient than this interface's tree API.
*
* @author Inderjeet Singh
* @author Joel Leitch
*
* @param <T> type for which the deserializer is being registered. It is possible that a
* deserializer may be asked to deserialize a specific generic type of the T.
*/
public interface JsonDeserializer<T> {
/**
* Gson invokes this call-back method during deserialization when it encounters a field of the
* specified type.
* <p>In the implementation of this call-back method, you should consider invoking
* {@link JsonDeserializationContext#deserialize(JsonElement, Type)} method to create objects
* for any non-trivial field of the returned object. However, you should never invoke it on the
* the same type passing {@code json} since that will cause an infinite loop (Gson will call your
* call-back method again).
*
* @param json The Json data being deserialized
* @param typeOfT The type of the Object to deserialize to
* @return a deserialized object of the specified type typeOfT which is a subclass of {@code T}
* @throws JsonParseException if json is not in the expected format of {@code typeofT}
*/
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException;
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import com.sobot.gson.JsonParseException;
/**
* This exception is raised when Gson was unable to read an input stream
* or write to one.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public final class JsonIOException extends JsonParseException {
private static final long serialVersionUID = 1L;
public JsonIOException(String msg) {
super(msg);
}
public JsonIOException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Creates exception with the specified cause. Consider using
* {@link #JsonIOException(String, Throwable)} instead if you can describe what happened.
*
* @param cause root exception that caused this exception to be thrown.
*/
public JsonIOException(Throwable cause) {
super(cause);
}
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
/**
* A class representing a Json {@code null} value.
*
* @author Inderjeet Singh
* @author Joel Leitch
* @since 1.2
*/
public final class JsonNull extends JsonElement {
/**
* singleton for JsonNull
*
* @since 1.8
*/
public static final JsonNull INSTANCE = new JsonNull();
/**
* Creates a new JsonNull object.
* Deprecated since Gson version 1.8. Use {@link #INSTANCE} instead
*/
@Deprecated
public JsonNull() {
// Do nothing
}
/**
* Returns the same instance since it is an immutable value
* @since 2.8.2
*/
@Override
public JsonNull deepCopy() {
return INSTANCE;
}
/**
* All instances of JsonNull have the same hash code since they are indistinguishable
*/
@Override
public int hashCode() {
return JsonNull.class.hashCode();
}
/**
* All instances of JsonNull are the same
*/
@Override
public boolean equals(Object other) {
return this == other || other instanceof JsonNull;
}
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import com.sobot.gson.JsonPrimitive;
import com.sobot.gson.internal.LinkedTreeMap;
import java.util.Map;
import java.util.Set;
/**
* A class representing an object type in Json. An object consists of name-value pairs where names
* are strings, and values are any other type of {@link JsonElement}. This allows for a creating a
* tree of JsonElements. The member elements of this object are maintained in order they were added.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public final class JsonObject extends JsonElement {
private final LinkedTreeMap<String, JsonElement> members = new LinkedTreeMap<>();
/**
* Creates a deep copy of this element and all its children
* @since 2.8.2
*/
@Override
public JsonObject deepCopy() {
JsonObject result = new JsonObject();
for (Map.Entry<String, JsonElement> entry : members.entrySet()) {
result.add(entry.getKey(), entry.getValue().deepCopy());
}
return result;
}
/**
* Adds a member, which is a name-value pair, to self. The name must be a String, but the value
* can be an arbitrary JsonElement, thereby allowing you to build a full tree of JsonElements
* rooted at this node.
*
* @param property name of the member.
* @param value the member object.
*/
public void add(String property, JsonElement value) {
members.put(property, value == null ? JsonNull.INSTANCE : value);
}
/**
* Removes the {@code property} from this {@link JsonObject}.
*
* @param property name of the member that should be removed.
* @return the {@link JsonElement} object that is being removed.
* @since 1.3
*/
public JsonElement remove(String property) {
return members.remove(property);
}
/**
* Convenience method to add a primitive member. The specified value is converted to a
* JsonPrimitive of String.
*
* @param property name of the member.
* @param value the string value associated with the member.
*/
public void addProperty(String property, String value) {
add(property, value == null ? JsonNull.INSTANCE : new JsonPrimitive(value));
}
/**
* Convenience method to add a primitive member. The specified value is converted to a
* JsonPrimitive of Number.
*
* @param property name of the member.
* @param value the number value associated with the member.
*/
public void addProperty(String property, Number value) {
add(property, value == null ? JsonNull.INSTANCE : new JsonPrimitive(value));
}
/**
* Convenience method to add a boolean member. The specified value is converted to a
* JsonPrimitive of Boolean.
*
* @param property name of the member.
* @param value the number value associated with the member.
*/
public void addProperty(String property, Boolean value) {
add(property, value == null ? JsonNull.INSTANCE : new JsonPrimitive(value));
}
/**
* Convenience method to add a char member. The specified value is converted to a
* JsonPrimitive of Character.
*
* @param property name of the member.
* @param value the number value associated with the member.
*/
public void addProperty(String property, Character value) {
add(property, value == null ? JsonNull.INSTANCE : new JsonPrimitive(value));
}
/**
* Returns a set of members of this object. The set is ordered, and the order is in which the
* elements were added.
*
* @return a set of members of this object.
*/
public Set<Map.Entry<String, JsonElement>> entrySet() {
return members.entrySet();
}
/**
* Returns a set of members key values.
*
* @return a set of member keys as Strings
* @since 2.8.1
*/
public Set<String> keySet() {
return members.keySet();
}
/**
* Returns the number of key/value pairs in the object.
*
* @return the number of key/value pairs in the object.
*/
public int size() {
return members.size();
}
/**
* Convenience method to check if a member with the specified name is present in this object.
*
* @param memberName name of the member that is being checked for presence.
* @return true if there is a member with the specified name, false otherwise.
*/
public boolean has(String memberName) {
return members.containsKey(memberName);
}
/**
* Returns the member with the specified name.
*
* @param memberName name of the member that is being requested.
* @return the member matching the name. Null if no such member exists.
*/
public JsonElement get(String memberName) {
return members.get(memberName);
}
/**
* Convenience method to get the specified member as a JsonPrimitive element.
*
* @param memberName name of the member being requested.
* @return the JsonPrimitive corresponding to the specified member.
*/
public JsonPrimitive getAsJsonPrimitive(String memberName) {
return (JsonPrimitive) members.get(memberName);
}
/**
* Convenience method to get the specified member as a JsonArray.
*
* @param memberName name of the member being requested.
* @return the JsonArray corresponding to the specified member.
*/
public JsonArray getAsJsonArray(String memberName) {
return (JsonArray) members.get(memberName);
}
/**
* Convenience method to get the specified member as a JsonObject.
*
* @param memberName name of the member being requested.
* @return the JsonObject corresponding to the specified member.
*/
public JsonObject getAsJsonObject(String memberName) {
return (JsonObject) members.get(memberName);
}
@Override
public boolean equals(Object o) {
return (o == this) || (o instanceof JsonObject
&& ((JsonObject) o).members.equals(members));
}
@Override
public int hashCode() {
return members.hashCode();
}
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
/**
* This exception is raised if there is a serious issue that occurs during parsing of a Json
* string. One of the main usages for this class is for the Gson infrastructure. If the incoming
* Json is bad/malicious, an instance of this exception is raised.
*
* <p>This exception is a {@link RuntimeException} because it is exposed to the client. Using a
* {@link RuntimeException} avoids bad coding practices on the client side where they catch the
* exception and do nothing. It is often the case that you want to blow up if there is a parsing
* error (i.e. often clients do not know how to recover from a {@link JsonParseException}.</p>
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public class JsonParseException extends RuntimeException {
static final long serialVersionUID = -4086729973971783390L;
/**
* Creates exception with the specified message. If you are wrapping another exception, consider
* using {@link #JsonParseException(String, Throwable)} instead.
*
* @param msg error message describing a possible cause of this exception.
*/
public JsonParseException(String msg) {
super(msg);
}
/**
* Creates exception with the specified message and cause.
*
* @param msg error message describing what happened.
* @param cause root exception that caused this exception to be thrown.
*/
public JsonParseException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Creates exception with the specified cause. Consider using
* {@link #JsonParseException(String, Throwable)} instead if you can describe what happened.
*
* @param cause root exception that caused this exception to be thrown.
*/
public JsonParseException(Throwable cause) {
super(cause);
}
}
/*
* Copyright (C) 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import com.sobot.gson.JsonSyntaxException;
import com.sobot.gson.internal.Streams;
import com.sobot.gson.stream.JsonReader;
import com.sobot.gson.stream.JsonToken;
import com.sobot.gson.stream.MalformedJsonException;
/**
* A parser to parse Json into a parse tree of {@link JsonElement}s
*
* @author Inderjeet Singh
* @author Joel Leitch
* @since 1.3
*/
public final class JsonParser {
/** @deprecated No need to instantiate this class, use the static methods instead. */
@Deprecated
public JsonParser() {}
/**
* Parses the specified JSON string into a parse tree
*
* @param json JSON text
* @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
* @throws JsonParseException if the specified text is not valid JSON
*/
public static JsonElement parseString(String json) throws JsonSyntaxException {
return parseReader(new StringReader(json));
}
/**
* Parses the specified JSON string into a parse tree
*
* @param reader JSON text
* @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
* @throws JsonParseException if the specified text is not valid JSON
*/
public static JsonElement parseReader(Reader reader) throws JsonIOException, JsonSyntaxException {
try {
JsonReader jsonReader = new JsonReader(reader);
JsonElement element = parseReader(jsonReader);
if (!element.isJsonNull() && jsonReader.peek() != JsonToken.END_DOCUMENT) {
throw new JsonSyntaxException("Did not consume the entire document.");
}
return element;
} catch (MalformedJsonException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
throw new JsonIOException(e);
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
/**
* Returns the next value from the JSON stream as a parse tree.
*
* @throws JsonParseException if there is an IOException or if the specified
* text is not valid JSON
*/
public static JsonElement parseReader(JsonReader reader)
throws JsonIOException, JsonSyntaxException {
boolean lenient = reader.isLenient();
reader.setLenient(true);
try {
return Streams.parse(reader);
} catch (StackOverflowError e) {
throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e);
} catch (OutOfMemoryError e) {
throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e);
} finally {
reader.setLenient(lenient);
}
}
/** @deprecated Use {@link JsonParser#parseString} */
@Deprecated
public JsonElement parse(String json) throws JsonSyntaxException {
return parseString(json);
}
/** @deprecated Use {@link JsonParser#parseReader(Reader)} */
@Deprecated
public JsonElement parse(Reader json) throws JsonIOException, JsonSyntaxException {
return parseReader(json);
}
/** @deprecated Use {@link JsonParser#parseReader(JsonReader)} */
@Deprecated
public JsonElement parse(JsonReader json) throws JsonIOException, JsonSyntaxException {
return parseReader(json);
}
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import com.sobot.gson.internal.$Gson$Preconditions;
import java.math.BigDecimal;
import java.math.BigInteger;
import com.sobot.gson.internal.LazilyParsedNumber;
/**
* A class representing a Json primitive value. A primitive value
* is either a String, a Java primitive, or a Java primitive
* wrapper type.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public final class JsonPrimitive extends JsonElement {
private final Object value;
/**
* Create a primitive containing a boolean value.
*
* @param bool the value to create the primitive with.
*/
public JsonPrimitive(Boolean bool) {
value = $Gson$Preconditions.checkNotNull(bool);
}
/**
* Create a primitive containing a {@link Number}.
*
* @param number the value to create the primitive with.
*/
public JsonPrimitive(Number number) {
value = $Gson$Preconditions.checkNotNull(number);
}
/**
* Create a primitive containing a String value.
*
* @param string the value to create the primitive with.
*/
public JsonPrimitive(String string) {
value = $Gson$Preconditions.checkNotNull(string);
}
/**
* Create a primitive containing a character. The character is turned into a one character String
* since Json only supports String.
*
* @param c the value to create the primitive with.
*/
public JsonPrimitive(Character c) {
// convert characters to strings since in JSON, characters are represented as a single
// character string
value = $Gson$Preconditions.checkNotNull(c).toString();
}
/**
* Returns the same value as primitives are immutable.
* @since 2.8.2
*/
@Override
public JsonPrimitive deepCopy() {
return this;
}
/**
* Check whether this primitive contains a boolean value.
*
* @return true if this primitive contains a boolean value, false otherwise.
*/
public boolean isBoolean() {
return value instanceof Boolean;
}
/**
* convenience method to get this element as a boolean value.
*
* @return get this element as a primitive boolean value.
*/
@Override
public boolean getAsBoolean() {
if (isBoolean()) {
return ((Boolean) value).booleanValue();
}
// Check to see if the value as a String is "true" in any case.
return Boolean.parseBoolean(getAsString());
}
/**
* Check whether this primitive contains a Number.
*
* @return true if this primitive contains a Number, false otherwise.
*/
public boolean isNumber() {
return value instanceof Number;
}
/**
* convenience method to get this element as a Number.
*
* @return get this element as a Number.
* @throws NumberFormatException if the value contained is not a valid Number.
*/
@Override
public Number getAsNumber() {
return value instanceof String ? new LazilyParsedNumber((String) value) : (Number) value;
}
/**
* Check whether this primitive contains a String value.
*
* @return true if this primitive contains a String value, false otherwise.
*/
public boolean isString() {
return value instanceof String;
}
/**
* convenience method to get this element as a String.
*
* @return get this element as a String.
*/
@Override
public String getAsString() {
if (isNumber()) {
return getAsNumber().toString();
} else if (isBoolean()) {
return ((Boolean) value).toString();
} else {
return (String) value;
}
}
/**
* convenience method to get this element as a primitive double.
*
* @return get this element as a primitive double.
* @throws NumberFormatException if the value contained is not a valid double.
*/
@Override
public double getAsDouble() {
return isNumber() ? getAsNumber().doubleValue() : Double.parseDouble(getAsString());
}
/**
* convenience method to get this element as a {@link BigDecimal}.
*
* @return get this element as a {@link BigDecimal}.
* @throws NumberFormatException if the value contained is not a valid {@link BigDecimal}.
*/
@Override
public BigDecimal getAsBigDecimal() {
return value instanceof BigDecimal ? (BigDecimal) value : new BigDecimal(value.toString());
}
/**
* convenience method to get this element as a {@link BigInteger}.
*
* @return get this element as a {@link BigInteger}.
* @throws NumberFormatException if the value contained is not a valid {@link BigInteger}.
*/
@Override
public BigInteger getAsBigInteger() {
return value instanceof BigInteger ?
(BigInteger) value : new BigInteger(value.toString());
}
/**
* convenience method to get this element as a float.
*
* @return get this element as a float.
* @throws NumberFormatException if the value contained is not a valid float.
*/
@Override
public float getAsFloat() {
return isNumber() ? getAsNumber().floatValue() : Float.parseFloat(getAsString());
}
/**
* convenience method to get this element as a primitive long.
*
* @return get this element as a primitive long.
* @throws NumberFormatException if the value contained is not a valid long.
*/
@Override
public long getAsLong() {
return isNumber() ? getAsNumber().longValue() : Long.parseLong(getAsString());
}
/**
* convenience method to get this element as a primitive short.
*
* @return get this element as a primitive short.
* @throws NumberFormatException if the value contained is not a valid short value.
*/
@Override
public short getAsShort() {
return isNumber() ? getAsNumber().shortValue() : Short.parseShort(getAsString());
}
/**
* convenience method to get this element as a primitive integer.
*
* @return get this element as a primitive integer.
* @throws NumberFormatException if the value contained is not a valid integer.
*/
@Override
public int getAsInt() {
return isNumber() ? getAsNumber().intValue() : Integer.parseInt(getAsString());
}
@Override
public byte getAsByte() {
return isNumber() ? getAsNumber().byteValue() : Byte.parseByte(getAsString());
}
@Override
public char getAsCharacter() {
return getAsString().charAt(0);
}
@Override
public int hashCode() {
if (value == null) {
return 31;
}
// Using recommended hashing algorithm from Effective Java for longs and doubles
if (isIntegral(this)) {
long value = getAsNumber().longValue();
return (int) (value ^ (value >>> 32));
}
if (value instanceof Number) {
long value = Double.doubleToLongBits(getAsNumber().doubleValue());
return (int) (value ^ (value >>> 32));
}
return value.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
JsonPrimitive other = (JsonPrimitive)obj;
if (value == null) {
return other.value == null;
}
if (isIntegral(this) && isIntegral(other)) {
return getAsNumber().longValue() == other.getAsNumber().longValue();
}
if (value instanceof Number && other.value instanceof Number) {
double a = getAsNumber().doubleValue();
// Java standard types other than double return true for two NaN. So, need
// special handling for double.
double b = other.getAsNumber().doubleValue();
return a == b || (Double.isNaN(a) && Double.isNaN(b));
}
return value.equals(other.value);
}
/**
* Returns true if the specified number is an integral type
* (Long, Integer, Short, Byte, BigInteger)
*/
private static boolean isIntegral(JsonPrimitive primitive) {
if (primitive.value instanceof Number) {
Number number = (Number) primitive.value;
return number instanceof BigInteger || number instanceof Long || number instanceof Integer
|| number instanceof Short || number instanceof Byte;
}
return false;
}
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import com.sobot.gson.JsonSerializer;
import java.lang.reflect.Type;
/**
* Context for serialization that is passed to a custom serializer during invocation of its
* {@link JsonSerializer#serialize(Object, Type, JsonSerializationContext)} method.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public interface JsonSerializationContext {
/**
* Invokes default serialization on the specified object.
*
* @param src the object that needs to be serialized.
* @return a tree of {@link JsonElement}s corresponding to the serialized form of {@code src}.
*/
public JsonElement serialize(Object src);
/**
* Invokes default serialization on the specified object passing the specific type information.
* It should never be invoked on the element received as a parameter of the
* {@link JsonSerializer#serialize(Object, Type, JsonSerializationContext)} method. Doing
* so will result in an infinite loop since Gson will in-turn call the custom serializer again.
*
* @param src the object that needs to be serialized.
* @param typeOfSrc the actual genericized type of src object.
* @return a tree of {@link JsonElement}s corresponding to the serialized form of {@code src}.
*/
public JsonElement serialize(Object src, Type typeOfSrc);
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import com.sobot.gson.TypeAdapter;
import java.lang.reflect.Type;
/**
* Interface representing a custom serializer for Json. You should write a custom serializer, if
* you are not happy with the default serialization done by Gson. You will also need to register
* this serializer through {@link com.sobot.gson.GsonBuilder#registerTypeAdapter(Type, Object)}.
*
* <p>Let us look at example where defining a serializer will be useful. The {@code Id} class
* defined below has two fields: {@code clazz} and {@code value}.</p>
*
* <pre>
* public class Id&lt;T&gt; {
* private final Class&lt;T&gt; clazz;
* private final long value;
*
* public Id(Class&lt;T&gt; clazz, long value) {
* this.clazz = clazz;
* this.value = value;
* }
*
* public long getValue() {
* return value;
* }
* }
* </pre>
*
* <p>The default serialization of {@code Id(com.foo.MyObject.class, 20L)} will be
* <code>{"clazz":com.foo.MyObject,"value":20}</code>. Suppose, you just want the output to be
* the value instead, which is {@code 20} in this case. You can achieve that by writing a custom
* serializer:</p>
*
* <pre>
* class IdSerializer implements JsonSerializer&lt;Id&gt;() {
* public JsonElement serialize(Id id, Type typeOfId, JsonSerializationContext context) {
* return new JsonPrimitive(id.getValue());
* }
* }
* </pre>
*
* <p>You will also need to register {@code IdSerializer} with Gson as follows:</p>
* <pre>
* Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdSerializer()).create();
* </pre>
*
* <p>New applications should prefer {@link TypeAdapter}, whose streaming API
* is more efficient than this interface's tree API.
*
* @author Inderjeet Singh
* @author Joel Leitch
*
* @param <T> type for which the serializer is being registered. It is possible that a serializer
* may be asked to serialize a specific generic type of the T.
*/
public interface JsonSerializer<T> {
/**
* Gson invokes this call-back method during serialization when it encounters a field of the
* specified type.
*
* <p>In the implementation of this call-back method, you should consider invoking
* {@link JsonSerializationContext#serialize(Object, Type)} method to create JsonElements for any
* non-trivial field of the {@code src} object. However, you should never invoke it on the
* {@code src} object itself since that will cause an infinite loop (Gson will call your
* call-back method again).</p>
*
* @param src the object that needs to be converted to Json.
* @param typeOfSrc the actual type (fully genericized version) of the source object.
* @return a JsonElement corresponding to the specified object.
*/
public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context);
}
/*
* Copyright (C) 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import java.io.EOFException;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.Iterator;
import java.util.NoSuchElementException;
import com.sobot.gson.JsonSyntaxException;
import com.sobot.gson.internal.Streams;
import com.sobot.gson.stream.JsonReader;
import com.sobot.gson.stream.JsonToken;
import com.sobot.gson.stream.MalformedJsonException;
/**
* A streaming parser that allows reading of multiple {@link JsonElement}s from the specified reader
* asynchronously. The JSON data is parsed in lenient mode, see also
* {@link JsonReader#setLenient(boolean)}.
*
* <p>This class is conditionally thread-safe (see Item 70, Effective Java second edition). To
* properly use this class across multiple threads, you will need to add some external
* synchronization. For example:
*
* <pre>
* JsonStreamParser parser = new JsonStreamParser("['first'] {'second':10} 'third'");
* JsonElement element;
* synchronized (parser) { // synchronize on an object shared by threads
* if (parser.hasNext()) {
* element = parser.next();
* }
* }
* </pre>
*
* @author Inderjeet Singh
* @author Joel Leitch
* @since 1.4
*/
public final class JsonStreamParser implements Iterator<JsonElement> {
private final JsonReader parser;
private final Object lock;
/**
* @param json The string containing JSON elements concatenated to each other.
* @since 1.4
*/
public JsonStreamParser(String json) {
this(new StringReader(json));
}
/**
* @param reader The data stream containing JSON elements concatenated to each other.
* @since 1.4
*/
public JsonStreamParser(Reader reader) {
parser = new JsonReader(reader);
parser.setLenient(true);
lock = new Object();
}
/**
* Returns the next available {@link JsonElement} on the reader. Throws a
* {@link NoSuchElementException} if no element is available.
*
* @return the next available {@code JsonElement} on the reader.
* @throws JsonSyntaxException if the incoming stream is malformed JSON.
* @throws NoSuchElementException if no {@code JsonElement} is available.
* @since 1.4
*/
@Override
public JsonElement next() throws JsonParseException {
if (!hasNext()) {
throw new NoSuchElementException();
}
try {
return Streams.parse(parser);
} catch (StackOverflowError e) {
throw new JsonParseException("Failed parsing JSON source to Json", e);
} catch (OutOfMemoryError e) {
throw new JsonParseException("Failed parsing JSON source to Json", e);
} catch (JsonParseException e) {
throw e.getCause() instanceof EOFException ? new NoSuchElementException() : e;
}
}
/**
* Returns true if a {@link JsonElement} is available on the input for consumption
* @return true if a {@link JsonElement} is available on the input, false otherwise
* @throws JsonSyntaxException if the incoming stream is malformed JSON.
* @since 1.4
*/
@Override
public boolean hasNext() {
synchronized (lock) {
try {
return parser.peek() != JsonToken.END_DOCUMENT;
} catch (MalformedJsonException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
throw new JsonIOException(e);
}
}
}
/**
* This optional {@link Iterator} method is not relevant for stream parsing and hence is not
* implemented.
* @since 1.4
*/
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
/*
* Copyright (C) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
/**
* This exception is raised when Gson attempts to read (or write) a malformed
* JSON element.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public final class JsonSyntaxException extends JsonParseException {
private static final long serialVersionUID = 1L;
public JsonSyntaxException(String msg) {
super(msg);
}
public JsonSyntaxException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Creates exception with the specified cause. Consider using
* {@link #JsonSyntaxException(String, Throwable)} instead if you can
* describe what actually happened.
*
* @param cause root exception that caused this exception to be thrown.
*/
public JsonSyntaxException(Throwable cause) {
super(cause);
}
}
/*
* Copyright (C) 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
/**
* Defines the expected format for a {@code long} or {@code Long} type when it is serialized.
*
* @since 1.3
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public enum LongSerializationPolicy {
/**
* This is the "default" serialization policy that will output a {@code Long} object as a JSON
* number. For example, assume an object has a long field named "f" then the serialized output
* would be:
* {@code {"f":123}}
*
* <p>A {@code null} value is serialized as {@link JsonNull}.
*/
DEFAULT() {
@Override public JsonElement serialize(Long value) {
if (value == null) {
return JsonNull.INSTANCE;
}
return new JsonPrimitive(value);
}
},
/**
* Serializes a long value as a quoted string. For example, assume an object has a long field
* named "f" then the serialized output would be:
* {@code {"f":"123"}}
*
* <p>A {@code null} value is serialized as {@link JsonNull}.
*/
STRING() {
@Override public JsonElement serialize(Long value) {
if (value == null) {
return JsonNull.INSTANCE;
}
return new JsonPrimitive(value.toString());
}
};
/**
* Serialize this {@code value} using this serialization policy.
*
* @param value the long value to be serialized into a {@link JsonElement}
* @return the serialized version of {@code value}
*/
public abstract JsonElement serialize(Long value);
}
package com.sobot.gson;
import java.lang.reflect.AccessibleObject;
import com.sobot.gson.TypeAdapter;
import com.sobot.gson.internal.ReflectionAccessFilterHelper;
/**
* Filter for determining whether reflection based serialization and
* deserialization is allowed for a class.
*
* <p>A filter can be useful in multiple scenarios, for example when
* upgrading to newer Java versions which use the Java Platform Module
* System (JPMS). A filter then allows to {@linkplain FilterResult#BLOCK_INACCESSIBLE
* prevent making inaccessible members accessible}, even if the used
* Java version might still allow illegal access (but logs a warning),
* or if {@code java} command line arguments are used to open the inaccessible
* packages to other parts of the application. This interface defines some
* convenience filters for this task, such as {@link #BLOCK_INACCESSIBLE_JAVA}.
*
* <p>A filter can also be useful to prevent mixing model classes of a
* project with other non-model classes; the filter could
* {@linkplain FilterResult#BLOCK_ALL block all reflective access} to
* non-model classes.
*
* <p>A reflection access filter is similar to an {@link ExclusionStrategy}
* with the major difference that a filter will cause an exception to be
* thrown when access is disallowed while an exclusion strategy just skips
* fields and classes.
*
* @see GsonBuilder#addReflectionAccessFilter(ReflectionAccessFilter)
*/
public interface ReflectionAccessFilter {
/**
* Result of a filter check.
*/
enum FilterResult {
/**
* Reflection access for the class is allowed.
*
* <p>Note that this does not affect the Java access checks in any way,
* it only permits Gson to try using reflection for a class. The Java
* runtime might still deny such access.
*/
ALLOW,
/**
* The filter is indecisive whether reflection access should be allowed.
* The next registered filter will be consulted to get the result. If
* there is no next filter, this result acts like {@link #ALLOW}.
*/
INDECISIVE,
/**
* Blocks reflection access if a member of the class is not accessible
* by default and would have to be made accessible. This is unaffected
* by any {@code java} command line arguments being used to make packages
* accessible, or by module declaration directives which <i>open</i> the
* complete module or certain packages for reflection and will consider
* such packages inaccessible.
*
* <p>Note that this <b>only works for Java 9 and higher</b>, for older
* Java versions its functionality will be limited and it might behave like
* {@link #ALLOW}. Access checks are only performed as defined by the Java
* Language Specification (<a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-6.html#jls-6.6">JLS 11 &sect;6.6</a>),
* restrictions imposed by a {@link SecurityManager} are not considered.
*
* <p>This result type is mainly intended to help enforce the access checks of
* the Java Platform Module System. It allows detecting illegal access, even if
* the used Java version would only log a warning, or is configured to open
* packages for reflection using command line arguments.
*
* @see AccessibleObject#canAccess(Object)
*/
BLOCK_INACCESSIBLE,
/**
* Blocks all reflection access for the class. Other means for serializing
* and deserializing the class, such as a {@link TypeAdapter}, have to
* be used.
*/
BLOCK_ALL
}
/**
* Blocks all reflection access to members of standard Java classes which are
* not accessible by default. However, reflection access is still allowed for
* classes for which all fields are accessible and which have an accessible
* no-args constructor (or for which an {@link InstanceCreator} has been registered).
*
* <p>If this filter encounters a class other than a standard Java class it
* returns {@link FilterResult#INDECISIVE}.
*
* <p>This filter is mainly intended to help enforcing the access checks of
* Java Platform Module System. It allows detecting illegal access, even if
* the used Java version would only log a warning, or is configured to open
* packages for reflection. However, this filter <b>only works for Java 9 and
* higher</b>, when using an older Java version its functionality will be
* limited.
*
* <p>Note that this filter might not cover all standard Java classes. Currently
* only classes in a {@code java.*} or {@code javax.*} package are considered. The
* set of detected classes might be expanded in the future without prior notice.
*
* @see FilterResult#BLOCK_INACCESSIBLE
*/
ReflectionAccessFilter BLOCK_INACCESSIBLE_JAVA = new ReflectionAccessFilter() {
@Override public FilterResult check(Class<?> rawClass) {
return ReflectionAccessFilterHelper.isJavaType(rawClass)
? FilterResult.BLOCK_INACCESSIBLE
: FilterResult.INDECISIVE;
}
};
/**
* Blocks all reflection access to members of standard Java classes.
*
* <p>If this filter encounters a class other than a standard Java class it
* returns {@link FilterResult#INDECISIVE}.
*
* <p>This filter is mainly intended to prevent depending on implementation
* details of the Java platform and to help applications prepare for upgrading
* to the Java Platform Module System.
*
* <p>Note that this filter might not cover all standard Java classes. Currently
* only classes in a {@code java.*} or {@code javax.*} package are considered. The
* set of detected classes might be expanded in the future without prior notice.
*
* @see #BLOCK_INACCESSIBLE_JAVA
* @see FilterResult#BLOCK_ALL
*/
ReflectionAccessFilter BLOCK_ALL_JAVA = new ReflectionAccessFilter() {
@Override public FilterResult check(Class<?> rawClass) {
return ReflectionAccessFilterHelper.isJavaType(rawClass)
? FilterResult.BLOCK_ALL
: FilterResult.INDECISIVE;
}
};
/**
* Blocks all reflection access to members of standard Android classes.
*
* <p>If this filter encounters a class other than a standard Android class it
* returns {@link FilterResult#INDECISIVE}.
*
* <p>This filter is mainly intended to prevent depending on implementation
* details of the Android platform.
*
* <p>Note that this filter might not cover all standard Android classes. Currently
* only classes in an {@code android.*} or {@code androidx.*} package, and standard
* Java classes in a {@code java.*} or {@code javax.*} package are considered. The
* set of detected classes might be expanded in the future without prior notice.
*
* @see FilterResult#BLOCK_ALL
*/
ReflectionAccessFilter BLOCK_ALL_ANDROID = new ReflectionAccessFilter() {
@Override public FilterResult check(Class<?> rawClass) {
return ReflectionAccessFilterHelper.isAndroidType(rawClass)
? FilterResult.BLOCK_ALL
: FilterResult.INDECISIVE;
}
};
/**
* Blocks all reflection access to members of classes belonging to programming
* language platforms, such as Java, Android, Kotlin or Scala.
*
* <p>If this filter encounters a class other than a standard platform class it
* returns {@link FilterResult#INDECISIVE}.
*
* <p>This filter is mainly intended to prevent depending on implementation
* details of the platform classes.
*
* <p>Note that this filter might not cover all platform classes. Currently it
* combines the filters {@link #BLOCK_ALL_JAVA} and {@link #BLOCK_ALL_ANDROID},
* and checks for other language-specific platform classes like {@code kotlin.*}.
* The set of detected classes might be expanded in the future without prior notice.
*
* @see FilterResult#BLOCK_ALL
*/
ReflectionAccessFilter BLOCK_ALL_PLATFORM = new ReflectionAccessFilter() {
@Override public FilterResult check(Class<?> rawClass) {
return ReflectionAccessFilterHelper.isAnyPlatformType(rawClass)
? FilterResult.BLOCK_ALL
: FilterResult.INDECISIVE;
}
};
/**
* Checks if reflection access should be allowed for a class.
*
* @param rawClass
* Class to check
* @return
* Result indicating whether reflection access is allowed
*/
FilterResult check(Class<?> rawClass);
}
package com.sobot.gson;
import android.util.Log;
import com.sobot.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class SobotGsonUtil {
public static final String TAG = SobotGsonUtil.class.getSimpleName();
// 无参的私有构造方法
private SobotGsonUtil() {
}
// 不用创建对象,直接使用 gson. 就可以调用方法
private static Gson gson = null;
/**
* 默认的时间格式化
*/
private static final String DATE_FORMAT_DEFAULT = "yyyy-MM-dd HH:mm:ss";
/**
* 判断gson对象是否存在了,不存在则创建对象
*/
static {
if (gson == null) {
// gson = new Gson();
// 当使用 GsonBuilder 方式时属性为空的时候输出来的json字符串是有键值key的,显示形式是"key":null,而直接 new 出来的就没有"key":null的
gson = buildGson();
}
}
/**
* 默认的 GSON 初始化
*/
public static Gson buildGson() {
Gson gson = new Gson().newBuilder()
.setDateFormat(DATE_FORMAT_DEFAULT)
.create();
return gson;
}
/**
* 将对象转成json格式
* Bean To Json
*
* @param object
* @return String
*/
public static String beanToJson(Object object) {
String jsonString = null;
try {
if (gson != null) {
jsonString = gson.toJson(object);
}
} catch (Exception e) {
Log.e(TAG, "Bean 转 Json 格式异常:" + e);
}
return jsonString;
}
/**
* 将 json 转成特定的 cls 的对象
* Json To Bean
*
* @param jsonString
* @param cls
* @return
*/
public static <T> T jsonToBean(String jsonString, Class<T> cls) {
T t = null;
try {
if (gson != null) {
// 传入json对象和对象类型,将json转成对象
t = gson.fromJson(jsonString, cls);
}
} catch (JsonSyntaxException e) {
Log.e(TAG, "Json 转 Bean 非法json字符串:" + e);
}
return t;
}
/**
* 将 json 转成特定的 cls 的对象
* 解决泛型问题
*
* @param jsonString
* @param type
* @return
*/
public static <T> T jsonToBeans(String jsonString, Type type) {
T t = null;
try {
if (gson != null) {
// 传入json对象和对象类型,将json转成对象
t = gson.fromJson(jsonString, type);
}
} catch (JsonSyntaxException e) {
Log.e(TAG, "Json 转 Bean 非法json字符串:" + e);
}
return t;
}
/**
* json字符串转成list
* 解决泛型问题
* 备注:
* List list=gson.fromJson(jsonString, new TypeToken<List>() {}.getType());
* 该方法会报泛型类型擦除问题
*
* @param jsonString
* @param cls
* @param
* @return
*/
public static List jsonToList(String jsonString, Class cls) {
List list = new ArrayList();
try {
if (gson != null) {
JsonArray array = new JsonParser().parse(jsonString).getAsJsonArray();
for (final JsonElement elem : array) {
list.add(gson.fromJson(elem, cls));
}
}
} catch (JsonSyntaxException e) {
Log.e(TAG, "Json 转 List 非法json字符串:" + e);
}
return list;
}
/**
* json 字符串转成 list map
* Json To List<Map<String,T>>
*
* @param jsonString
* @return
*/
public static <T> List<Map<String, T>> jsonToListMaps(String jsonString) {
List<Map<String, T>> list = null;
try {
if (gson != null) {
list = gson.fromJson(jsonString,
new TypeToken<List<Map<String, T>>>() {
}.getType());
}
} catch (JsonSyntaxException e) {
Log.e(TAG, "Json 转 List 非法json字符串:" + e);
}
return list;
}
/**
* json 字符串转成 map 的
* Json To Map
*
* @param jsonString
* @return
*/
public static <T> Map<String, T> jsonToMaps(String jsonString) {
Map<String, T> map = null;
try {
if (gson != null) {
map = gson.fromJson(jsonString, new TypeToken<Map<String, T>>() {
}.getType());
}
} catch (JsonSyntaxException e) {
Log.e(TAG, "Json 转 Map 非法json字符串:" + e);
}
return map;
}
}
\ No newline at end of file
/*
* Copyright (C) 2021 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import java.io.IOException;
import java.math.BigDecimal;
import com.sobot.gson.ToNumberStrategy;
import com.sobot.gson.internal.LazilyParsedNumber;
import com.sobot.gson.stream.JsonReader;
import com.sobot.gson.stream.MalformedJsonException;
/**
* An enumeration that defines two standard number reading strategies and a couple of
* strategies to overcome some historical Gson limitations while deserializing numbers as
* {@link Object} and {@link Number}.
*
* @see ToNumberStrategy
*/
public enum ToNumberPolicy implements ToNumberStrategy {
/**
* Using this policy will ensure that numbers will be read as {@link Double} values.
* This is the default strategy used during deserialization of numbers as {@link Object}.
*/
DOUBLE {
@Override public Double readNumber(JsonReader in) throws IOException {
return in.nextDouble();
}
},
/**
* Using this policy will ensure that numbers will be read as a lazily parsed number backed
* by a string. This is the default strategy used during deserialization of numbers as
* {@link Number}.
*/
LAZILY_PARSED_NUMBER {
@Override public Number readNumber(JsonReader in) throws IOException {
return new LazilyParsedNumber(in.nextString());
}
},
/**
* Using this policy will ensure that numbers will be read as {@link Long} or {@link Double}
* values depending on how JSON numbers are represented: {@code Long} if the JSON number can
* be parsed as a {@code Long} value, or otherwise {@code Double} if it can be parsed as a
* {@code Double} value. If the parsed double-precision number results in a positive or negative
* infinity ({@link Double#isInfinite()}) or a NaN ({@link Double#isNaN()}) value and the
* {@code JsonReader} is not {@link JsonReader#isLenient() lenient}, a {@link MalformedJsonException}
* is thrown.
*/
LONG_OR_DOUBLE {
@Override public Number readNumber(JsonReader in) throws IOException, JsonParseException {
String value = in.nextString();
try {
return Long.parseLong(value);
} catch (NumberFormatException longE) {
try {
Double d = Double.valueOf(value);
if ((d.isInfinite() || d.isNaN()) && !in.isLenient()) {
throw new MalformedJsonException("JSON forbids NaN and infinities: " + d + "; at path " + in.getPreviousPath());
}
return d;
} catch (NumberFormatException doubleE) {
throw new JsonParseException("Cannot parse " + value + "; at path " + in.getPreviousPath(), doubleE);
}
}
}
},
/**
* Using this policy will ensure that numbers will be read as numbers of arbitrary length
* using {@link BigDecimal}.
*/
BIG_DECIMAL {
@Override public BigDecimal readNumber(JsonReader in) throws IOException {
String value = in.nextString();
try {
return new BigDecimal(value);
} catch (NumberFormatException e) {
throw new JsonParseException("Cannot parse " + value + "; at path " + in.getPreviousPath(), e);
}
}
}
}
/*
* Copyright (C) 2021 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import java.io.IOException;
import com.sobot.gson.stream.JsonReader;
/**
* A strategy that is used to control how numbers should be deserialized for {@link Object} and {@link Number}
* when a concrete type of the deserialized number is unknown in advance. By default, Gson uses the following
* deserialization strategies:
*
* <ul>
* <li>{@link Double} values are returned for JSON numbers if the deserialization type is declared as
* {@code Object}, see {@link ToNumberPolicy#DOUBLE};</li>
* <li>Lazily parsed number values are returned if the deserialization type is declared as {@code Number},
* see {@link ToNumberPolicy#LAZILY_PARSED_NUMBER}.</li>
* </ul>
*
* <p>For historical reasons, Gson does not support deserialization of arbitrary-length numbers for
* {@code Object} and {@code Number} by default, potentially causing precision loss. However,
* <a href="https://tools.ietf.org/html/rfc8259#section-6">RFC 8259</a> permits this:
*
* <pre>
* This specification allows implementations to set limits on the range
* and precision of numbers accepted. Since software that implements
* IEEE 754 binary64 (double precision) numbers [IEEE754] is generally
* available and widely used, good interoperability can be achieved by
* implementations that expect no more precision or range than these
* provide, in the sense that implementations will approximate JSON
* numbers within the expected precision. A JSON number such as 1E400
* or 3.141592653589793238462643383279 may indicate potential
* interoperability problems, since it suggests that the software that
* created it expects receiving software to have greater capabilities
* for numeric magnitude and precision than is widely available.
* </pre>
*
* <p>To overcome the precision loss, use for example {@link ToNumberPolicy#LONG_OR_DOUBLE} or
* {@link ToNumberPolicy#BIG_DECIMAL}.</p>
*
* @see ToNumberPolicy
* @see GsonBuilder#setObjectToNumberStrategy(ToNumberStrategy)
* @see GsonBuilder#setNumberToNumberStrategy(ToNumberStrategy)
*/
public interface ToNumberStrategy {
/**
* Reads a number from the given JSON reader. A strategy is supposed to read a single value from the
* reader, and the read value is guaranteed never to be {@code null}.
*
* @param in JSON reader to read a number from
* @return number read from the JSON reader.
*/
public Number readNumber(JsonReader in) throws IOException;
}
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson;
import com.sobot.gson.reflect.TypeToken;
/**
* Creates type adapters for set of related types. Type adapter factories are
* most useful when several types share similar structure in their JSON form.
*
* <h3>Example: Converting enums to lowercase</h3>
* In this example, we implement a factory that creates type adapters for all
* enums. The type adapters will write enums in lowercase, despite the fact
* that they're defined in {@code CONSTANT_CASE} in the corresponding Java
* model: <pre> {@code
*
* public class LowercaseEnumTypeAdapterFactory implements TypeAdapterFactory {
* public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
* Class<T> rawType = (Class<T>) type.getRawType();
* if (!rawType.isEnum()) {
* return null;
* }
*
* final Map<String, T> lowercaseToConstant = new HashMap<>();
* for (T constant : rawType.getEnumConstants()) {
* lowercaseToConstant.put(toLowercase(constant), constant);
* }
*
* return new TypeAdapter<T>() {
* public void write(JsonWriter out, T value) throws IOException {
* if (value == null) {
* out.nullValue();
* } else {
* out.value(toLowercase(value));
* }
* }
*
* public T read(JsonReader reader) throws IOException {
* if (reader.peek() == JsonToken.NULL) {
* reader.nextNull();
* return null;
* } else {
* return lowercaseToConstant.get(reader.nextString());
* }
* }
* };
* }
*
* private String toLowercase(Object o) {
* return o.toString().toLowerCase(Locale.US);
* }
* }
* }</pre>
*
* <p>Type adapter factories select which types they provide type adapters
* for. If a factory cannot support a given type, it must return null when
* that type is passed to {@link #create}. Factories should expect {@code
* create()} to be called on them for many types and should return null for
* most of those types. In the above example the factory returns null for
* calls to {@code create()} where {@code type} is not an enum.
*
* <p>A factory is typically called once per type, but the returned type
* adapter may be used many times. It is most efficient to do expensive work
* like reflection in {@code create()} so that the type adapter's {@code
* read()} and {@code write()} methods can be very fast. In this example the
* mapping from lowercase name to enum value is computed eagerly.
*
* <p>As with type adapters, factories must be <i>registered</i> with a {@link
* com.sobot.gson.GsonBuilder} for them to take effect: <pre> {@code
*
* GsonBuilder builder = new GsonBuilder();
* builder.registerTypeAdapterFactory(new LowercaseEnumTypeAdapterFactory());
* ...
* Gson gson = builder.create();
* }</pre>
* If multiple factories support the same type, the factory registered earlier
* takes precedence.
*
* <h3>Example: composing other type adapters</h3>
* In this example we implement a factory for Guava's {@code Multiset}
* collection type. The factory can be used to create type adapters for
* multisets of any element type: the type adapter for {@code
* Multiset<String>} is different from the type adapter for {@code
* Multiset<URL>}.
*
* <p>The type adapter <i>delegates</i> to another type adapter for the
* multiset elements. It figures out the element type by reflecting on the
* multiset's type token. A {@code Gson} is passed in to {@code create} for
* just this purpose: <pre> {@code
*
* public class MultisetTypeAdapterFactory implements TypeAdapterFactory {
* public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
* Type type = typeToken.getType();
* if (typeToken.getRawType() != Multiset.class
* || !(type instanceof ParameterizedType)) {
* return null;
* }
*
* Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
* TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
* return (TypeAdapter<T>) newMultisetAdapter(elementAdapter);
* }
*
* private <E> TypeAdapter<Multiset<E>> newMultisetAdapter(
* final TypeAdapter<E> elementAdapter) {
* return new TypeAdapter<Multiset<E>>() {
* public void write(JsonWriter out, Multiset<E> value) throws IOException {
* if (value == null) {
* out.nullValue();
* return;
* }
*
* out.beginArray();
* for (Multiset.Entry<E> entry : value.entrySet()) {
* out.value(entry.getCount());
* elementAdapter.write(out, entry.getElement());
* }
* out.endArray();
* }
*
* public Multiset<E> read(JsonReader in) throws IOException {
* if (in.peek() == JsonToken.NULL) {
* in.nextNull();
* return null;
* }
*
* Multiset<E> result = LinkedHashMultiset.create();
* in.beginArray();
* while (in.hasNext()) {
* int count = in.nextInt();
* E element = elementAdapter.read(in);
* result.add(element, count);
* }
* in.endArray();
* return result;
* }
* };
* }
* }
* }</pre>
* Delegating from one type adapter to another is extremely powerful; it's
* the foundation of how Gson converts Java objects and collections. Whenever
* possible your factory should retrieve its delegate type adapter in the
* {@code create()} method; this ensures potentially-expensive type adapter
* creation happens only once.
*
* @since 2.1
*/
public interface TypeAdapterFactory {
/**
* Returns a type adapter for {@code type}, or null if this factory doesn't
* support {@code type}.
*/
<T> TypeAdapter<T> create(Gson gson, TypeToken<T> type);
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.annotations;
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;
/**
* An annotation that indicates this member should be exposed for JSON
* serialization or deserialization.
*
* <p>This annotation has no effect unless you build {@link com.sobot.gson.Gson}
* with a {@link com.sobot.gson.GsonBuilder} and invoke
* {@link com.sobot.gson.GsonBuilder#excludeFieldsWithoutExposeAnnotation()}
* method.</p>
*
* <p>Here is an example of how this annotation is meant to be used:
* <pre>
* public class User {
* &#64;Expose private String firstName;
* &#64;Expose(serialize = false) private String lastName;
* &#64;Expose (serialize = false, deserialize = false) private String emailAddress;
* private String password;
* }
* </pre>
* If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()}
* methods will use the {@code password} field along-with {@code firstName}, {@code lastName},
* and {@code emailAddress} for serialization and deserialization. However, if you created Gson
* with {@code Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()}
* then the {@code toJson()} and {@code fromJson()} methods of Gson will exclude the
* {@code password} field. This is because the {@code password} field is not marked with the
* {@code @Expose} annotation. Gson will also exclude {@code lastName} and {@code emailAddress}
* from serialization since {@code serialize} is set to {@code false}. Similarly, Gson will
* exclude {@code emailAddress} from deserialization since {@code deserialize} is set to false.
*
* <p>Note that another way to achieve the same effect would have been to just mark the
* {@code password} field as {@code transient}, and Gson would have excluded it even with default
* settings. The {@code @Expose} annotation is useful in a style of programming where you want to
* explicitly specify all fields that should get considered for serialization or deserialization.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Expose {
/**
* If {@code true}, the field marked with this annotation is written out in the JSON while
* serializing. If {@code false}, the field marked with this annotation is skipped from the
* serialized output. Defaults to {@code true}.
* @since 1.4
*/
public boolean serialize() default true;
/**
* If {@code true}, the field marked with this annotation is deserialized from the JSON.
* If {@code false}, the field marked with this annotation is skipped during deserialization.
* Defaults to {@code true}.
* @since 1.4
*/
public boolean deserialize() default true;
}
/*
* Copyright (C) 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.annotations;
import com.sobot.gson.JsonDeserializer;
import com.sobot.gson.JsonSerializer;
import com.sobot.gson.TypeAdapter;
import com.sobot.gson.TypeAdapterFactory;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* An annotation that indicates the Gson {@link TypeAdapter} to use with a class
* or field.
*
* <p>Here is an example of how this annotation is used:</p>
* <pre>
* &#64;JsonAdapter(UserJsonAdapter.class)
* public class User {
* public final String firstName, lastName;
* private User(String firstName, String lastName) {
* this.firstName = firstName;
* this.lastName = lastName;
* }
* }
* public class UserJsonAdapter extends TypeAdapter&lt;User&gt; {
* &#64;Override public void write(JsonWriter out, User user) throws IOException {
* // implement write: combine firstName and lastName into name
* out.beginObject();
* out.name("name");
* out.value(user.firstName + " " + user.lastName);
* out.endObject();
* // implement the write method
* }
* &#64;Override public User read(JsonReader in) throws IOException {
* // implement read: split name into firstName and lastName
* in.beginObject();
* in.nextName();
* String[] nameParts = in.nextString().split(" ");
* in.endObject();
* return new User(nameParts[0], nameParts[1]);
* }
* }
* </pre>
*
* Since User class specified UserJsonAdapter.class in &#64;JsonAdapter annotation, it
* will automatically be invoked to serialize/deserialize User instances. <br>
*
* <p> Here is an example of how to apply this annotation to a field.
* <pre>
* private static final class Gadget {
* &#64;JsonAdapter(UserJsonAdapter2.class)
* final User user;
* Gadget(User user) {
* this.user = user;
* }
* }
* </pre>
*
* It's possible to specify different type adapters on a field, that
* field's type, and in the {@link com.sobot.gson.GsonBuilder}. Field
* annotations take precedence over {@code GsonBuilder}-registered type
* adapters, which in turn take precedence over annotated types.
*
* <p>The class referenced by this annotation must be either a {@link
* TypeAdapter} or a {@link TypeAdapterFactory}, or must implement one
* or both of {@link JsonDeserializer} or {@link JsonSerializer}.
* Using {@link TypeAdapterFactory} makes it possible to delegate
* to the enclosing {@code Gson} instance.
*
* @since 2.3
*
* @author Inderjeet Singh
* @author Joel Leitch
* @author Jesse Wilson
*/
// Note that the above example is taken from AdaptAnnotationTest.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface JsonAdapter {
/** Either a {@link TypeAdapter} or {@link TypeAdapterFactory}, or one or both of {@link JsonDeserializer} or {@link JsonSerializer}. */
Class<?> value();
/** false, to be able to handle {@code null} values within the adapter, default value is true. */
boolean nullSafe() default true;
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.annotations;
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;
/**
* An annotation that indicates this member should be serialized to JSON with
* the provided name value as its field name.
*
* <p>This annotation will override any {@link com.sobot.gson.FieldNamingPolicy}, including
* the default field naming policy, that may have been set on the {@link com.sobot.gson.Gson}
* instance. A different naming policy can set using the {@code GsonBuilder} class. See
* {@link com.sobot.gson.GsonBuilder#setFieldNamingPolicy(com.sobot.gson.FieldNamingPolicy)}
* for more information.</p>
*
* <p>Here is an example of how this annotation is meant to be used:</p>
* <pre>
* public class MyClass {
* &#64;SerializedName("name") String a;
* &#64;SerializedName(value="name1", alternate={"name2", "name3"}) String b;
* String c;
*
* public MyClass(String a, String b, String c) {
* this.a = a;
* this.b = b;
* this.c = c;
* }
* }
* </pre>
*
* <p>The following shows the output that is generated when serializing an instance of the
* above example class:</p>
* <pre>
* MyClass target = new MyClass("v1", "v2", "v3");
* Gson gson = new Gson();
* String json = gson.toJson(target);
* System.out.println(json);
*
* ===== OUTPUT =====
* {"name":"v1","name1":"v2","c":"v3"}
* </pre>
*
* <p>NOTE: The value you specify in this annotation must be a valid JSON field name.</p>
* While deserializing, all values specified in the annotation will be deserialized into the field.
* For example:
* <pre>
* MyClass target = gson.fromJson("{'name1':'v1'}", MyClass.class);
* assertEquals("v1", target.b);
* target = gson.fromJson("{'name2':'v2'}", MyClass.class);
* assertEquals("v2", target.b);
* target = gson.fromJson("{'name3':'v3'}", MyClass.class);
* assertEquals("v3", target.b);
* </pre>
* Note that MyClass.b is now deserialized from either name1, name2 or name3.
*
* @see com.sobot.gson.FieldNamingPolicy
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface SerializedName {
/**
* @return the desired name of the field when it is serialized or deserialized
*/
String value();
/**
* @return the alternative names of the field when it is deserialized
*/
String[] alternate() default {};
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.annotations;
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;
/**
* An annotation that indicates the version number since a member or a type has been present.
* This annotation is useful to manage versioning of your Json classes for a web-service.
*
* <p>
* This annotation has no effect unless you build {@link com.sobot.gson.Gson} with a
* {@link com.sobot.gson.GsonBuilder} and invoke
* {@link com.sobot.gson.GsonBuilder#setVersion(double)} method.
*
* <p>Here is an example of how this annotation is meant to be used:</p>
* <pre>
* public class User {
* private String firstName;
* private String lastName;
* &#64;Since(1.0) private String emailAddress;
* &#64;Since(1.0) private String password;
* &#64;Since(1.1) private Address address;
* }
* </pre>
*
* <p>If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()}
* methods will use all the fields for serialization and deserialization. However, if you created
* Gson with {@code Gson gson = new GsonBuilder().setVersion(1.0).create()} then the
* {@code toJson()} and {@code fromJson()} methods of Gson will exclude the {@code address} field
* since it's version number is set to {@code 1.1}.</p>
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Since {
/**
* the value indicating a version number since this member
* or type has been present.
*/
double value();
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.annotations;
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;
/**
* An annotation that indicates the version number until a member or a type should be present.
* Basically, if Gson is created with a version number that exceeds the value stored in the
* {@code Until} annotation then the field will be ignored from the JSON output. This annotation
* is useful to manage versioning of your JSON classes for a web-service.
*
* <p>
* This annotation has no effect unless you build {@link com.sobot.gson.Gson} with a
* {@link com.sobot.gson.GsonBuilder} and invoke
* {@link com.sobot.gson.GsonBuilder#setVersion(double)} method.
*
* <p>Here is an example of how this annotation is meant to be used:</p>
* <pre>
* public class User {
* private String firstName;
* private String lastName;
* &#64;Until(1.1) private String emailAddress;
* &#64;Until(1.1) private String password;
* }
* </pre>
*
* <p>If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()}
* methods will use all the fields for serialization and deserialization. However, if you created
* Gson with {@code Gson gson = new GsonBuilder().setVersion(1.2).create()} then the
* {@code toJson()} and {@code fromJson()} methods of Gson will exclude the {@code emailAddress}
* and {@code password} fields from the example above, because the version number passed to the
* GsonBuilder, {@code 1.2}, exceeds the version number set on the {@code Until} annotation,
* {@code 1.1}, for those fields.
*
* @author Inderjeet Singh
* @author Joel Leitch
* @since 1.3
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Until {
/**
* the value indicating a version number until this member
* or type should be ignored.
*/
double value();
}
/**
* This package provides annotations that can be used with {@link com.sobot.gson.Gson}.
*
* @author Inderjeet Singh, Joel Leitch
*/
package com.sobot.gson.annotations;
\ No newline at end of file
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.internal;
/**
* A simple utility class used to check method Preconditions.
*
* <pre>
* public long divideBy(long value) {
* Preconditions.checkArgument(value != 0);
* return this.value / value;
* }
* </pre>
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public final class $Gson$Preconditions {
private $Gson$Preconditions() {
throw new UnsupportedOperationException();
}
public static <T> T checkNotNull(T obj) {
if (obj == null) {
throw new NullPointerException();
}
return obj;
}
public static void checkArgument(boolean condition) {
if (!condition) {
throw new IllegalArgumentException();
}
}
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.internal;
import com.sobot.gson.ExclusionStrategy;
import com.sobot.gson.FieldAttributes;
import com.sobot.gson.Gson;
import com.sobot.gson.TypeAdapter;
import com.sobot.gson.TypeAdapterFactory;
import com.sobot.gson.annotations.Expose;
import com.sobot.gson.annotations.Since;
import com.sobot.gson.annotations.Until;
import com.sobot.gson.reflect.TypeToken;
import com.sobot.gson.stream.JsonReader;
import com.sobot.gson.stream.JsonWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* This class selects which fields and types to omit. It is configurable,
* supporting version attributes {@link Since} and {@link Until}, modifiers,
* synthetic fields, anonymous and local classes, inner classes, and fields with
* the {@link Expose} annotation.
*
* <p>This class is a type adapter factory; types that are excluded will be
* adapted to null. It may delegate to another type adapter if only one
* direction is excluded.
*
* @author Joel Leitch
* @author Jesse Wilson
*/
public final class Excluder implements TypeAdapterFactory, Cloneable {
private static final double IGNORE_VERSIONS = -1.0d;
public static final Excluder DEFAULT = new Excluder();
private double version = IGNORE_VERSIONS;
private int modifiers = Modifier.TRANSIENT | Modifier.STATIC;
private boolean serializeInnerClasses = true;
private boolean requireExpose;
private List<ExclusionStrategy> serializationStrategies = Collections.emptyList();
private List<ExclusionStrategy> deserializationStrategies = Collections.emptyList();
@Override protected Excluder clone() {
try {
return (Excluder) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
public Excluder withVersion(double ignoreVersionsAfter) {
Excluder result = clone();
result.version = ignoreVersionsAfter;
return result;
}
public Excluder withModifiers(int... modifiers) {
Excluder result = clone();
result.modifiers = 0;
for (int modifier : modifiers) {
result.modifiers |= modifier;
}
return result;
}
public Excluder disableInnerClassSerialization() {
Excluder result = clone();
result.serializeInnerClasses = false;
return result;
}
public Excluder excludeFieldsWithoutExposeAnnotation() {
Excluder result = clone();
result.requireExpose = true;
return result;
}
public Excluder withExclusionStrategy(ExclusionStrategy exclusionStrategy,
boolean serialization, boolean deserialization) {
Excluder result = clone();
if (serialization) {
result.serializationStrategies = new ArrayList<>(serializationStrategies);
result.serializationStrategies.add(exclusionStrategy);
}
if (deserialization) {
result.deserializationStrategies = new ArrayList<>(deserializationStrategies);
result.deserializationStrategies.add(exclusionStrategy);
}
return result;
}
@Override public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
Class<?> rawType = type.getRawType();
boolean excludeClass = excludeClassChecks(rawType);
final boolean skipSerialize = excludeClass || excludeClassInStrategy(rawType, true);
final boolean skipDeserialize = excludeClass || excludeClassInStrategy(rawType, false);
if (!skipSerialize && !skipDeserialize) {
return null;
}
return new TypeAdapter<T>() {
/** The delegate is lazily created because it may not be needed, and creating it may fail. */
private TypeAdapter<T> delegate;
@Override public T read(JsonReader in) throws IOException {
if (skipDeserialize) {
in.skipValue();
return null;
}
return delegate().read(in);
}
@Override public void write(JsonWriter out, T value) throws IOException {
if (skipSerialize) {
out.nullValue();
return;
}
delegate().write(out, value);
}
private TypeAdapter<T> delegate() {
TypeAdapter<T> d = delegate;
return d != null
? d
: (delegate = gson.getDelegateAdapter(Excluder.this, type));
}
};
}
public boolean excludeField(Field field, boolean serialize) {
if ((modifiers & field.getModifiers()) != 0) {
return true;
}
if (version != Excluder.IGNORE_VERSIONS
&& !isValidVersion(field.getAnnotation(Since.class), field.getAnnotation(Until.class))) {
return true;
}
if (field.isSynthetic()) {
return true;
}
if (requireExpose) {
Expose annotation = field.getAnnotation(Expose.class);
if (annotation == null || (serialize ? !annotation.serialize() : !annotation.deserialize())) {
return true;
}
}
if (!serializeInnerClasses && isInnerClass(field.getType())) {
return true;
}
if (isAnonymousOrNonStaticLocal(field.getType())) {
return true;
}
List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
if (!list.isEmpty()) {
FieldAttributes fieldAttributes = new FieldAttributes(field);
for (ExclusionStrategy exclusionStrategy : list) {
if (exclusionStrategy.shouldSkipField(fieldAttributes)) {
return true;
}
}
}
return false;
}
private boolean excludeClassChecks(Class<?> clazz) {
if (version != Excluder.IGNORE_VERSIONS && !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class))) {
return true;
}
if (!serializeInnerClasses && isInnerClass(clazz)) {
return true;
}
if (isAnonymousOrNonStaticLocal(clazz)) {
return true;
}
return false;
}
public boolean excludeClass(Class<?> clazz, boolean serialize) {
return excludeClassChecks(clazz) ||
excludeClassInStrategy(clazz, serialize);
}
private boolean excludeClassInStrategy(Class<?> clazz, boolean serialize) {
List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
for (ExclusionStrategy exclusionStrategy : list) {
if (exclusionStrategy.shouldSkipClass(clazz)) {
return true;
}
}
return false;
}
private boolean isAnonymousOrNonStaticLocal(Class<?> clazz) {
return !Enum.class.isAssignableFrom(clazz) && !isStatic(clazz)
&& (clazz.isAnonymousClass() || clazz.isLocalClass());
}
private boolean isInnerClass(Class<?> clazz) {
return clazz.isMemberClass() && !isStatic(clazz);
}
private boolean isStatic(Class<?> clazz) {
return (clazz.getModifiers() & Modifier.STATIC) != 0;
}
private boolean isValidVersion(Since since, Until until) {
return isValidSince(since) && isValidUntil(until);
}
private boolean isValidSince(Since annotation) {
if (annotation != null) {
double annotationVersion = annotation.value();
if (annotationVersion > version) {
return false;
}
}
return true;
}
private boolean isValidUntil(Until annotation) {
if (annotation != null) {
double annotationVersion = annotation.value();
if (annotationVersion <= version) {
return false;
}
}
return true;
}
}
/*
* Copyright (C) 2017 The Gson authors
*
* 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.sobot.gson.internal;
/**
* Utility to check the major Java version of the current JVM.
*/
public final class JavaVersion {
// Oracle defines naming conventions at http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html
// However, many alternate implementations differ. For example, Debian used 9-debian as the version string
private static final int majorJavaVersion = determineMajorJavaVersion();
private static int determineMajorJavaVersion() {
String javaVersion = System.getProperty("java.version");
return getMajorJavaVersion(javaVersion);
}
// Visible for testing only
static int getMajorJavaVersion(String javaVersion) {
int version = parseDotted(javaVersion);
if (version == -1) {
version = extractBeginningInt(javaVersion);
}
if (version == -1) {
return 6; // Choose minimum supported JDK version as default
}
return version;
}
// Parses both legacy 1.8 style and newer 9.0.4 style
private static int parseDotted(String javaVersion) {
try {
String[] parts = javaVersion.split("[._]");
int firstVer = Integer.parseInt(parts[0]);
if (firstVer == 1 && parts.length > 1) {
return Integer.parseInt(parts[1]);
} else {
return firstVer;
}
} catch (NumberFormatException e) {
return -1;
}
}
private static int extractBeginningInt(String javaVersion) {
try {
StringBuilder num = new StringBuilder();
for (int i = 0; i < javaVersion.length(); ++i) {
char c = javaVersion.charAt(i);
if (Character.isDigit(c)) {
num.append(c);
} else {
break;
}
}
return Integer.parseInt(num.toString());
} catch (NumberFormatException e) {
return -1;
}
}
/**
* @return the major Java version, i.e. '8' for Java 1.8, '9' for Java 9 etc.
*/
public static int getMajorJavaVersion() {
return majorJavaVersion;
}
/**
* @return {@code true} if the application is running on Java 9 or later; and {@code false} otherwise.
*/
public static boolean isJava9OrLater() {
return majorJavaVersion >= 9;
}
private JavaVersion() { }
}
/*
* Copyright 1999-2101 Alibaba Group.
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -13,19 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.fastjson;
package com.sobot.gson.internal;
import com.sobot.gson.stream.JsonReader;
import java.io.IOException;
/**
* Beans that support customized output of JSON text to a writer shall implement this interface.
*
* @author wenshao[szujobs@hotmail.com]
* Internal-only APIs of JsonReader available only to other classes in Gson.
*/
public interface JSONStreamAware {
public abstract class JsonReaderInternalAccess {
public static JsonReaderInternalAccess INSTANCE;
/**
* write JSON string to out.
*/
void writeJSONString(Appendable out) throws IOException;
/**
* Changes the type of the current property name token to a string value.
*/
public abstract void promoteNameToValue(JsonReader reader) throws IOException;
}
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.internal;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.math.BigDecimal;
/**
* This class holds a number value that is lazily converted to a specific number type
*
* @author Inderjeet Singh
*/
public final class LazilyParsedNumber extends Number {
private final String value;
/** @param value must not be null */
public LazilyParsedNumber(String value) {
this.value = value;
}
@Override
public int intValue() {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
try {
return (int) Long.parseLong(value);
} catch (NumberFormatException nfe) {
return new BigDecimal(value).intValue();
}
}
}
@Override
public long longValue() {
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
return new BigDecimal(value).longValue();
}
}
@Override
public float floatValue() {
return Float.parseFloat(value);
}
@Override
public double doubleValue() {
return Double.parseDouble(value);
}
@Override
public String toString() {
return value;
}
/**
* If somebody is unlucky enough to have to serialize one of these, serialize
* it as a BigDecimal so that they won't need Gson on the other side to
* deserialize it.
*/
private Object writeReplace() throws ObjectStreamException {
return new BigDecimal(value);
}
private void readObject(ObjectInputStream in) throws IOException {
// Don't permit directly deserializing this class; writeReplace() should have written a replacement
throw new InvalidObjectException("Deserialization is unsupported");
}
@Override
public int hashCode() {
return value.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof LazilyParsedNumber) {
LazilyParsedNumber other = (LazilyParsedNumber) obj;
return value == other.value || value.equals(other.value);
}
return false;
}
}
/*
* Copyright 1999-2101 Alibaba Group.
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* 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,
......@@ -13,15 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.fastjson.serializer;
import java.io.IOException;
import java.lang.reflect.Type;
package com.sobot.gson.internal;
/**
* @author wenshao[szujobs@hotmail.com]
* Defines a generic object construction factory. The purpose of this class
* is to construct a default instance of a class that can be used for object
* navigation while deserialization from its JSON representation.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public interface ObjectSerializer {
public interface ObjectConstructor<T> {
void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType) throws IOException;
}
/**
* Returns a new instance.
*/
public T construct();
}
\ No newline at end of file
/*
* Copyright (C) 2017 The Gson authors
*
* 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.sobot.gson.internal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Locale;
/**
* Provides DateFormats for US locale with patterns which were the default ones before Java 9.
*/
public class PreJava9DateFormatProvider {
/**
* Returns the same DateFormat as {@code DateFormat.getDateInstance(style, Locale.US)} in Java 8 or below.
*/
public static DateFormat getUSDateFormat(int style) {
return new SimpleDateFormat(getDateFormatPattern(style), Locale.US);
}
/**
* Returns the same DateFormat as {@code DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US)}
* in Java 8 or below.
*/
public static DateFormat getUSDateTimeFormat(int dateStyle, int timeStyle) {
String pattern = getDatePartOfDateTimePattern(dateStyle) + " " + getTimePartOfDateTimePattern(timeStyle);
return new SimpleDateFormat(pattern, Locale.US);
}
private static String getDateFormatPattern(int style) {
switch (style) {
case DateFormat.SHORT:
return "M/d/yy";
case DateFormat.MEDIUM:
return "MMM d, y";
case DateFormat.LONG:
return "MMMM d, y";
case DateFormat.FULL:
return "EEEE, MMMM d, y";
default:
throw new IllegalArgumentException("Unknown DateFormat style: " + style);
}
}
private static String getDatePartOfDateTimePattern(int dateStyle) {
switch (dateStyle) {
case DateFormat.SHORT:
return "M/d/yy";
case DateFormat.MEDIUM:
return "MMM d, yyyy";
case DateFormat.LONG:
return "MMMM d, yyyy";
case DateFormat.FULL:
return "EEEE, MMMM d, yyyy";
default:
throw new IllegalArgumentException("Unknown DateFormat style: " + dateStyle);
}
}
private static String getTimePartOfDateTimePattern(int timeStyle) {
switch (timeStyle) {
case DateFormat.SHORT:
return "h:mm a";
case DateFormat.MEDIUM:
return "h:mm:ss a";
case DateFormat.FULL:
case DateFormat.LONG:
return "h:mm:ss a z";
default:
throw new IllegalArgumentException("Unknown DateFormat style: " + timeStyle);
}
}
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.internal;
import java.lang.reflect.Type;
/**
* Contains static utility methods pertaining to primitive types and their
* corresponding wrapper types.
*
* @author Kevin Bourrillion
*/
public final class Primitives {
private Primitives() {}
/**
* Returns true if this type is a primitive.
*/
public static boolean isPrimitive(Type type) {
return type instanceof Class<?> && ((Class<?>) type).isPrimitive();
}
/**
* Returns {@code true} if {@code type} is one of the nine
* primitive-wrapper types, such as {@link Integer}.
*
* @see Class#isPrimitive
*/
public static boolean isWrapperType(Type type) {
return type == Integer.class
|| type == Float.class
|| type == Byte.class
|| type == Double.class
|| type == Long.class
|| type == Character.class
|| type == Boolean.class
|| type == Short.class
|| type == Void.class;
}
/**
* Returns the corresponding wrapper type of {@code type} if it is a primitive
* type; otherwise returns {@code type} itself. Idempotent.
* <pre>
* wrap(int.class) == Integer.class
* wrap(Integer.class) == Integer.class
* wrap(String.class) == String.class
* </pre>
*/
@SuppressWarnings("unchecked")
public static <T> Class<T> wrap(Class<T> type) {
if (type == int.class) return (Class<T>) Integer.class;
if (type == float.class) return (Class<T>) Float.class;
if (type == byte.class) return (Class<T>) Byte.class;
if (type == double.class) return (Class<T>) Double.class;
if (type == long.class) return (Class<T>) Long.class;
if (type == char.class) return (Class<T>) Character.class;
if (type == boolean.class) return (Class<T>) Boolean.class;
if (type == short.class) return (Class<T>) Short.class;
if (type == void.class) return (Class<T>) Void.class;
return type;
}
/**
* Returns the corresponding primitive type of {@code type} if it is a
* wrapper type; otherwise returns {@code type} itself. Idempotent.
* <pre>
* unwrap(Integer.class) == int.class
* unwrap(int.class) == int.class
* unwrap(String.class) == String.class
* </pre>
*/
@SuppressWarnings("unchecked")
public static <T> Class<T> unwrap(Class<T> type) {
if (type == Integer.class) return (Class<T>) int.class;
if (type == Float.class) return (Class<T>) float.class;
if (type == Byte.class) return (Class<T>) byte.class;
if (type == Double.class) return (Class<T>) double.class;
if (type == Long.class) return (Class<T>) long.class;
if (type == Character.class) return (Class<T>) char.class;
if (type == Boolean.class) return (Class<T>) boolean.class;
if (type == Short.class) return (Class<T>) short.class;
if (type == Void.class) return (Class<T>) void.class;
return type;
}
}
package com.sobot.gson.internal;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.List;
import com.sobot.gson.ReflectionAccessFilter;
import com.sobot.gson.ReflectionAccessFilter.FilterResult;
/**
* Internal helper class for {@link ReflectionAccessFilter}.
*/
public class ReflectionAccessFilterHelper {
private ReflectionAccessFilterHelper() { }
// Platform type detection is based on Moshi's Util.isPlatformType(Class)
// See https://github.com/square/moshi/blob/3c108919ee1cce88a433ffda04eeeddc0341eae7/moshi/src/main/java/com/squareup/moshi/internal/Util.java#L141
public static boolean isJavaType(Class<?> c) {
return isJavaType(c.getName());
}
private static boolean isJavaType(String className) {
return className.startsWith("java.") || className.startsWith("javax.");
}
public static boolean isAndroidType(Class<?> c) {
return isAndroidType(c.getName());
}
private static boolean isAndroidType(String className) {
return className.startsWith("android.")
|| className.startsWith("androidx.")
|| isJavaType(className);
}
public static boolean isAnyPlatformType(Class<?> c) {
String className = c.getName();
return isAndroidType(className) // Covers Android and Java
|| className.startsWith("kotlin.")
|| className.startsWith("kotlinx.")
|| className.startsWith("scala.");
}
/**
* Gets the result of applying all filters until the first one returns a result
* other than {@link FilterResult#INDECISIVE}, or {@link FilterResult#ALLOW} if
* the list of filters is empty or all returned {@code INDECISIVE}.
*/
public static FilterResult getFilterResult(List<ReflectionAccessFilter> reflectionFilters, Class<?> c) {
for (ReflectionAccessFilter filter : reflectionFilters) {
FilterResult result = filter.check(c);
if (result != FilterResult.INDECISIVE) {
return result;
}
}
return FilterResult.ALLOW;
}
/**
* See {@link AccessibleObject#canAccess(Object)} (Java >= 9)
*/
public static boolean canAccess(AccessibleObject accessibleObject, Object object) {
return AccessChecker.INSTANCE.canAccess(accessibleObject, object);
}
private static abstract class AccessChecker {
public static final AccessChecker INSTANCE;
static {
AccessChecker accessChecker = null;
// TODO: Ideally should use Multi-Release JAR for this version specific code
if (JavaVersion.isJava9OrLater()) {
try {
final Method canAccessMethod = AccessibleObject.class.getDeclaredMethod("canAccess", Object.class);
accessChecker = new AccessChecker() {
@Override public boolean canAccess(AccessibleObject accessibleObject, Object object) {
try {
return (Boolean) canAccessMethod.invoke(accessibleObject, object);
} catch (Exception e) {
throw new RuntimeException("Failed invoking canAccess", e);
}
}
};
} catch (NoSuchMethodException ignored) {
}
}
if (accessChecker == null) {
accessChecker = new AccessChecker() {
@Override public boolean canAccess(AccessibleObject accessibleObject, Object object) {
// Cannot determine whether object can be accessed, so assume it can be accessed
return true;
}
};
}
INSTANCE = accessChecker;
}
public abstract boolean canAccess(AccessibleObject accessibleObject, Object object);
}
}
/*
* Copyright (C) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.internal;
import com.sobot.gson.JsonElement;
import com.sobot.gson.JsonIOException;
import com.sobot.gson.JsonNull;
import com.sobot.gson.JsonParseException;
import com.sobot.gson.JsonSyntaxException;
import com.sobot.gson.internal.bind.TypeAdapters;
import com.sobot.gson.stream.JsonReader;
import com.sobot.gson.stream.JsonWriter;
import com.sobot.gson.stream.MalformedJsonException;
import java.io.EOFException;
import java.io.IOException;
import java.io.Writer;
/**
* Reads and writes GSON parse trees over streams.
*/
public final class Streams {
private Streams() {
throw new UnsupportedOperationException();
}
/**
* Takes a reader in any state and returns the next value as a JsonElement.
*/
public static JsonElement parse(JsonReader reader) throws JsonParseException {
boolean isEmpty = true;
try {
reader.peek();
isEmpty = false;
return TypeAdapters.JSON_ELEMENT.read(reader);
} catch (EOFException e) {
/*
* For compatibility with JSON 1.5 and earlier, we return a JsonNull for
* empty documents instead of throwing.
*/
if (isEmpty) {
return JsonNull.INSTANCE;
}
// The stream ended prematurely so it is likely a syntax error.
throw new JsonSyntaxException(e);
} catch (MalformedJsonException e) {
throw new JsonSyntaxException(e);
} catch (IOException e) {
throw new JsonIOException(e);
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
/**
* Writes the JSON element to the writer, recursively.
*/
public static void write(JsonElement element, JsonWriter writer) throws IOException {
TypeAdapters.JSON_ELEMENT.write(writer, element);
}
public static Writer writerForAppendable(Appendable appendable) {
return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable);
}
/**
* Adapts an {@link Appendable} so it can be passed anywhere a {@link Writer}
* is used.
*/
private static final class AppendableWriter extends Writer {
private final Appendable appendable;
private final CurrentWrite currentWrite = new CurrentWrite();
AppendableWriter(Appendable appendable) {
this.appendable = appendable;
}
@Override public void write(char[] chars, int offset, int length) throws IOException {
currentWrite.chars = chars;
appendable.append(currentWrite, offset, offset + length);
}
@Override public void write(int i) throws IOException {
appendable.append((char) i);
}
@Override public void flush() {}
@Override public void close() {}
/**
* A mutable char sequence pointing at a single char[].
*/
static class CurrentWrite implements CharSequence {
char[] chars;
@Override public int length() {
return chars.length;
}
@Override public char charAt(int i) {
return chars[i];
}
@Override public CharSequence subSequence(int start, int end) {
return new String(chars, start, end - start);
}
}
}
}
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.internal;
import com.sobot.gson.internal.ConstructorConstructor;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* Do sneaky things to allocate objects without invoking their constructors.
*
* @author Joel Leitch
* @author Jesse Wilson
*/
public abstract class UnsafeAllocator {
public abstract <T> T newInstance(Class<T> c) throws Exception;
/**
* Check if the class can be instantiated by Unsafe allocator. If the instance has interface or abstract modifiers
* return an exception message.
* @param c instance of the class to be checked
* @return if instantiable {@code null}, else a non-{@code null} exception message
*/
static String checkInstantiable(Class<?> c) {
int modifiers = c.getModifiers();
if (Modifier.isInterface(modifiers)) {
return "Interfaces can't be instantiated! Register an InstanceCreator "
+ "or a TypeAdapter for this type. Interface name: " + c.getName();
}
if (Modifier.isAbstract(modifiers)) {
return "Abstract classes can't be instantiated! Register an InstanceCreator "
+ "or a TypeAdapter for this type. Class name: " + c.getName();
}
return null;
}
/**
* Asserts that the class is instantiable. This check should have already occurred
* in {@link ConstructorConstructor}; this check here acts as safeguard since trying
* to use Unsafe for non-instantiable classes might crash the JVM on some devices.
*/
private static void assertInstantiable(Class<?> c) {
String exceptionMessage = checkInstantiable(c);
if (exceptionMessage != null) {
throw new AssertionError("UnsafeAllocator is used for non-instantiable type: " + exceptionMessage);
}
}
public static UnsafeAllocator create() {
// try JVM
// public class Unsafe {
// public Object allocateInstance(Class<?> type);
// }
try {
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
Field f = unsafeClass.getDeclaredField("theUnsafe");
f.setAccessible(true);
final Object unsafe = f.get(null);
final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);
return new UnsafeAllocator() {
@Override
@SuppressWarnings("unchecked")
public <T> T newInstance(Class<T> c) throws Exception {
assertInstantiable(c);
return (T) allocateInstance.invoke(unsafe, c);
}
};
} catch (Exception ignored) {
}
// try dalvikvm, post-gingerbread
// public class ObjectStreamClass {
// private static native int getConstructorId(Class<?> c);
// private static native Object newInstance(Class<?> instantiationClass, int methodId);
// }
try {
Method getConstructorId = ObjectStreamClass.class
.getDeclaredMethod("getConstructorId", Class.class);
getConstructorId.setAccessible(true);
final int constructorId = (Integer) getConstructorId.invoke(null, Object.class);
final Method newInstance = ObjectStreamClass.class
.getDeclaredMethod("newInstance", Class.class, int.class);
newInstance.setAccessible(true);
return new UnsafeAllocator() {
@Override
@SuppressWarnings("unchecked")
public <T> T newInstance(Class<T> c) throws Exception {
assertInstantiable(c);
return (T) newInstance.invoke(null, c, constructorId);
}
};
} catch (Exception ignored) {
}
// try dalvikvm, pre-gingerbread
// public class ObjectInputStream {
// private static native Object newInstance(
// Class<?> instantiationClass, Class<?> constructorClass);
// }
try {
final Method newInstance = ObjectInputStream.class
.getDeclaredMethod("newInstance", Class.class, Class.class);
newInstance.setAccessible(true);
return new UnsafeAllocator() {
@Override
@SuppressWarnings("unchecked")
public <T> T newInstance(Class<T> c) throws Exception {
assertInstantiable(c);
return (T) newInstance.invoke(null, c, Object.class);
}
};
} catch (Exception ignored) {
}
// give up
return new UnsafeAllocator() {
@Override
public <T> T newInstance(Class<T> c) {
throw new UnsupportedOperationException("Cannot allocate " + c + ". Usage of JDK sun.misc.Unsafe is enabled, "
+ "but it could not be used. Make sure your runtime is configured correctly.");
}
};
}
}
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.internal.bind;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import com.sobot.gson.Gson;
import com.sobot.gson.TypeAdapter;
import com.sobot.gson.TypeAdapterFactory;
import com.sobot.gson.internal.$Gson$Types;
import com.sobot.gson.internal.bind.TypeAdapterRuntimeTypeWrapper;
import com.sobot.gson.reflect.TypeToken;
import com.sobot.gson.stream.JsonReader;
import com.sobot.gson.stream.JsonToken;
import com.sobot.gson.stream.JsonWriter;
/**
* Adapt an array of objects.
*/
public final class ArrayTypeAdapter<E> extends TypeAdapter<Object> {
public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
@SuppressWarnings({"unchecked", "rawtypes"})
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (!(type instanceof GenericArrayType || type instanceof Class && ((Class<?>) type).isArray())) {
return null;
}
Type componentType = $Gson$Types.getArrayComponentType(type);
TypeAdapter<?> componentTypeAdapter = gson.getAdapter(TypeToken.get(componentType));
return new ArrayTypeAdapter(
gson, componentTypeAdapter, $Gson$Types.getRawType(componentType));
}
};
private final Class<E> componentType;
private final TypeAdapter<E> componentTypeAdapter;
public ArrayTypeAdapter(Gson context, TypeAdapter<E> componentTypeAdapter, Class<E> componentType) {
this.componentTypeAdapter =
new TypeAdapterRuntimeTypeWrapper<>(context, componentTypeAdapter, componentType);
this.componentType = componentType;
}
@Override public Object read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
List<E> list = new ArrayList<>();
in.beginArray();
while (in.hasNext()) {
E instance = componentTypeAdapter.read(in);
list.add(instance);
}
in.endArray();
int size = list.size();
Object array = Array.newInstance(componentType, size);
for (int i = 0; i < size; i++) {
Array.set(array, i, list.get(i));
}
return array;
}
@SuppressWarnings("unchecked")
@Override public void write(JsonWriter out, Object array) throws IOException {
if (array == null) {
out.nullValue();
return;
}
out.beginArray();
for (int i = 0, length = Array.getLength(array); i < length; i++) {
E value = (E) Array.get(array, i);
componentTypeAdapter.write(out, value);
}
out.endArray();
}
}
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.internal.bind;
import com.sobot.gson.Gson;
import com.sobot.gson.TypeAdapter;
import com.sobot.gson.TypeAdapterFactory;
import com.sobot.gson.internal.$Gson$Types;
import com.sobot.gson.internal.ConstructorConstructor;
import com.sobot.gson.internal.ObjectConstructor;
import com.sobot.gson.internal.bind.TypeAdapterRuntimeTypeWrapper;
import com.sobot.gson.reflect.TypeToken;
import com.sobot.gson.stream.JsonReader;
import com.sobot.gson.stream.JsonToken;
import com.sobot.gson.stream.JsonWriter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collection;
/**
* Adapt a homogeneous collection of objects.
*/
public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {
private final ConstructorConstructor constructorConstructor;
public CollectionTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
this.constructorConstructor = constructorConstructor;
}
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
Class<? super T> rawType = typeToken.getRawType();
if (!Collection.class.isAssignableFrom(rawType)) {
return null;
}
Type elementType = $Gson$Types.getCollectionElementType(type, rawType);
TypeAdapter<?> elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType));
ObjectConstructor<T> constructor = constructorConstructor.get(typeToken);
@SuppressWarnings({"unchecked", "rawtypes"}) // create() doesn't define a type parameter
TypeAdapter<T> result = new Adapter(gson, elementType, elementTypeAdapter, constructor);
return result;
}
private static final class Adapter<E> extends TypeAdapter<Collection<E>> {
private final TypeAdapter<E> elementTypeAdapter;
private final ObjectConstructor<? extends Collection<E>> constructor;
public Adapter(Gson context, Type elementType,
TypeAdapter<E> elementTypeAdapter,
ObjectConstructor<? extends Collection<E>> constructor) {
this.elementTypeAdapter =
new TypeAdapterRuntimeTypeWrapper<>(context, elementTypeAdapter, elementType);
this.constructor = constructor;
}
@Override public Collection<E> read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
Collection<E> collection = constructor.construct();
in.beginArray();
while (in.hasNext()) {
E instance = elementTypeAdapter.read(in);
collection.add(instance);
}
in.endArray();
return collection;
}
@Override public void write(JsonWriter out, Collection<E> collection) throws IOException {
if (collection == null) {
out.nullValue();
return;
}
out.beginArray();
for (E element : collection) {
elementTypeAdapter.write(out, element);
}
out.endArray();
}
}
}
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.internal.bind;
import com.sobot.gson.Gson;
import com.sobot.gson.JsonSyntaxException;
import com.sobot.gson.TypeAdapter;
import com.sobot.gson.TypeAdapterFactory;
import com.sobot.gson.internal.JavaVersion;
import com.sobot.gson.internal.PreJava9DateFormatProvider;
import com.sobot.gson.internal.bind.util.ISO8601Utils;
import com.sobot.gson.reflect.TypeToken;
import com.sobot.gson.stream.JsonReader;
import com.sobot.gson.stream.JsonToken;
import com.sobot.gson.stream.JsonWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
/**
* Adapter for Date. Although this class appears stateless, it is not.
* DateFormat captures its time zone and locale when it is created, which gives
* this class state. DateFormat isn't thread safe either, so this class has
* to synchronize its read and write methods.
*/
public final class DateTypeAdapter extends TypeAdapter<Date> {
public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
return typeToken.getRawType() == Date.class ? (TypeAdapter<T>) new DateTypeAdapter() : null;
}
};
/**
* List of 1 or more different date formats used for de-serialization attempts.
* The first of them (default US format) is used for serialization as well.
*/
private final List<DateFormat> dateFormats = new ArrayList<>();
public DateTypeAdapter() {
dateFormats.add(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US));
if (!Locale.getDefault().equals(Locale.US)) {
dateFormats.add(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT));
}
if (JavaVersion.isJava9OrLater()) {
dateFormats.add(PreJava9DateFormatProvider.getUSDateTimeFormat(DateFormat.DEFAULT, DateFormat.DEFAULT));
}
}
@Override public Date read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
return deserializeToDate(in);
}
private Date deserializeToDate(JsonReader in) throws IOException {
String s = in.nextString();
synchronized (dateFormats) {
for (DateFormat dateFormat : dateFormats) {
try {
return dateFormat.parse(s);
} catch (ParseException ignored) {}
}
}
try {
return ISO8601Utils.parse(s, new ParsePosition(0));
} catch (ParseException e) {
throw new JsonSyntaxException("Failed parsing '" + s + "' as Date; at path " + in.getPreviousPath(), e);
}
}
@Override public void write(JsonWriter out, Date value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
DateFormat dateFormat = dateFormats.get(0);
String dateFormatAsString;
synchronized (dateFormats) {
dateFormatAsString = dateFormat.format(value);
}
out.value(dateFormatAsString);
}
}
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.internal.bind;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import com.sobot.gson.JsonSyntaxException;
import com.sobot.gson.TypeAdapter;
import com.sobot.gson.TypeAdapterFactory;
import com.sobot.gson.internal.$Gson$Preconditions;
import com.sobot.gson.internal.JavaVersion;
import com.sobot.gson.internal.PreJava9DateFormatProvider;
import com.sobot.gson.internal.bind.util.ISO8601Utils;
import com.sobot.gson.stream.JsonReader;
import com.sobot.gson.stream.JsonToken;
import com.sobot.gson.stream.JsonWriter;
/**
* This type adapter supports subclasses of date by defining a
* {@link DateType} and then using its {@code createAdapterFactory}
* methods.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public final class DefaultDateTypeAdapter<T extends Date> extends TypeAdapter<T> {
private static final String SIMPLE_NAME = "DefaultDateTypeAdapter";
public static abstract class DateType<T extends Date> {
public static final DateType<Date> DATE = new DateType<Date>(Date.class) {
@Override protected Date deserialize(Date date) {
return date;
}
};
private final Class<T> dateClass;
protected DateType(Class<T> dateClass) {
this.dateClass = dateClass;
}
protected abstract T deserialize(Date date);
private final TypeAdapterFactory createFactory(DefaultDateTypeAdapter<T> adapter) {
return TypeAdapters.newFactory(dateClass, adapter);
}
public final TypeAdapterFactory createAdapterFactory(String datePattern) {
return createFactory(new DefaultDateTypeAdapter<>(this, datePattern));
}
public final TypeAdapterFactory createAdapterFactory(int style) {
return createFactory(new DefaultDateTypeAdapter<>(this, style));
}
public final TypeAdapterFactory createAdapterFactory(int dateStyle, int timeStyle) {
return createFactory(new DefaultDateTypeAdapter<>(this, dateStyle, timeStyle));
}
public final TypeAdapterFactory createDefaultsAdapterFactory() {
return createFactory(new DefaultDateTypeAdapter<>(this, DateFormat.DEFAULT, DateFormat.DEFAULT));
}
}
private final DateType<T> dateType;
/**
* List of 1 or more different date formats used for de-serialization attempts.
* The first of them is used for serialization as well.
*/
private final List<DateFormat> dateFormats = new ArrayList<>();
private DefaultDateTypeAdapter(DateType<T> dateType, String datePattern) {
this.dateType = $Gson$Preconditions.checkNotNull(dateType);
dateFormats.add(new SimpleDateFormat(datePattern, Locale.US));
if (!Locale.getDefault().equals(Locale.US)) {
dateFormats.add(new SimpleDateFormat(datePattern));
}
}
private DefaultDateTypeAdapter(DateType<T> dateType, int style) {
this.dateType = $Gson$Preconditions.checkNotNull(dateType);
dateFormats.add(DateFormat.getDateInstance(style, Locale.US));
if (!Locale.getDefault().equals(Locale.US)) {
dateFormats.add(DateFormat.getDateInstance(style));
}
if (JavaVersion.isJava9OrLater()) {
dateFormats.add(PreJava9DateFormatProvider.getUSDateFormat(style));
}
}
private DefaultDateTypeAdapter(DateType<T> dateType, int dateStyle, int timeStyle) {
this.dateType = $Gson$Preconditions.checkNotNull(dateType);
dateFormats.add(DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US));
if (!Locale.getDefault().equals(Locale.US)) {
dateFormats.add(DateFormat.getDateTimeInstance(dateStyle, timeStyle));
}
if (JavaVersion.isJava9OrLater()) {
dateFormats.add(PreJava9DateFormatProvider.getUSDateTimeFormat(dateStyle, timeStyle));
}
}
// These methods need to be synchronized since JDK DateFormat classes are not thread-safe
// See issue 162
@Override
public void write(JsonWriter out, Date value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
DateFormat dateFormat = dateFormats.get(0);
String dateFormatAsString;
synchronized (dateFormats) {
dateFormatAsString = dateFormat.format(value);
}
out.value(dateFormatAsString);
}
@Override
public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
Date date = deserializeToDate(in);
return dateType.deserialize(date);
}
private Date deserializeToDate(JsonReader in) throws IOException {
String s = in.nextString();
synchronized (dateFormats) {
for (DateFormat dateFormat : dateFormats) {
try {
return dateFormat.parse(s);
} catch (ParseException ignored) {}
}
}
try {
return ISO8601Utils.parse(s, new ParsePosition(0));
} catch (ParseException e) {
throw new JsonSyntaxException("Failed parsing '" + s + "' as Date; at path " + in.getPreviousPath(), e);
}
}
@Override
public String toString() {
DateFormat defaultFormat = dateFormats.get(0);
if (defaultFormat instanceof SimpleDateFormat) {
return SIMPLE_NAME + '(' + ((SimpleDateFormat) defaultFormat).toPattern() + ')';
} else {
return SIMPLE_NAME + '(' + defaultFormat.getClass().getSimpleName() + ')';
}
}
}
/*
* Copyright (C) 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.internal.bind;
import com.sobot.gson.Gson;
import com.sobot.gson.JsonDeserializer;
import com.sobot.gson.JsonSerializer;
import com.sobot.gson.TypeAdapter;
import com.sobot.gson.TypeAdapterFactory;
import com.sobot.gson.annotations.JsonAdapter;
import com.sobot.gson.internal.ConstructorConstructor;
import com.sobot.gson.reflect.TypeToken;
/**
* Given a type T, looks for the annotation {@link JsonAdapter} and uses an instance of the
* specified class as the default type adapter.
*
* @since 2.3
*/
public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapterFactory {
private final ConstructorConstructor constructorConstructor;
public JsonAdapterAnnotationTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
this.constructorConstructor = constructorConstructor;
}
@SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> targetType) {
Class<? super T> rawType = targetType.getRawType();
JsonAdapter annotation = rawType.getAnnotation(JsonAdapter.class);
if (annotation == null) {
return null;
}
return (TypeAdapter<T>) getTypeAdapter(constructorConstructor, gson, targetType, annotation);
}
@SuppressWarnings({ "unchecked", "rawtypes" }) // Casts guarded by conditionals.
TypeAdapter<?> getTypeAdapter(ConstructorConstructor constructorConstructor, Gson gson,
TypeToken<?> type, JsonAdapter annotation) {
Object instance = constructorConstructor.get(TypeToken.get(annotation.value())).construct();
TypeAdapter<?> typeAdapter;
if (instance instanceof TypeAdapter) {
typeAdapter = (TypeAdapter<?>) instance;
} else if (instance instanceof TypeAdapterFactory) {
typeAdapter = ((TypeAdapterFactory) instance).create(gson, type);
} else if (instance instanceof JsonSerializer || instance instanceof JsonDeserializer) {
JsonSerializer<?> serializer = instance instanceof JsonSerializer
? (JsonSerializer) instance
: null;
JsonDeserializer<?> deserializer = instance instanceof JsonDeserializer
? (JsonDeserializer) instance
: null;
typeAdapter = new TreeTypeAdapter(serializer, deserializer, gson, type, null);
} else {
throw new IllegalArgumentException("Invalid attempt to bind an instance of "
+ instance.getClass().getName() + " as a @JsonAdapter for " + type.toString()
+ ". @JsonAdapter value must be a TypeAdapter, TypeAdapterFactory,"
+ " JsonSerializer or JsonDeserializer.");
}
if (typeAdapter != null && annotation.nullSafe()) {
typeAdapter = typeAdapter.nullSafe();
}
return typeAdapter;
}
}
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sobot.gson.internal.bind;
import com.sobot.gson.JsonArray;
import com.sobot.gson.JsonElement;
import com.sobot.gson.JsonNull;
import com.sobot.gson.JsonObject;
import com.sobot.gson.JsonPrimitive;
import com.sobot.gson.stream.JsonWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
/**
* This writer creates a JsonElement.
*/
public final class JsonTreeWriter extends JsonWriter {
private static final Writer UNWRITABLE_WRITER = new Writer() {
@Override public void write(char[] buffer, int offset, int counter) {
throw new AssertionError();
}
@Override public void flush() throws IOException {
throw new AssertionError();
}
@Override public void close() throws IOException {
throw new AssertionError();
}
};
/** Added to the top of the stack when this writer is closed to cause following ops to fail. */
private static final JsonPrimitive SENTINEL_CLOSED = new JsonPrimitive("closed");
/** The JsonElements and JsonArrays under modification, outermost to innermost. */
private final List<JsonElement> stack = new ArrayList<>();
/** The name for the next JSON object value. If non-null, the top of the stack is a JsonObject. */
private String pendingName;
/** the JSON element constructed by this writer. */
private JsonElement product = JsonNull.INSTANCE; // TODO: is this really what we want?;
public JsonTreeWriter() {
super(UNWRITABLE_WRITER);
}
/**
* Returns the top level object produced by this writer.
*/
public JsonElement get() {
if (!stack.isEmpty()) {
throw new IllegalStateException("Expected one JSON element but was " + stack);
}
return product;
}
private JsonElement peek() {
return stack.get(stack.size() - 1);
}
private void put(JsonElement value) {
if (pendingName != null) {
if (!value.isJsonNull() || getSerializeNulls()) {
JsonObject object = (JsonObject) peek();
object.add(pendingName, value);
}
pendingName = null;
} else if (stack.isEmpty()) {
product = value;
} else {
JsonElement element = peek();
if (element instanceof JsonArray) {
((JsonArray) element).add(value);
} else {
throw new IllegalStateException();
}
}
}
@Override public JsonWriter beginArray() throws IOException {
JsonArray array = new JsonArray();
put(array);
stack.add(array);
return this;
}
@Override public JsonWriter endArray() throws IOException {
if (stack.isEmpty() || pendingName != null) {
throw new IllegalStateException();
}
JsonElement element = peek();
if (element instanceof JsonArray) {
stack.remove(stack.size() - 1);
return this;
}
throw new IllegalStateException();
}
@Override public JsonWriter beginObject() throws IOException {
JsonObject object = new JsonObject();
put(object);
stack.add(object);
return this;
}
@Override public JsonWriter endObject() throws IOException {
if (stack.isEmpty() || pendingName != null) {
throw new IllegalStateException();
}
JsonElement element = peek();
if (element instanceof JsonObject) {
stack.remove(stack.size() - 1);
return this;
}
throw new IllegalStateException();
}
@Override public JsonWriter name(String name) throws IOException {
if (name == null) {
throw new NullPointerException("name == null");
}
if (stack.isEmpty() || pendingName != null) {
throw new IllegalStateException();
}
JsonElement element = peek();
if (element instanceof JsonObject) {
pendingName = name;
return this;
}
throw new IllegalStateException();
}
@Override public JsonWriter value(String value) throws IOException {
if (value == null) {
return nullValue();
}
put(new JsonPrimitive(value));
return this;
}
@Override public JsonWriter nullValue() throws IOException {
put(JsonNull.INSTANCE);
return this;
}
@Override public JsonWriter value(boolean value) throws IOException {
put(new JsonPrimitive(value));
return this;
}
@Override public JsonWriter value(Boolean value) throws IOException {
if (value == null) {
return nullValue();
}
put(new JsonPrimitive(value));
return this;
}
@Override public JsonWriter value(double value) throws IOException {
if (!isLenient() && (Double.isNaN(value) || Double.isInfinite(value))) {
throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value);
}
put(new JsonPrimitive(value));
return this;
}
@Override public JsonWriter value(long value) throws IOException {
put(new JsonPrimitive(value));
return this;
}
@Override public JsonWriter value(Number value) throws IOException {
if (value == null) {
return nullValue();
}
if (!isLenient()) {
double d = value.doubleValue();
if (Double.isNaN(d) || Double.isInfinite(d)) {
throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value);
}
}
put(new JsonPrimitive(value));
return this;
}
@Override public void flush() throws IOException {
}
@Override public void close() throws IOException {
if (!stack.isEmpty()) {
throw new IOException("Incomplete document");
}
stack.add(SENTINEL_CLOSED);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment