2017-02-28 71 views
1

我有一個JSON對象,看起來像這樣如何將JSON對象子項序列化到字段中?

{ 
    "foo":{ 
     "bar":"bar", 
     "echo":"echo" 
    } 
} 

但後來我的Java對象是這樣的:

class Foo { 
    public String foo2; 
} 

我想直接序列echofoo。是這樣的可能:

class Foo { 
    @SerializedName("foo/echo") 
    public String foo2; 
} 

或者我該如何做一個自定義的解串器?

+0

您發佈的json無效,密鑰必須是字符串 – lelloman

回答

0

你必須寫的所有變量的模型類,然後你可以使用

Gson gson=new Gson(); 
ClassName objectName=gson.fromJson(yourJsonObject,ClassName.class); 

這裏objectName包含您的JSON

1

我假設你使用GSON。爲JSONObject創建不同的類。

public class FooModel { 
    @SerializedName("foo") 
    public Foo foo; 

    public class Foo { 
     @SerializedName("bar") 
     public String Bar; 

     @SerializedName("echo") 
     public String Echo; 
    } 
} 
0
import com.google.gson.annotations.Expose; 
import com.google.gson.annotations.SerializedName; 

public class Example { 

@SerializedName("foo") 
@Expose 
private Foo foo; 

public Foo getFoo() { 
return foo; 
} 

public void setFoo(Foo foo) { 
this.foo = foo; 
} 

} 

import com.google.gson.annotations.Expose; 
import com.google.gson.annotations.SerializedName; 

public class Foo { 

@SerializedName("bar") 
@Expose 
private String bar; 
@SerializedName("echo") 
@Expose 
private String echo; 

public String getBar() { 
return bar; 
} 

public void setBar(String bar) { 
this.bar = bar; 
} 

public String getEcho() { 
return echo; 
} 

public void setEcho(String echo) { 
this.echo = echo; 
} 

} 

你可以找到更多的細節here

0

是的,你可以做到這一點

添加此導入

import com.google.gson.annotations.SerializedName; 

,並聲明變量這樣

@SerializedName("echo") 
    private String myCustomeName; 
1

不幸的是,您不能使用@SerializedName來做到這一點,因爲它用於流式解析,因此Gson無法提前解析路徑表達式。然而,這個想法會很好,但至少需要將一個子樹存儲在內存中,這在某些情況下可能會消耗太多內存。由於JsonSerializerJsonDeserializer僅適用於JSON樹,因此您可以輕鬆編寫自己的JSON解串器,它可以省略不必要的JSON對象(在語義上等效於@SerializedName中想要的表達式)。所以,

// To unwrap the top-most JSON object 
final class Wrapper { 
    Foo foo; 
} 
final class Foo { 
    String foo2;  
} 

解串器可以這樣實現(但是你應該記住,JsonSerializerJsonDeserializer不要用GSON內置ReflectiveTypeAdapterFactory,實際上可以處理@SerializedName玩):

final class FooJsonDeserializer 
     implements JsonDeserializer<Foo> { 

    private static final JsonDeserializer<Foo> fooJsonDeserializer = new FooJsonDeserializer(); 

    private FooJsonDeserializer() { 
    } 

    static JsonDeserializer<Foo> getFooJsonDeserializer() { 
     return fooJsonDeserializer; 
    } 

    @Override 
    public Foo deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context) { 
     final JsonObject jsonObject = jsonElement.getAsJsonObject(); 
     final Foo foo = new Foo(); 
     foo.foo2 = jsonObject.get("echo").getAsString(); 
     return foo; 
    } 

} 

實施例使用:

private static final Gson gson = new GsonBuilder() 
     .registerTypeAdapter(Foo.class, getFooJsonDeserializer()) 
     .create(); 

public static void main(final String... args) { 
    final Wrapper wrapper = gson.fromJson("{\"foo\":{\"bar\":\"bar\",\"echo\":\"echo\"}}", Wrapper.class); 
    System.out.println(wrapper.foo.foo2); 
} 

輸出:

呼應

1

作爲一種替代方法,您也可以以JSON表達式應用到不存在的領域創建自己類型的適配器。如果您可以自由地將新庫添加到您正在處理的項目中,它可以基於JsonPath

具有這樣的非標準型適配器,則可以省略中間映射類直接綁定到丟失的字段:

final class Foo { 

    // or @JsonPathExpression("foo.echo") 
    @JsonPathExpression("$.foo.echo") 
    String foo2; 

} 

@JsonPathExpression是自定義註釋,它可以自己處理(JsonPath可能是一個較短的名字,但它已經被JsonPath庫佔領,這樣不會使混亂):

@Retention(RUNTIME) 
@Target(FIELD) 
@interface JsonPathExpression { 

    String value(); 

} 

型適配器允許編寫複雜的序列化/反序列化策略,其特點之一可以將它們組合起來編寫後處理程序,因此,例如,可以處理自定義註釋。

