com.jsoniter.spi.Config Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jsoniter Show documentation
Show all versions of jsoniter Show documentation
jsoniter (json-iterator) is fast and flexible JSON parser available in Java and Go
The newest version!
package com.jsoniter.spi;
import com.jsoniter.annotation.*;
import com.jsoniter.output.EncodingMode;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;
public class Config extends EmptyExtension {
private final String configName;
private final Builder builder;
private static volatile Map configs = new HashMap();
private volatile Map decoderCacheKeys = new HashMap();
private volatile Map encoderCacheKeys = new HashMap();
private final static Map primitiveOmitValues = new HashMap() {{
put(boolean.class, new OmitValue.False());
put(char.class, new OmitValue.ZeroChar());
put(byte.class, new OmitValue.ZeroByte());
put(short.class, new OmitValue.ZeroShort());
put(int.class, new OmitValue.ZeroInt());
put(long.class, new OmitValue.ZeroLong());
put(float.class, new OmitValue.ZeroFloat());
put(double.class, new OmitValue.ZeroDouble());
}};
protected Config(String configName, Builder builder) {
this.configName = configName;
this.builder = builder;
}
public String configName() {
return configName;
}
public String getDecoderCacheKey(Type type) {
String cacheKey = decoderCacheKeys.get(type);
if (cacheKey != null) {
return cacheKey;
}
synchronized (this) {
cacheKey = decoderCacheKeys.get(type);
if (cacheKey != null) {
return cacheKey;
}
cacheKey = TypeLiteral.create(type).getDecoderCacheKey(configName);
HashMap newCache = new HashMap(decoderCacheKeys);
newCache.put(type, cacheKey);
decoderCacheKeys = newCache;
return cacheKey;
}
}
public String getEncoderCacheKey(Type type) {
String cacheKey = encoderCacheKeys.get(type);
if (cacheKey != null) {
return cacheKey;
}
synchronized (this) {
cacheKey = encoderCacheKeys.get(type);
if (cacheKey != null) {
return cacheKey;
}
cacheKey = TypeLiteral.create(type).getEncoderCacheKey(configName);
HashMap newCache = new HashMap(encoderCacheKeys);
newCache.put(type, cacheKey);
encoderCacheKeys = newCache;
return cacheKey;
}
}
public DecodingMode decodingMode() {
return builder.decodingMode;
}
protected Builder builder() {
return builder;
}
public Builder copyBuilder() {
return builder.copy();
}
public int indentionStep() {
return builder.indentionStep;
}
public boolean omitDefaultValue() {
return builder.omitDefaultValue;
}
public boolean escapeUnicode() {
return builder.escapeUnicode;
}
public EncodingMode encodingMode() {
return builder.encodingMode;
}
public static class Builder {
private DecodingMode decodingMode;
private EncodingMode encodingMode;
private int indentionStep;
private boolean escapeUnicode = true;
private boolean omitDefaultValue = false;
public Builder() {
String envMode = System.getenv("JSONITER_DECODING_MODE");
if (envMode != null) {
decodingMode = DecodingMode.valueOf(envMode);
} else {
decodingMode = DecodingMode.REFLECTION_MODE;
}
envMode = System.getenv("JSONITER_ENCODING_MODE");
if (envMode != null) {
encodingMode = EncodingMode.valueOf(envMode);
} else {
encodingMode = EncodingMode.REFLECTION_MODE;
}
}
public Builder decodingMode(DecodingMode decodingMode) {
this.decodingMode = decodingMode;
return this;
}
public Builder encodingMode(EncodingMode encodingMode) {
this.encodingMode = encodingMode;
return this;
}
public Builder indentionStep(int indentionStep) {
this.indentionStep = indentionStep;
return this;
}
public Builder omitDefaultValue(boolean omitDefaultValue) {
this.omitDefaultValue = omitDefaultValue;
return this;
}
public Builder escapeUnicode(boolean escapeUnicode) {
this.escapeUnicode = escapeUnicode;
return this;
}
public Config build() {
String configName = JsoniterSpi.assignConfigName(this);
Config config = configs.get(configName);
if (config != null) {
return config;
}
synchronized (Config.class) {
config = configs.get(configName);
if (config != null) {
return config;
}
config = doBuild(configName);
HashMap newCache = new HashMap(configs);
newCache.put(configName, config);
configs = newCache;
return config;
}
}
protected Config doBuild(String configName) {
return new Config(configName, this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Builder builder = (Builder) o;
if (indentionStep != builder.indentionStep) return false;
if (escapeUnicode != builder.escapeUnicode) return false;
if (decodingMode != builder.decodingMode) return false;
if (omitDefaultValue != builder.omitDefaultValue) return false;
return encodingMode == builder.encodingMode;
}
@Override
public int hashCode() {
int result = decodingMode != null ? decodingMode.hashCode() : 0;
result = 31 * result + (encodingMode != null ? encodingMode.hashCode() : 0);
result = 31 * result + indentionStep;
result = 31 * result + (escapeUnicode ? 1 : 0);
result = 31 * result + (omitDefaultValue ? 1 : 0);
return result;
}
public Builder copy() {
Builder builder = new Builder();
builder.encodingMode = encodingMode;
builder.decodingMode = decodingMode;
builder.indentionStep = indentionStep;
builder.escapeUnicode = escapeUnicode;
builder.omitDefaultValue = omitDefaultValue;
return builder;
}
@Override
public String toString() {
return "Config{" +
"decodingMode=" + decodingMode +
", encodingMode=" + encodingMode +
", indentionStep=" + indentionStep +
", escapeUnicode=" + escapeUnicode +
", omitDefaultValue=" + omitDefaultValue +
'}';
}
}
public static final Config INSTANCE = new Builder().build();
@Override
public void updateClassDescriptor(ClassDescriptor desc) {
JsonObject jsonObject = (JsonObject) desc.clazz.getAnnotation(JsonObject.class);
if (jsonObject != null) {
if (jsonObject.asExtraForUnknownProperties()) {
desc.asExtraForUnknownProperties = true;
}
for (String fieldName : jsonObject.unknownPropertiesWhitelist()) {
Binding binding = new Binding(desc.classInfo, desc.lookup, Object.class);
binding.name = fieldName;
binding.fromNames = new String[]{binding.name};
binding.toNames = new String[0];
binding.shouldSkip = true;
desc.fields.add(binding);
}
for (String fieldName : jsonObject.unknownPropertiesBlacklist()) {
Binding binding = new Binding(desc.classInfo, desc.lookup, Object.class);
binding.name = fieldName;
binding.fromNames = new String[]{binding.name};
binding.toNames = new String[0];
binding.asExtraWhenPresent = true;
desc.fields.add(binding);
}
}
List allMethods = new ArrayList();
Class current = desc.clazz;
while (current != null) {
allMethods.addAll(Arrays.asList(current.getDeclaredMethods()));
current = current.getSuperclass();
}
updateBindings(desc);
detectCtor(desc);
detectStaticFactory(desc, allMethods);
detectWrappers(desc, allMethods);
detectUnwrappers(desc, allMethods);
}
private void detectUnwrappers(ClassDescriptor desc, List allMethods) {
for (Method method : allMethods) {
if (Modifier.isStatic(method.getModifiers())) {
continue;
}
if (getJsonUnwrapper(method.getAnnotations()) == null) {
continue;
}
desc.unwrappers.add(new UnwrapperDescriptor(method));
}
}
private void detectWrappers(ClassDescriptor desc, List allMethods) {
for (Method method : allMethods) {
if (Modifier.isStatic(method.getModifiers())) {
continue;
}
JsonWrapper jsonWrapper = getJsonWrapper(method.getAnnotations());
if (jsonWrapper == null) {
continue;
}
Annotation[][] annotations = method.getParameterAnnotations();
String[] paramNames = getParamNames(method, annotations.length);
Iterator iter = desc.setters.iterator();
while(iter.hasNext()) {
if (method.equals(iter.next().method)) {
iter.remove();
}
}
if (JsonWrapperType.BINDING.equals(jsonWrapper.value())) {
WrapperDescriptor wrapper = new WrapperDescriptor();
wrapper.method = method;
for (int i = 0; i < annotations.length; i++) {
Annotation[] paramAnnotations = annotations[i];
Binding binding = new Binding(desc.classInfo, desc.lookup, method.getGenericParameterTypes()[i]);
JsonProperty jsonProperty = getJsonProperty(paramAnnotations);
if (jsonProperty != null) {
updateBindingWithJsonProperty(binding, jsonProperty);
}
if (binding.name == null || binding.name.length() == 0) {
binding.name = paramNames[i];
}
binding.fromNames = new String[]{binding.name};
binding.toNames = new String[]{binding.name};
binding.annotations = paramAnnotations;
wrapper.parameters.add(binding);
}
desc.bindingTypeWrappers.add(wrapper);
} else if (JsonWrapperType.KEY_VALUE.equals(jsonWrapper.value())) {
desc.keyValueTypeWrappers.add(method);
} else {
throw new JsonException("unknown json wrapper type: " + jsonWrapper.value());
}
}
}
private String[] getParamNames(Object obj, int paramCount) {
String[] paramNames = new String[paramCount];
try {
Object params = reflectCall(obj, "getParameters");
for (int i = 0; i < paramNames.length; i++) {
paramNames[i] = (String) reflectCall(Array.get(params, i), "getName");
}
} catch (Exception e) {
}
return paramNames;
}
private Object reflectCall(Object obj, String methodName, Object... args) throws Exception {
Method method = obj.getClass().getMethod(methodName);
return method.invoke(obj, args);
}
private void detectStaticFactory(ClassDescriptor desc, List allMethods) {
for (Method method : allMethods) {
if (!Modifier.isStatic(method.getModifiers())) {
continue;
}
JsonCreator jsonCreator = getJsonCreator(method.getAnnotations());
if (jsonCreator == null) {
continue;
}
desc.ctor.staticMethodName = method.getName();
desc.ctor.staticFactory = method;
desc.ctor.ctor = null;
Annotation[][] annotations = method.getParameterAnnotations();
String[] paramNames = getParamNames(method, annotations.length);
for (int i = 0; i < annotations.length; i++) {
Annotation[] paramAnnotations = annotations[i];
JsonProperty jsonProperty = getJsonProperty(paramAnnotations);
Binding binding = new Binding(desc.classInfo, desc.lookup, method.getGenericParameterTypes()[i]);
if (jsonProperty != null) {
updateBindingWithJsonProperty(binding, jsonProperty);
}
if (binding.name == null || binding.name.length() == 0) {
binding.name = paramNames[i];
}
binding.fromNames = new String[]{binding.name};
binding.toNames = new String[]{binding.name};
binding.annotations = paramAnnotations;
desc.ctor.parameters.add(binding);
}
}
}
private void detectCtor(ClassDescriptor desc) {
if (desc.ctor == null) {
return;
}
for (Constructor ctor : desc.clazz.getDeclaredConstructors()) {
JsonCreator jsonCreator = getJsonCreator(ctor.getAnnotations());
if (jsonCreator == null) {
continue;
}
desc.ctor.staticMethodName = null;
desc.ctor.ctor = ctor;
desc.ctor.staticFactory = null;
Annotation[][] annotations = ctor.getParameterAnnotations();
String[] paramNames = getParamNames(ctor, annotations.length);
for (int i = 0; i < annotations.length; i++) {
Annotation[] paramAnnotations = annotations[i];
JsonProperty jsonProperty = getJsonProperty(paramAnnotations);
Binding binding = new Binding(desc.classInfo, desc.lookup, ctor.getGenericParameterTypes()[i]);
if (jsonProperty != null) {
updateBindingWithJsonProperty(binding, jsonProperty);
}
if (binding.name == null || binding.name.length() == 0) {
binding.name = paramNames[i];
}
binding.fromNames = new String[]{binding.name};
binding.toNames = new String[]{binding.name};
binding.annotations = paramAnnotations;
desc.ctor.parameters.add(binding);
}
}
}
private void updateBindings(ClassDescriptor desc) {
boolean globalOmitDefault = JsoniterSpi.getCurrentConfig().omitDefaultValue();
for (Binding binding : desc.allBindings()) {
boolean annotated = false;
JsonIgnore jsonIgnore = getJsonIgnore(binding.annotations);
if (jsonIgnore != null) {
annotated = true;
if (jsonIgnore.ignoreDecoding()) {
binding.fromNames = new String[0];
}
if (jsonIgnore.ignoreEncoding()) {
binding.toNames = new String[0];
}
}
// map JsonUnwrapper is not getter
JsonUnwrapper jsonUnwrapper = getJsonUnwrapper(binding.annotations);
if (jsonUnwrapper != null) {
annotated = true;
binding.fromNames = new String[0];
binding.toNames = new String[0];
}
if (globalOmitDefault) {
binding.defaultValueToOmit = createOmitValue(binding.valueType);
}
JsonProperty jsonProperty = getJsonProperty(binding.annotations);
if (jsonProperty != null) {
annotated = true;
updateBindingWithJsonProperty(binding, jsonProperty);
}
if (getAnnotation(binding.annotations, JsonMissingProperties.class) != null) {
annotated = true;
// this binding will not bind from json
// instead it will be set by jsoniter with missing property names
binding.fromNames = new String[0];
desc.onMissingProperties = binding;
}
if (getAnnotation(binding.annotations, JsonExtraProperties.class) != null) {
annotated = true;
// this binding will not bind from json
// instead it will be set by jsoniter with extra properties
binding.fromNames = new String[0];
desc.onExtraProperties = binding;
}
if (annotated && binding.field != null) {
if (desc.setters != null) {
for (Binding setter : desc.setters) {
if (binding.field.getName().equals(setter.name)) {
setter.fromNames = new String[0];
setter.toNames = new String[0];
}
}
}
if (desc.getters != null) {
for (Binding getter : desc.getters) {
if (binding.field.getName().equals(getter.name)) {
getter.fromNames = new String[0];
getter.toNames = new String[0];
}
}
}
}
}
}
private void updateBindingWithJsonProperty(Binding binding, JsonProperty jsonProperty) {
binding.asMissingWhenNotPresent = jsonProperty.required();
binding.isNullable = jsonProperty.nullable();
binding.isCollectionValueNullable = jsonProperty.collectionValueNullable();
String defaultValueToOmit = jsonProperty.defaultValueToOmit();
if (!defaultValueToOmit.isEmpty()) {
binding.defaultValueToOmit = OmitValue.Parsed.parse(binding.valueType, defaultValueToOmit);
}
String altName = jsonProperty.value();
if (!altName.isEmpty()) {
if (binding.name == null) {
binding.name = altName;
}
binding.fromNames = new String[]{altName};
binding.toNames = new String[]{altName};
}
if (jsonProperty.from().length > 0) {
binding.fromNames = jsonProperty.from();
}
if (jsonProperty.to().length > 0) {
binding.toNames = jsonProperty.to();
}
Class decoderClass = jsonProperty.decoder();
if (decoderClass != Decoder.class) {
try {
try {
Constructor decoderCtor = decoderClass.getConstructor(Binding.class);
binding.decoder = (Decoder) decoderCtor.newInstance(binding);
} catch (NoSuchMethodException e) {
binding.decoder = (Decoder) decoderClass.newInstance();
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new JsonException(e);
}
}
Class encoderClass = jsonProperty.encoder();
if (encoderClass != Encoder.class) {
try {
try {
Constructor encoderCtor = encoderClass.getConstructor(Binding.class);
binding.encoder = (Encoder) encoderCtor.newInstance(binding);
} catch (NoSuchMethodException e) {
binding.encoder = (Encoder) encoderClass.newInstance();
}
} catch (JsonException e) {
throw e;
} catch (Exception e) {
throw new JsonException(e);
}
}
if (jsonProperty.implementation() != Object.class) {
binding.valueType = GenericsHelper.useImpl(binding.valueType, jsonProperty.implementation());
binding.valueTypeLiteral = TypeLiteral.create(binding.valueType);
}
}
protected OmitValue createOmitValue(Type valueType) {
OmitValue omitValue = primitiveOmitValues.get(valueType);
if (omitValue != null) {
return omitValue;
}
return new OmitValue.Null();
}
protected JsonWrapper getJsonWrapper(Annotation[] annotations) {
return getAnnotation(annotations, JsonWrapper.class);
}
protected JsonUnwrapper getJsonUnwrapper(Annotation[] annotations) {
return getAnnotation(annotations, JsonUnwrapper.class);
}
protected JsonCreator getJsonCreator(Annotation[] annotations) {
return getAnnotation(annotations, JsonCreator.class);
}
protected JsonProperty getJsonProperty(Annotation[] annotations) {
return getAnnotation(annotations, JsonProperty.class);
}
protected JsonIgnore getJsonIgnore(Annotation[] annotations) {
return getAnnotation(annotations, JsonIgnore.class);
}
protected static T getAnnotation(Annotation[] annotations, Class annotationClass) {
if (annotations == null) {
return null;
}
for (Annotation annotation : annotations) {
if (annotationClass.isAssignableFrom(annotation.getClass())) {
return (T) annotation;
}
}
return null;
}
}