JsonDeserializer由于要执行处理的业务逻辑,因此我正在实现自定义。但是某些部分可以解析为标准方式。这是否可能- 我自己处理一些元素,并让一些嵌套元素自动处理?
JsonDeserializer
这是JSON:
{ "id": "10", "games": ["PZ"], "definition": { "count": 10, "operatorDefinitions": [ { "operators": "+", "first": "1-5", "second": "1-5", "result": "2-5" } ] }
这是definition项的自定义反序列化器:
definition
public class FormulaDefinitionGsonAdapter implements JsonDeserializer<FormulaDefinition> { public FormulaDefinition deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { FormulaDefinition definition = new FormulaDefinition(); JsonObject jsonObject = json.getAsJsonObject(); JsonPrimitive p = jsonObject.getAsJsonPrimitive("count"); definition.setCount(p.getAsInt()); JsonArray array = jsonObject.getAsJsonArray("operatorDefinitions"); if (array == null || array.size() == 0) { throw new JsonParseException("Element 'operatorDefinitions' is missing!"); } for (JsonElement jsonElement : array) { jsonObject = (JsonObject) jsonElement; p = jsonObject.getAsJsonPrimitive("first"); String sValues = p.getAsString(); Values firstArgValues = Values.parse(sValues);
现在,我想让GSON解析operators枚举。我可以自己完成,这只是几行代码,但是我更愿意库做很多。
operators
…但是我希望图书馆尽其所能。
好吧,只需使用Gson。
有数据传输对象模式,尤其是Gson映射类,可以完美地解决您的问题。默认情况下,如果Gson能够满足内置功能的映射,并且除了特殊情况外,您不必自己完成工作。此类映射类仅旨在存在于JSON内容和您的业务对象类之间,以便对数据进行(反)序列化(简单地说,DTO仅为此目的而存在,并且与Gson相关的注释不得散布到您的业务类中- 只需将DTO转换为业务对象)。
final class Wrapper { @SerializedName("id") @Expose private final String id = null; @SerializedName("games") @Expose private final List<String> games = null; @SerializedName("definition") @Expose private final FormulaDefinition formulaDefinition = null; private Wrapper() { } @Override public String toString() { return new StringBuilder("Wrapper{") .append("id='").append(id) .append("', games=").append(games) .append(", formulaDefinition=").append(formulaDefinition) .append('}') .toString(); } } package q41323887; import java.util.List; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; final class FormulaDefinition { @SerializedName("count") @Expose private /*final*/ int count /*= 0*/; // Gson works with final primitives like `int` strangely @SerializedName("operatorDefinitions") @Expose private final List<OperatorDefinition> operatorDefinitions = null; private FormulaDefinition() { } @Override public String toString() { return new StringBuilder("FormulaDefinition{") .append("count=").append(count) .append(", operatorDefinitions=").append(operatorDefinitions) .append('}') .toString(); } } final class OperatorDefinition { @SerializedName("operators") @Expose private final Operator operators = null; @SerializedName("first") @Expose private final String first = null; @SerializedName("second") @Expose private final String second = null; @SerializedName("result") @Expose private final String result = null; private OperatorDefinition() { } @Override public String toString() { return new StringBuilder("OperatorDefinition{") .append("operators=").append(operators) .append(", first='").append(first) .append("', second='").append(second) .append("', result='").append(result) .append("'}") .toString(); } } enum Operator { PLUS("+"), MINUS("-"), ASTERISK("*"), SLASH("/"); private static final Map<String, Operator> tokenToOperatorIndex = createTokenToOperatorIndexInJava8(); private final String token; Operator(final String token) { this.token = token; } static Operator resolveOperator(final String token) throws NoSuchElementException { final Operator operator = tokenToOperatorIndex.get(token); if ( operator == null ) { throw new NoSuchElementException("Cannot resolve operator by " + token); } return operator; } private static Map<String, Operator> createTokenToOperatorIndex() { final Map<String, Operator> index = new HashMap<>(); for ( final Operator operator : values() ) { index.put(operator.token, operator); } return unmodifiableMap(index); } private static Map<String, Operator> createTokenToOperatorIndexInJava8() { final Map<String, Operator> index = Stream.of(values()) .collect(toMap(operator -> operator.token, identity())); return unmodifiableMap(index); } }
然后,由于您本operators打算成为有效的枚举,因此这是您真正需要自定义JSON反序列化器的唯一地方,仅因为Gson默认规则不了解这些规则。
final class OperatorJsonDeserializer implements JsonDeserializer<Operator> { private static final JsonDeserializer<Operator> operatorJsonDeserializer = new OperatorJsonDeserializer(); private OperatorJsonDeserializer() { } static JsonDeserializer<Operator> getOperatorJsonDeserializer() { return operatorJsonDeserializer; } @Override public Operator deserialize(final JsonElement json, final Type type, final JsonDeserializationContext context) throws JsonParseException { try { final String token = json.getAsJsonPrimitive().getAsString(); return resolveOperator(token); } catch ( final NoSuchElementException ex ) { throw new JsonParseException(ex); } } }
现在,您可以使用Wrapper该类反序列化JSON:
Wrapper
// Gson instances are thread-safe and can be easily instantiated once private static final Gson gson = new GsonBuilder() .registerTypeAdapter(Operator.class, getOperatorJsonDeserializer()) .create(); public static void main(final String... args) throws IOException { try ( final Reader reader = new InputStreamReader(EntryPoint.class.getResourceAsStream("/test.json")) ) { final Wrapper wrapper = gson.fromJson(reader, Wrapper.class); out.println(wrapper); // ... convert the wrapper DTO above to your target business object } }
输出:
包装器{id = ‘10’,游戏= [PZ],FormulaDefinition = FormulaDefinition {count = 10,operatorDefinitions = [OperatorDefinition {operators = PLUS,first =‘1-5’,second =‘1-5’,result =’ 2-5’}]}}
我在以下代码片段中对Gson的理解是错误的:
@SerializedName("count") @Expose private /*final*/ int count /*= 0*/; // Gson works with final primitives like `int` strangely
实际上,Gson 确实 工作正常。我忘记了Java常量内联。使用获得count通孔反射Field效果完美。但是,由于内联,返回了contant值。与相似的普通对象javap -p -c:
count
Field
javap -p -c
final class ext.Test$Immutable { private final int foo; private ext.Test$Immutable(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: iconst_0 6: putfield #2 // Field foo:I 9: return private int getFoo(); Code: 0: iconst_0 1: ireturn public java.lang.String toString(); Code: 0: ldc #4 // String (IMMUTABLE:0) 2: areturn }
在这种情况下,甚至toString()返回一个常数。是的,这就是Java和javac工作方式。为了禁用这种内联并将final修饰符添加到该字段,就像周围的所有字段一样,应添加一个非编译时值:
toString()
javac
final
@SerializedName("count") @Expose private final int count = constOf(0);
哪里constOf(int)仅仅是:
constOf(int)
private static int constOf(final int value) { return value; }
现在,所有传入的DTO字段都可以轻松声明final。