create - JSON a la instancia de clase TypeScript?
recorrer json typescript (5)
¿Cuál es realmente la solución automatizada más robusta y elegante para deserializar las instancias de clase de tiempo de ejecución de JSON a TypeScript?
El uso de decoradores de propiedades con ReflectDecorators para registrar información de tipo accesible en tiempo de ejecución que se puede utilizar durante un proceso de deserialización proporciona un enfoque sorprendentemente limpio y ampliamente adaptable, que también encaja perfectamente en el código existente. También es totalmente automatizable y funciona también para objetos anidados.
Una implementación de esta idea es TypedJSON , que creé precisamente para esta tarea:
@JsonObject
class Foo {
@JsonMember
name: string;
getName(): string { return this.name };
}
var foo = TypedJSON.parse(''{"name": "John Doe"}'', Foo);
foo instanceof Foo; // true
foo.getName(); // "John Doe"
Esta pregunta ya tiene una respuesta aquí:
- ¿Cómo inicializo un objeto TypeScript con un objeto JSON 14 respuestas
He investigado bastante, pero no estoy totalmente satisfecho con lo que encontré. Solo para asegurarme de que esta es mi pregunta: ¿Cuál es en realidad la solución automatizada más robusta y elegante para deserializar las instancias de clase de tiempo de ejecución de JSON a TypeScript?
Digamos que tengo esta clase:
class Foo {
name: string;
GetName(): string { return this.name };
}
Y digamos que obtuve esta cadena JSON para la deserialización:
{"name": "John Doe"}
¿Cuál es la solución mejor y más fácil de mantener para obtener una instancia de una clase Foo con el nombre establecido en "John Doe" y el método GetName () para que funcione? Pregunto muy específicamente porque sé que es fácil deserializar a un objeto de datos puro. Me pregunto si es posible obtener una instancia de clase con métodos de trabajo, sin tener que hacer ningún análisis manual o copia de datos manual. Si no es posible una solución totalmente automatizada, ¿cuál es la siguiente mejor solución?
¿Por qué no podrías simplemente hacer algo como esto?
class Foo {
constructor(myObj){
Object.assign(this, myObj);
}
get name() { return this._name; }
set name(v) { this._name = v; }
}
let foo = new Foo({ name: "bat" });
foo.toJSON() //=> your json ...
Ahora puede usar
Object.assign(target, ...sources)
.
Siguiendo su ejemplo, podría usarlo así:
class Foo {
name: string;
getName(): string { return this.name };
}
let fooJson: string = ''{"name": "John Doe"}'';
let foo: Foo = Object.assign(new Foo(), JSON.parse(fooJson));
console.log(foo.getName()); //returns John Doe
Object.assign
es parte de
ECMAScript 2015
y actualmente está disponible en la mayoría de los navegadores modernos.
Esta pregunta es bastante amplia, así que voy a dar un par de soluciones.
Solución 1: Método auxiliar
Aquí hay un ejemplo del uso de un Método auxiliar que puede cambiar para satisfacer sus necesidades:
class SerializationHelper {
static toInstance<T>(obj: T, json: string) : T {
var jsonObj = JSON.parse(json);
if (typeof obj["fromJSON"] === "function") {
obj["fromJSON"](jsonObj);
}
else {
for (var propName in jsonObj) {
obj[propName] = jsonObj[propName]
}
}
return obj;
}
}
Luego usándolo:
var json = ''{"name": "John Doe"}'',
foo = SerializationHelper.toInstance(new Foo(), json);
foo.GetName() === "John Doe";
Deserialización avanzada
Esto también podría permitir una deserialización personalizada al agregar su propio método
fromJSON
a la clase (esto funciona bien con la forma en que
JSON.stringify
ya usa el método
toJSON
, como se mostrará):
interface IFooSerialized {
nameSomethingElse: string;
}
class Foo {
name: string;
GetName(): string { return this.name }
toJSON(): IFooSerialized {
return {
nameSomethingElse: this.name
};
}
fromJSON(obj: IFooSerialized) {
this.name = obj.nameSomethingElse;
}
}
Luego usándolo:
var foo1 = new Foo();
foo1.name = "John Doe";
var json = JSON.stringify(foo1);
json === ''{"nameSomethingElse":"John Doe"}'';
var foo2 = SerializationHelper.toInstance(new Foo(), json);
foo2.GetName() === "John Doe";
Solución 2: clase base
Otra forma de hacerlo es creando su propia clase base:
class Serializable {
fillFromJSON(json: string) {
var jsonObj = JSON.parse(json);
for (var propName in jsonObj) {
this[propName] = jsonObj[propName]
}
}
}
class Foo extends Serializable {
name: string;
GetName(): string { return this.name }
}
Luego usándolo:
var foo = new Foo();
foo.fillFromJSON(json);
Hay demasiadas formas diferentes de implementar una deserialización personalizada utilizando una clase base, así que lo dejaré como quieras.
La mejor solución que encontré al tratar con clases de Tipos de letra y objetos json: agregue un constructor en su clase de Tipos de letra que tome los datos de json como parámetro. En ese constructor, extiende su objeto json con jQuery, así: $ .extend (this, jsonData). $ .extend permite mantener los prototipos de JavaScript al tiempo que agrega las propiedades del objeto json.
export class Foo
{
Name: string;
getName(): string { return this.Name };
constructor( jsonFoo: any )
{
$.extend( this, jsonFoo);
}
}
En su devolución de llamada ajax, traduzca sus jsons en un objeto de mecanografía como este:
onNewFoo( jsonFoos : any[] )
{
let receviedFoos = $.map( jsonFoos, (json) => { return new Foo( json ); } );
// then call a method:
let firstFooName = receviedFoos[0].GetName();
}
Si no agrega el constructor, simplemente llame en su devolución de llamada ajax:
let newFoo = new Foo();
$.extend( newFoo, jsonData);
let name = newFoo.GetName()
... pero el constructor será útil si también desea convertir el objeto hijos json. Vea mi respuesta detallada here .