Tengo un problema para deserializar una cadena json con Gson. Recibo una serie de comandos. El comando puede ser iniciar, detener, algún otro tipo de comando. Naturalmente, tengo polimorfismo y el comando de inicio / parada hereda del comando.
¿Cómo puedo serializarlo de nuevo en el objeto de comando correcto usando gson?
Parece que obtengo solo el tipo base, que es el tipo declarado y nunca el tipo de tiempo de ejecución.
java
json
polymorphism
gson
deserialization
Sophie
fuente
fuente
Respuestas:
Esto es un poco tarde, pero tuve que hacer exactamente lo mismo hoy. Entonces, según mi investigación y cuando use gson-2.0, realmente no desea usar el método registerTypeHierarchyAdapter , sino el más mundano registerTypeAdapter . Y ciertamente no necesita hacer instanceofs ni escribir adaptadores para las clases derivadas: solo un adaptador para la clase base o interfaz, siempre que, por supuesto, esté satisfecho con la serialización predeterminada de las clases derivadas. De todos modos, aquí está el código (paquete e importaciones eliminados) (también disponible en github ):
La clase base (interfaz en mi caso):
Las dos clases derivadas, Cat:
Y perro:
El IAnimalAdapter:
Y la clase de prueba:
Cuando ejecuta Test :: main, obtiene el siguiente resultado:
De hecho, también hice lo anterior usando el método registerTypeHierarchyAdapter , pero eso parecía requerir la implementación de clases personalizadas de serializador / deserializador de DogAdapter y CatAdapter, que son difíciles de mantener en cualquier momento que desee agregar otro campo a Dog o Cat.
fuente
Gson tiene actualmente un mecanismo para registrar un adaptador de jerarquía de tipos que, según se informa, se puede configurar para una deserialización polimórfica simple, pero no veo cómo ese es el caso, ya que un Adaptador de jerarquía de tipos parece ser solo un serializador / deserializador / creador de instancias combinado, dejando los detalles de la creación de la instancia en manos del codificador, sin proporcionar ningún registro de tipo polimórfico real.
Parece que Gson pronto tendrá la
RuntimeTypeAdapter
deserialización polimórfica más simple. Ver http://code.google.com/p/google-gson/issues/detail?id=231 para obtener más información.Si
RuntimeTypeAdapter
no es posible usar el nuevo y debe usar Gson, entonces creo que tendrá que lanzar su propia solución, registrando un deserializador personalizado como Adaptador de jerarquía de tipos o como Adaptador de tipos. A continuación se muestra uno de esos ejemplos.fuente
RuntimeTypeAdapter
ahora está completo, desafortunadamente no parece que esté en el núcleo de Gson todavía. :-(Marcus Junius Brutus tuvo una gran respuesta (¡gracias!). Para ampliar su ejemplo, puede hacer que su clase de adaptador sea genérica para que funcione para todo tipo de objetos (no solo IAnimal) con los siguientes cambios:
Y en la clase de prueba:
fuente
GSON tiene un caso de prueba bastante bueno aquí que muestra cómo definir y registrar un adaptador de jerarquía de tipos.
http://code.google.com/p/google-gson/source/browse/trunk/gson/src/test/java/com/google/gson/functional/TypeHierarchyAdapterTest.java?r=739
Para usar eso, haz esto:
El método de serialización del adaptador puede ser una verificación en cascada si-si no del tipo que está serializando.
Deserializar es un poco hacky. En el ejemplo de prueba unitaria, verifica la existencia de atributos reveladores para decidir a qué clase deserializar. Si puede cambiar la fuente del objeto que está serializando, puede agregar un atributo 'classType' a cada instancia que contiene el FQN del nombre de la clase de instancia. Sin embargo, esto es muy poco orientado a objetos.
fuente
Google ha lanzado su propio RuntimeTypeAdapterFactory para manejar el polimorfismo, pero desafortunadamente no es parte del núcleo gson (debe copiar y pegar la clase dentro de su proyecto).
Ejemplo:
Aquí he publicado un ejemplo completo de trabajo usando los modelos Animal, Dog y Cat.
Creo que es mejor confiar en este adaptador en lugar de volver a implementarlo desde cero.
fuente
Ha pasado mucho tiempo, pero no pude encontrar una solución realmente buena en línea. Aquí hay un pequeño giro en la solución de @ MarcusJuniusBrutus, que evita la recursividad infinita.
Mantenga el mismo deserializador, pero elimine el serializador -
Luego, en su clase original, agregue un campo con
@SerializedName("CLASSNAME")
. El truco ahora es inicializar esto en el constructor de la clase base , así que convierta su interfaz en una clase abstracta.La razón por la que no hay una recursividad infinita aquí es que pasamos la clase de tiempo de ejecución real (es decir, Perro, no IAnimal) a
context.deserialize
. Esto no llamará a nuestro adaptador de tipo, siempre que usemosregisterTypeAdapter
y noregisterTypeHierarchyAdapter
fuente
Respuesta actualizada: las mejores partes de todas las demás respuestas
Estoy describiendo soluciones para diferentes casos de uso y se dirigiría a la recursividad infinita problema también
Caso 1: Usted está en control de las clases , es decir, se llega a escribir sus propios
Cat
,Dog
clases, así como laIAnimal
interfaz. Simplemente puede seguir la solución proporcionada por @ marcus-junius-brutus (la respuesta mejor calificada)No habrá ninguna recursividad infinita si hay una interfaz base común como
IAnimal
Pero, ¿qué pasa si no quiero implementar la
IAnimal
interfaz o ninguna de ellas?Entonces, @ marcus-junius-brutus (la respuesta mejor calificada) producirá un error de recursividad infinito. En este caso, podemos hacer algo como a continuación.
Tendríamos que crear un constructor de copia dentro de la clase base y una subclase contenedora de la siguiente manera:
.
Y el serializador para el tipo
Cat
:Entonces, ¿por qué un constructor de copias?
Bueno, una vez que defina el constructor de copia, no importa cuánto cambie la clase base, su envoltorio continuará con el mismo rol. En segundo lugar, si no definimos un constructor de copia y simplemente subclasificamos la clase base, entonces tendríamos que "hablar" en términos de la clase extendida, es decir,
CatWrapper
. Es muy posible que sus componentes hablen en términos de la clase base y no del tipo de contenedor.¿Existe una alternativa fácil?
Claro, ahora ha sido introducido por Google; esta es la
RuntimeTypeAdapterFactory
implementación:Aquí, deberías introducir un campo llamado "tipo"
Animal
y el valor del mismo dentroDog
para ser "perro",Cat
para ser "gato".Ejemplo completo: https://static.javadoc.io/org.danilopianini/gson-extras/0.2.1/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.html
Caso 2: No tienes el control de las clases . Te unes a una empresa o usas una biblioteca donde las clases ya están definidas y tu gerente no quiere que las cambies de ninguna manera: puedes subclasificar tus clases y hacer que implementen una interfaz de marcador común (que no tiene ningún método ) como
AnimalInterface
.Ex:
.
Entonces, estaríamos usando en
CatWrapper
lugar deCat
, enDogWrapper
lugar deDog
y enAlternativeAnimalAdapter
lugar deIAnimalAdapter
Realizamos una prueba:
Salida:
fuente
Si desea administrar un TypeAdapter para un tipo y otro para su subtipo, puede usar un TypeAdapterFactory como este:
Esta fábrica enviará el TypeAdapter más preciso
fuente
Si combina la respuesta de Marcus Junius Brutus con la edición de user2242263, puede evitar tener que especificar una jerarquía de clases grande en su adaptador definiendo que su adaptador funciona en un tipo de interfaz. Luego puede proporcionar implementaciones predeterminadas de toJSON () y fromJSON () en su interfaz (que solo incluye estos dos métodos) y hacer que todas las clases que necesita para serializar implementen su interfaz. Para lidiar con la conversión, en sus subclases puede proporcionar un método fromJSON () estático que deserializa y realiza la conversión adecuada desde su tipo de interfaz. Esto funcionó de maravilla para mí (solo tenga cuidado con la serialización / deserialización de clases que contienen hashmaps; agregue esto cuando cree una instancia de su generador de gson:
GsonBuilder builder = new GsonBuilder().enableComplexMapKeySerialization();
¡Espero que esto ayude a alguien a ahorrar tiempo y esfuerzo!
fuente