2017-01-31 20 views
0

我有一個抽象類用於配置文件,它可以通過很多其他類進行擴展。我設法讓系統將它寫入JSON,但現在我需要加載函數。Gson Java - JSON中的子類

這裏的一般配置類:

public class Configuration { 

    public boolean load(){ 
     FileReader reader = new FileReader(this.getClass().getSimpleName() + ".json"); 
     Gson gson = new Gson(); 
     gson.fromJson(reader, this.getClass()); 
     reader.close(); 
     /** Doesn't give an error, but doesn't set any info to the child class */ 
    } 

    public boolean save(){ 
     FileWriter writer = new FileWriter(this.getClass().getSimpleName() + ".json"); 
     Gson gson = new Gson(); 
     gson.toJson(this, writer); 
     writer.close(); 
     /** This all works fine. */ 
    } 

} 

下面是一個擴展類的例子:我會做

public class ExampleConfig extends Configuration { 

    private static transient ExampleConfig i = new ExampleConfig(); 
    public static ExampleConfig get() { return i; } 

    @Expose public String ServerID = UUID.randomUUID().toString(); 

} 

在我的主類:

ExampleConfig.get().load(); 
System.out.println(ExampleConfig.get().ServerID); 

這不給出任何錯誤,但是從JSON加載的類都不是。即使我想從JSON文件中加載一個UUID,它也會繼續輸出一個隨機的UUID。我可能得到了孩子班的錯誤實例,但我對如何解決這個問題沒有想法。 (使用thisgson.fromJson(.....);不起作用。

+1

'gson.fromJson(reader,this.getClass());' - 這是你的值丟失的地方。 'fromJson'方法返回一個_new_值,你必須在某處指定。 –

+0

有道理,我將如何用我目前的例子來做到這一點? –

回答

1

你缺少的讀取值分配給您的配置實例。Java可以不支持任何類似this = gson.fromJson(...)和GSON只能返回值,並不能修補現有的。該下面是一種Gson破解版,如果它真的是必須爲,那麼請僅使用它。同樣,我強烈建議您重新設計代碼並將您的配置對象和配置讀者/作者分開 - 這些只是兩個不同的事情從技術角度來看是相互衝突的,作爲重構的結果,你可以說,一旦你得到了你的配置實例,就把它委託給一個編寫者來保存它,如果你需要的話b ACK,那麼就得到了讀者的一個實例,讀取配置值,並將其分配給您的配置(配置是單身,我記不清了),如:

final ConfigurationWriter writer = getConfigurationWriter(); 
writer.write(ExampleConfig.get()); 
... 
final ConfigurationReader reader = getConfigurationReader(); 
ExampleConfig.set(reader.read(ExampleConfig.class)); 

至少這個代碼不混合兩種不同的東西,並使reader.read的結果爲明確讀取並分配給您的配置單例。

如果你沒事打開罪惡的大門,使因爲黑客的代碼的工作,那麼你可以是爲了騙取GSON和修補當前的配置實例使用GSON TypeAdapterFactory

abstract class Configuration { 

    private static final Gson saveGson = new Gson(); 

    public final void load() 
      throws IOException { 
     try (final FileReader reader = new FileReader(getTargetName())) { 
      // You have to instantiate Gson every time (unless you use caching strategies) in order to let it be *specifically* be aware of the current 
      // Configuration instance class. Thus you cannot make it a static field. 
      final Gson loadGson = new GsonBuilder() 
        .registerTypeAdapterFactory(new TypeAdapterFactory() { 
         // A Gson way to denote a type since Configuration.class may not be enough and it also works with generics 
         private final TypeToken<Configuration> configurationTypeToken = new TypeToken<Configuration>() { 
         }; 

         @Override 
         @SuppressWarnings("deprecation") // isAssignableFrom is deprecated 
         public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) { 
          // Checking if the type token represents a parent class for the given configuration 
          // If yes, then we cheat... 
          if (configurationTypeToken.isAssignableFrom(typeToken)) { 
           // The map that's artificially bound as great cheating to a current configuration instance 
           final Map<Type, InstanceCreator<?>> instanceCreators = bindInstance(typeToken.getType(), Configuration.this); 
           // A factory used by Gson internally, we're intruding into its heart 
           final ConstructorConstructor constructorConstructor = new ConstructorConstructor(instanceCreators); 
           final TypeAdapterFactory delegatedTypeAdapterFactory = new ReflectiveTypeAdapterFactory(
             constructorConstructor, 
             gson.fieldNamingStrategy(), 
             gson.excluder(), 
             new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor) 
           ); 
           // Since the only thing necessary here is to define how to instantiate an object 
           // (and we just give it an already-existing instance) 
           // ... just delegate the job to Gson -- it would think as if it's creating a new instance. 
           // Actually it won't create one, but would "patch" the current instance 
           return delegatedTypeAdapterFactory.create(gson, typeToken); 
          } 
          // Otherwise returning a null means looking up for an existing type adapter from how Gson is configured 
          return null; 
         } 
        }) 
        .create(); 
      // The value is still loaded to nowhere, however. 
      // The type adapter factory is tightly bound to an existing configuration instance via ConstructorConstructor 
      // This is actually another code smell... 
      loadGson.fromJson(reader, getClass()); 
     } 
    } 

    public final void save() 
      throws IOException { 
     try (final FileWriter writer = new FileWriter(getTargetName())) { 
      saveGson.toJson(this, writer); 
     } 
    } 

    private String getTargetName() { 
     return getClass().getSimpleName() + ".json"; 
    } 

    private static Map<Type, InstanceCreator<?>> bindInstance(final Type type, final Configuration existingConfiguration) { 
     return singletonMap(type, new InstanceCreator<Object>() { 
      @Override 
      public Object createInstance(final Type t) { 
       return t.equals(type) ? existingConfiguration : null; // don't know if null is allowed here though 
      } 
     }); 
    } 

} 

我希望上面的代碼中的註釋是詳盡的。正如我上面所說,我懷疑你是否需要它只是因爲有意識有一些更好的代碼。你可以爭辯說,java.util.Properties可以加載和保存自己。是的,的確如此,但java.util.Properties可以通過設計對其屬性進行迭代,並且始終可以從其他位置讀取和寫入屬性。 Gson使用反射,一種窺視引擎蓋下方領域的方法,這對於精心設計的對象來說非常棒。您需要重構並分離兩個概念:數據和數據寫入器/讀取器。