…但是我希望图书馆尽其所能。
好吧,只需使用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 int count ; // 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:
// Gson instances are thread-safe and can be easily instantiated onceprivate 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 int count ; // Gson works with final primitives like `int` strangely实际上,Gson 确实
工作正常。我忘记了Java常量内联。使用获得
count通孔反射
Field效果完美。但是,由于内联,返回了contant值。与相似的普通对象
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修饰符添加到该字段,就像周围的所有字段一样,应添加一个非编译时值:
@SerializedName("count") @Expose private final int count = constOf(0);哪里
constOf(int)仅仅是:
private static int constOf(final int value) { return value;}现在,所有传入的DTO字段都可以轻松声明
final。



