简体   繁体   中英

Gson Class cast exception

I have a class to hold some json data as follows:

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 + "]";
}
}

I am trying to convert some data to JSON (write it to a file), read it back and cast into the above JsonData object. This is my unit test which fails:

@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());
}

The helper methods for this test are as follows:

 /**
 * 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;
}

and

/**
 * 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();
}

I am getting the following error when i run the unit test to read the data from the json file into my JsonData object:

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)

The file gets written like this:

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

I am unable to read the 'data' part of the above json written into the file into the JsonData object due to the above error.

Please advise

Thanks

please see https://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Generic-Types

When you call toJson(obj), Gson calls obj.getClass() to get information on the fields to serialize. Similarly, you can typically pass MyClass.class object in the fromJson(json, MyClass.class) method. This works fine if the object is a non-generic type. However, if the object is of a generic type, then the Generic type information is lost because of Java Type Erasure. Here is an example illustrating the point:

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

The above code fails to interpret value as type Bar because Gson invokes list.getClass() to get its class information, but this method returns a raw class, Foo.class. This means that Gson has no way of knowing that this is an object of type Foo, and not just plain Foo.

You can solve this problem by specifying the correct parameterized type for your generic type. You can do this by using the TypeToken class.

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

gson.fromJson(json, fooType);

The idiom used to get fooType actually defines an anonymous local inner class containing a method getType() that returns the fully parameterized type.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM