Gson Class施放异常

时间:2015-06-26 12:45:26

标签: java generics casting gson

我有一个类来保存一些json数据,如下所示:

package org.swx.nursing.tools.configuration.data;

import java.util.Set;

import com.cerner.system.exception.Verifier;
import com.cerner.system.exception.VerifyException;
import com.google.common.collect.ImmutableSet;

/**
 * Class representing a simple {@link JsonData#identifier}, 
 * {@link JsonData#data}  format. This class can be used to 
 * persist application data for example in a Configuration file.
 * 
 * @author SW029693
 * @since v1.0
 */
public class JsonData <T>{
/**
 * Represents a unique identifier 
 */
private String identifier;
/**
 * Represents the data pertaining to this {@link JsonData#identifier} 
 */
private T data;

private static final Set<String> VALID_JSON_ID_TYPES = ImmutableSet.of("CONFIG","HOTKEYS");

public JsonData(String identifier, T data) {
    super();
    this.identifier = identifier;
    this.data = data;
}

/**
 * Getter for {@link JsonData#identifier} 
 * @return
 */
public String getIdentifier() {
    return identifier;
}

/**
 * Sets the {@link JsonData#identifier} to the given value
 * @param  identifier
 *         Represents a unique {@link JsonData#identifier}  
 * @throws VerifyException
 *         If the argument is {@code null} or {@code empty}
 */
public void setIdentifier(String identifier) throws VerifyException{
    Verifier.verifyNotNull(identifier, "identifier : null");
    Verifier.verifyNotEmpty(identifier,"identifier : empty");
    this.identifier = identifier;
}

/**
 * Getter for {@link JsonData} 
 * @return
 */
public T getData() {
    return data;
}

/**
 * Sets the {@link JsonData#data} to the given value
 * @param  identifier
 *         Represents a unique {@link JsonData#data}  
 * @throws VerifyException
 *         If the argument is {@code null} 
 */
public void setData(T data) {
    Verifier.verifyNotNull(data, "data : null");
    this.data = data;
}

@Override
public String toString() {
    return "JsonData [identifier=" + identifier + ", data=" + data + "]";
}
}

我正在尝试将一些数据转换为JSON(将其写入文件),将其读回并转换为上面的JsonData对象。这是我的单元测试失败了:

@Test
@SuppressWarnings("unchecked")
public void testWriteContentsToFile() {
    ConfigurationManager<JsonData> configurationManager = (ConfigurationManager<JsonData>) 
    ConfigurationManager.Factory.create();
    assertNotNull(configurationManager);
    configurationManager.write(getMockJsonData());
    System.out.println("1="+getMockJsonData().getData().toString());

    assertEquals(getMockJsonData().getData(), ((JsonData) readconfigFile()).getData());
}

此测试的辅助方法如下:

 /**
 * Helper method to read the config.json file
 * @return 
 */
private static JsonData<ConfigurationProperty> readconfigFile() {
    Reader reader = null;
    JsonData<ConfigurationProperty> data = null;
    Gson gson  = null;
    try {
        reader = new FileReader("./config.json");
        gson = new GsonBuilder().create();
        data = gson.fromJson(reader, JsonData.class);
        System.out.println("yooo="+data.getData().getRunnableContext());
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        fail("Test failed while reading the config.json file: "+e.getMessage());        }
    finally {
        try {
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
            fail("Test failed while reading the config.json file: "+e.getMessage());
        }
    }
    return data;
}

/**
 * Helper method which creates a mock {@link JsonData} object for testing purposes
 * 
 * @return An instance of the newly created mock {@link Jsondata} object
 */
private static JsonData<ConfigurationProperty> getMockJsonData() {
    JsonData<ConfigurationProperty> data = new JsonData<ConfigurationProperty>("CONFIG",
            getMockConfigurationProperty("testKey", "testContext", "APPLICATION"));
    System.out.println("data ==="+data);
    return data;
}