final class JsonPathTypeAdapterFactory 
     implements TypeAdapterFactory { 

    // The type adapter factory is stateless so it can be instantiated once 
    private static final TypeAdapterFactory jsonPathTypeAdapterFactory = new JsonPathTypeAdapterFactory(); 

    private JsonPathTypeAdapterFactory() { 
    } 

    static TypeAdapterFactory getJsonPathTypeAdapterFactory() { 
     return jsonPathTypeAdapterFactory; 
    } 

    @Override 
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) { 
     // Pick up the down stream type adapter to avoid infinite recursion 
     final TypeAdapter<T> delegateAdapter = gson.getDelegateAdapter(this, typeToken); 
     // Collect @JsonPathExpression-annotated fields 
     final Collection<FieldInfo> fieldInfos = FieldInfo.of(typeToken.getRawType()); 
     // If no such fields found, then just return the delegated type adapter 
     // Otherwise wrap the type adapter in order to make some annotation processing 
     return fieldInfos.isEmpty() 
       ? delegateAdapter 
       : new JsonPathTypeAdapter<>(gson, delegateAdapter, gson.getAdapter(JsonElement.class), fieldInfos); 
    } 

    private static final class JsonPathTypeAdapter<T> 
      extends TypeAdapter<T> { 

     private final Gson gson; 
     private final TypeAdapter<T> delegateAdapter; 
     private final TypeAdapter<JsonElement> jsonElementTypeAdapter; 
     private final Collection<FieldInfo> fieldInfos; 

     private JsonPathTypeAdapter(final Gson gson, final TypeAdapter<T> delegateAdapter, final TypeAdapter<JsonElement> jsonElementTypeAdapter, 
       final Collection<FieldInfo> fieldInfos) { 
      this.gson = gson; 
      this.delegateAdapter = delegateAdapter; 
      this.jsonElementTypeAdapter = jsonElementTypeAdapter; 
      this.fieldInfos = fieldInfos; 
     } 

     @Override 
     public void write(final JsonWriter out, final T value) 
       throws IOException { 
      // JsonPath can only read by expression, but not write by expression, so we can only write it as it is... 
      delegateAdapter.write(out, value); 
     } 

     @Override 
     public T read(final JsonReader in) 
       throws IOException { 
      // Building the original JSON tree to keep *all* fields 
      final JsonElement outerJsonElement = jsonElementTypeAdapter.read(in).getAsJsonObject(); 
      // Deserialize the value, not-existing fields will be omitted 
      final T value = delegateAdapter.fromJsonTree(outerJsonElement); 
      for (final FieldInfo fieldInfo : fieldInfos) { 
       try { 
        // Resolving JSON element by a JSON path expression 
        final JsonElement innerJsonElement = fieldInfo.jsonPath.read(outerJsonElement); 
        // And convert it to the field type 
        final Object innerValue = gson.fromJson(innerJsonElement, fieldInfo.field.getType()); 
        // Since now it's what can be assigned to the object field... 
        fieldInfo.field.set(value, innerValue); 
       } catch (final PathNotFoundException ignored) { 
        // if no path given, then just ignore the assignment to the field 
       } catch (final IllegalAccessException ex) { 
        throw new IOException(ex); 
       } 
      } 
      return value; 
     } 

    } 

    private static final class FieldInfo { 

     private final Field field; 
     private final JsonPath jsonPath; 

     private FieldInfo(final Field field, final JsonPath jsonPath) { 
      this.field = field; 
      this.jsonPath = jsonPath; 
     } 

     // Scan the given class for the JsonPathExpressionAnnotation 
     private static Collection<FieldInfo> of(final Class<?> clazz) { 
      Collection<FieldInfo> collection = emptyList(); 
      for (final Field field : clazz.getDeclaredFields()) { 
       final JsonPathExpression jsonPathExpression = field.getAnnotation(JsonPathExpression.class); 
       if (jsonPathExpression != null) { 
        if (collection.isEmpty()) { 
         collection = new ArrayList<>(); 
        } 
        field.setAccessible(true); 
        collection.add(new FieldInfo(field, compile(jsonPathExpression.value()))); 
       } 
      } 
      return collection; 
     } 

    } 

} 

現在GSON和JsonPath都必須配置(後者不使用默認GSON):

private static final Gson gson = new GsonBuilder() 
     .registerTypeAdapterFactory(getJsonPathTypeAdapterFactory()) 
     .create(); 

static { 
    final JsonProvider jsonProvider = new GsonJsonProvider(gson); 
    final MappingProvider gsonMappingProvider = new GsonMappingProvider(gson); 
    Configuration.setDefaults(new Configuration.Defaults() { 
     @Override 
     public JsonProvider jsonProvider() { 
      return jsonProvider; 
     } 

     @Override 
     public MappingProvider mappingProvider() { 
      return gsonMappingProvider; 
     } 

     @Override 
     public Set<Option> options() { 
      return EnumSet.noneOf(Option.class); 
     } 
    }); 

} 

以及它如何使用:

final Foo foo = gson.fromJson("{\"foo\":{\"bar\":\"bar\",\"echo\":\"echo\"}}", Foo.class); 
System.out.println(foo.foo2); 
final String json = gson.toJson(foo); 
System.out.println(json); 

輸出:

echo
{「foo2的」:「回聲」}

注意,這種方法有兩個缺點:

  • 它不能被用來編寫原來的JSON回來,由於根本原因一樣破壞原有的信息和JsonPath只讀語義。
  • 如果給定的JSON文檔包含已由同名對象字段映射的對象屬性,則下游解析器(由上述類型適配器使用)具有較高的優先級,因此會導致JSON解析錯誤。
+0

謝謝真的很明確的解釋。我認爲我可以應對 –

+0

@JohnErnestGuadalupe不客氣。 –