2017-01-31 6 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. Он сохраняет вывод случайного UUID, хотя я хочу загрузить его из файла JSON. Вероятно, я получаю неправильный экземпляр дочернего класса, но у меня нет идей о том, как это исправить. (Использование this в gson.fromJson(.....); не работает.

+1

'gson.fromJson (reader, this.getClass());' - здесь теряется ваше значение. Метод 'fromJson' возвращает значение _new_, которое вы должны назначить где-нибудь. –

+0

Имеет смысл, как бы я это сделал с моим текущим примером? –

ответ

1

Вы пропускаете, чтобы присвоить значение для чтения к экземпляру конфигурации. Java не может ничего поддерживать, как this = gson.fromJson(...) и Gson может возвращать только новых значения и не может исправить существующие. ниже - это своего рода взлом Gson, и, пожалуйста, используйте его, только если это действительно должно быть. Опять же, я настоятельно рекомендую вам переконфигурировать ваш код и разделить ваши объекты конфигурации и устройства чтения/чтения конфигурации - это всего лишь два разные вещи, которые противоречат техническим аспектам. В результате рефакторинга вы могли бы, скажем так, получить экземпляр своей конфигурации, просто передайте его автору, чтобы он оставался в другом месте. Если вам это нужно b извед, то просто получить экземпляр читателя прочитать значение конфигурации и присвоить его конфигурации (конфигурации одиночек, я помню), как:

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

По крайней мере, этот код не смешивает две разные вещи, и делает результат reader.readявно прочитал и присвоен вашей конфигурации singleton.

Если вы хорошо, чтобы открыть ворота зла и сделать код работу из-за взломов, то вы можете использовать Gson TypeAdapterFactory для того, чтобы обмануть Gson и залатать текущий экземпляр конфигурации.

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 использует отражение, способ подглядывать поля под капотом, и это потрясающе для хорошо продуманных объектов. Вам нужно некоторое рефакторинг и отдельные два понятия: данные и запись/чтение данных.

Смежные вопросы