/**
 * Helper method which creates a {@link ConfigurationProperty} based on the given 
 * non-null and non-empty arguments. This method can be used to get the  
 * {@link ConfigurationProperty} instance created with the desired fields when ALL
 * of the 3 parameters are non-null, non-empty & valid.
 * 
 * @param  mockHotKey
 *         A non-null & non-empty mockHotkey parameter passed in as an argument
 * 
 * @param  mockContext
 *         A non-null & non-empty mockContext parameter passed in as an argument
 * 
 * @param  mockContextType
 *         A non-null & non-empty mockContextType parameter passed in as an argument
 *        
 * @throws VerifierException
 *         If the argument validation for non-null or non-empty arguments as failed. 
 * 
 * @throws IllegalArgumentException
 *         The {@link ConfigurationProperty.Builder#withRunnableContextType(String type) method 
 *         throws an {@link IllegalArgumentException} if the provided type is not supported in
 *         {@link ConfigurationProperty#RUNNABLE_CONTEXT_TYPE}
 *        
 * @return An instance of newly created {@link ConfigurationProperty} object
 */
private static ConfigurationProperty getMockConfigurationProperty (
        String mockHotKey, String mockContext, String mockContextType) {
    Verifier.verifyNotNull(mockHotKey);
    Verifier.verifyNotNull(mockContext);
    Verifier.verifyNotNull(mockContextType);
    Verifier.verifyNotEmpty(mockHotKey);
    Verifier.verifyNotEmpty(mockContext);
    Verifier.verifyNotEmpty(mockContextType);
    return ConfigurationProperty.Builder
              .create()
              .withHotKey(mockHotKey)
              .withRunnableContext(mockContext)
              .withRunnableContextType(mockContextType)
              .build();
}

当我运行单元测试以将json文件中的数据读入我的JsonData对象时,我收到以下错误:

java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to org.swx.nursing.tools.configuration.data.ConfigurationProperty
at org.swx.nursing.tools.configuration.data.ConfigurationManagerImplTest.readconfigFile(ConfigurationManagerImplTest.java:181)
at org.swx.nursing.tools.configuration.data.ConfigurationManagerImplTest.testWriteContentsToFile(ConfigurationManagerImplTest.java:166)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:66)
at org.jmock.integration.junit4.JMock$1.invoke(JMock.java:37)
at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:105)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:86)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:94)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:84)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:98)
at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:61)
at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:54)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:52)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

文件写得像这样:

{
 "identifier": "CONFIG",
 "data": {
   "hotKey": "testKey",
   "type": "APPLICATION",
   "runnableContext": "testContext"
 }
}

我无法阅读&#39;数据&#39;由于上述错误,以上json写入JsonData对象的文件中的一部分。

请告知

由于

1 个答案:

答案 0 :(得分:2)

请参阅https://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Generic-Types

当你调用toJson(obj)时,Gson调用obj.getClass()来获取有关要序列化的字段的信息。类似地,您通常可以在fromJson(json,MyClass.class)方法中传递MyClass.class对象。如果对象是非泛型类型,则此方法正常。但是,如果对象是泛型类型,则由于Java类型擦除而丢失通用类型信息。这是一个说明这一点的例子:

class Foo<T> {
  T value;
}
Gson gson = new Gson();
Foo<Bar> foo = new Foo<Bar>();
gson.toJson(foo); // May not serialize foo.value correctly

gson.fromJson(json, foo.getClass()); // Fails to deserialize foo.value as Bar

上面的代码无法将值解释为类型Bar,因为Gson调用list.getClass()来获取其类信息,但此方法返回一个原始类Foo.class。这意味着Gson无法知道这是Foo类型的对象,而不仅仅是普通的Foo。

您可以通过为通用类型指定正确的参数化类型来解决此问题。您可以使用TypeToken类来完成此操作。

Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);

gson.fromJson(json, fooType);

用于获取fooType的习惯用法实际上定义了一个匿名本地内部类,其中包含一个返回完全参数化类型的方法getType()。