Recibo un objeto JSON de una llamada AJAX a un servidor REST. Este objeto tiene nombres de propiedad que coinciden con mi clase TypeScript (esto es una continuación de esta pregunta ).
¿Cuál es la mejor manera de inicializarlo? No creo que esto funcione porque la clase (& objeto JSON) tiene miembros que son listas de objetos y miembros que son clases, y esas clases tienen miembros que son listas y / o clases.
Pero preferiría un enfoque que busque los nombres de los miembros y los asigne, creando listas e instanciando clases según sea necesario, por lo que no tengo que escribir código explícito para cada miembro en cada clase (¡hay MUCHO!)
json
typescript
David Thielen
fuente
fuente
Respuestas:
Estas son algunas tomas rápidas para mostrar algunas formas diferentes. De ninguna manera son "completos" y, como descargo de responsabilidad, no creo que sea una buena idea hacerlo así. Además, el código no está demasiado limpio, ya que lo escribí todo bastante rápido.
También como una nota: por supuesto, las clases deserializables deben tener constructores predeterminados, como es el caso en todos los demás idiomas donde estoy al tanto de cualquier deserialización. Por supuesto, Javascript no se quejará si llama a un constructor no predeterminado sin argumentos, pero es mejor que la clase esté preparada para eso (además, realmente no sería la "forma de tipografía").
Opción # 1: no hay información de tiempo de ejecución
El problema con este enfoque es principalmente que el nombre de cualquier miembro debe coincidir con su clase. Lo que lo limita automáticamente a un miembro del mismo tipo por clase y rompe varias reglas de buenas prácticas. Le recomiendo encarecidamente que no lo haga, pero solo enumérelo aquí porque fue el primer "borrador" cuando escribí esta respuesta (que también explica por qué los nombres son "Foo", etc.).
Opción # 2: la propiedad de nombre
Para deshacernos del problema en la opción # 1, necesitamos tener algún tipo de información de qué tipo es un nodo en el objeto JSON. El problema es que en Typecript, estas cosas son construcciones en tiempo de compilación y las necesitamos en tiempo de ejecución, pero los objetos de tiempo de ejecución simplemente no tienen conocimiento de sus propiedades hasta que se establecen.
Una forma de hacerlo es haciendo que las clases conozcan sus nombres. Sin embargo, también necesita esta propiedad en JSON. En realidad, solo lo necesitas en el json:
Opción n. ° 3: indicar explícitamente los tipos de miembros
Como se indicó anteriormente, la información de tipo de los miembros de la clase no está disponible en tiempo de ejecución, es decir, a menos que la hagamos disponible. Solo necesitamos hacer esto para los miembros no primitivos y estamos listos para comenzar:
Opción # 4: la forma detallada, pero ordenada
Actualización 01/03/2016: Como @GameAlchemist señaló en los comentarios ( idea , implementación ), a partir de Typecript 1.7, la solución que se describe a continuación se puede escribir de una mejor manera utilizando decoradores de clase / propiedad.
La serialización siempre es un problema y, en mi opinión, la mejor manera es la que no es la más corta. De todas las opciones, esto es lo que preferiría porque el autor de la clase tiene control total sobre el estado de los objetos deserializados. Si tuviera que adivinar, diría que todas las demás opciones, tarde o temprano, lo meterán en problemas (a menos que Javascript presente una forma nativa de lidiar con esto).
Realmente, el siguiente ejemplo no le hace justicia a la flexibilidad. Realmente solo copia la estructura de la clase. Sin embargo, la diferencia que debe tener en cuenta aquí es que la clase tiene el control total para usar cualquier tipo de JSON que quiera controlar el estado de toda la clase (puede calcular cosas, etc.).
fuente
equals
otoString
métodos (solo que generalmente los genera automáticamente). No debería ser demasiado difícil escribir un generadordeserialize
si quisieras, pero no puede ser una automatización en tiempo de ejecución.puede usar
Object.assign
No sé cuándo se agregó esto, actualmente estoy usando Typecript 2.0.2, y esta parece ser una característica de ES6.aqui esta
HalJson
esto es lo que Chrome dice que es
para que pueda ver que no hace la asignación de forma recursiva
fuente
Object.assign
. ¿Por qué entonces tenemos dos respuestas tipo léxico sobre esta?Object.assign
no funcionará de forma recursiva y no creará instancias de los tipos de objeto correctos, dejando los valores comoObject
instancias. Si bien está bien para tareas triviales, la serialización de tipo complejo no es posible con ella. Por ejemplo, si una propiedad de clase es de un tipo de clase personalizado,JSON.parse
+Object.assign
instanciará esa propiedad enObject
. Los efectos secundarios incluyen métodos faltantes y accesorios.Object.assign
, donde todavía se trata de escribir instancias anidadas por mano. Este enfoque está bien para objetos de nivel tutorial muy simples, pero no para uso real.TLDR: TypedJSON (prueba de concepto de trabajo)
La raíz de la complejidad de este problema es que necesitamos deserializar JSON en tiempo de ejecución utilizando información de tipo que solo existe en tiempo de compilación . Esto requiere que la información de tipo esté disponible de alguna manera en tiempo de ejecución.
Afortunadamente, esto se puede resolver de una manera muy elegante y robusta con decoradores y ReflectDecorators :
Información de tipo de grabación
Con una combinación de ReflectDecorators y decoradores de propiedades, la información de tipo puede registrarse fácilmente sobre una propiedad. Una implementación rudimentaria de este enfoque sería:
Para cualquier propiedad dada, el fragmento anterior agregará una referencia de la función constructora de la propiedad a la
__propertyTypes__
propiedad oculta en el prototipo de clase. Por ejemplo:Y eso es todo, tenemos la información de tipo requerida en tiempo de ejecución, que ahora se puede procesar.
Información de tipo de procesamiento
Primero necesitamos obtener una
Object
instancia usandoJSON.parse
- después de eso, podemos iterar sobre los entrantes en__propertyTypes__
(recopilados anteriormente) e instanciar las propiedades requeridas en consecuencia. Se debe especificar el tipo del objeto raíz, de modo que el deserializador tenga un punto de partida.Una vez más, una implementación muy simple de este enfoque sería:
La idea anterior tiene una gran ventaja de deserializar por tipos esperados (para valores complejos / de objeto), en lugar de lo que está presente en el JSON. Si
Person
se espera a, entonces sePerson
crea una instancia. Con algunas medidas de seguridad adicionales para tipos y matrices primitivos, este enfoque se puede hacer seguro, que resista cualquier JSON malicioso.Casos de borde
Sin embargo, si ahora está contento de que la solución sea así de simple, tengo algunas malas noticias: hay una gran cantidad de casos extremos que deben ser atendidos. Solo algunos de los cuales son:
Si no quiere jugar con todo esto (apuesto a que no), me gustaría recomendar una versión experimental funcional de una prueba de concepto utilizando este enfoque, TypedJSON , que creé para abordar este problema exacto, un problema que me enfrento a diario.
Debido a que los decoradores todavía se consideran experimentales, no recomendaría usarlo para producción, pero hasta ahora me sirvió mucho.
fuente
He estado usando a este tipo para hacer el trabajo: https://github.com/weichx/cerialize
Es muy simple pero poderoso. Soporta:
Ejemplo:
fuente
He creado una herramienta que genera interfaces TypeScript y un "mapa de tipos" en tiempo de ejecución para realizar la verificación de tipos en tiempo de ejecución con los resultados de
JSON.parse
: ts.quicktype.ioPor ejemplo, dado este JSON:
quicktype produce la siguiente interfaz TypeScript y el mapa de tipos:
Luego verificamos el resultado de
JSON.parse
contra el tipo de mapa:He omitido algo de código, pero puedes probar quicktype para los detalles.
fuente
Opción # 5: Uso de constructores de mecanografiado y jQuery.extend
Este parece ser el método más fácil de mantener: agregue un constructor que tome como parámetro la estructura json y extienda el objeto json. De esa manera, puede analizar una estructura json en todo el modelo de aplicación.
No es necesario crear interfaces o enumerar propiedades en el constructor.
En su devolución de llamada ajax donde recibe una empresa para calcular los salarios:
fuente
$.extend
vieneObject.assign
, lo que elimina esta dependencia de jQuery.Para objetos simples, me gusta este método:
Aprovechar la capacidad de definir propiedades en el constructor le permite ser conciso.
Esto le proporciona un objeto escrito (frente a todas las respuestas que usan Object.assign o alguna variante, que le proporciona un Objeto) y no requiere bibliotecas o decoradores externos.
fuente
La cuarta opción descrita anteriormente es una manera simple y agradable de hacerlo, que debe combinarse con la segunda opción en el caso de que tenga que manejar una jerarquía de clases como, por ejemplo, una lista de miembros que es una de las subclases de una superclase de miembro, por ejemplo, el director extiende al miembro o el estudiante extiende al miembro. En ese caso, debe dar el tipo de subclase en formato json
fuente
JQuery .extend hace esto por usted:
fuente
Lo mejor que encontré para este propósito es el transformador de clase. github.com/typestack/class-transformer
Así es como lo usas:
Alguna clase:
Si usa el decorador @Type, también se crearán propiedades anidadas.
fuente
Quizás no sea real, sino una solución simple:
¡trabaje para dependencias difíciles también!
fuente
baz
será de tipoObject
y no de tipo.Bar.
Funciona en este caso simple porqueBar
no tiene métodos (solo propiedades primitivas). SiBar
tuviera un método comoisEnabled()
, este enfoque fallaría ya que ese método no estaría en la cadena JSON serializada.Otra opción usando fábricas
https://github.com/MrAntix/ts-deserialize
usar así
fuente
Personalmente prefiero la opción # 3 de @Ingo Bürk. Y mejoré sus códigos para admitir una matriz de datos complejos y una matriz de datos primitivos.
fuente
puedes hacer lo siguiente
y
fuente
fuente