How do I initialize a TypeScript object with a JSON object

前端 未结 16 713
被撕碎了的回忆
被撕碎了的回忆 2020-11-22 08:30

I receive a JSON object from an AJAX call to a REST server. This object has property names that match my TypeScript class (this is a follow-on to this question).

Wha

相关标签:
16条回答
  • 2020-11-22 09:03

    the best I found for this purpose is the class-transformer. github.com/typestack/class-transformer

    That's how you use it:

    Some class:

    export class Foo {
    
        name: string;
    
        @Type(() => Bar)
        bar: Bar;
    
        public someFunction = (test: string): boolean => {
            ...
        }
    }
    
    
    import { plainToClass } from 'class-transformer';
    
    export class SomeService {
    
      anyFunction() {
    u = plainToClass(Foo, JSONobj);
     }
    

    If you use the @Type decorator nested properties will be created, too.

    0 讨论(0)
  • 2020-11-22 09:05

    Option #5: Using Typescript constructors and jQuery.extend

    This seems to be the most maintainable method: add a constructor that takes as parameter the json structure, and extend the json object. That way you can parse a json structure into the whole application model.

    There is no need to create interfaces, or listing properties in constructor.

    export class Company
    {
        Employees : Employee[];
    
        constructor( jsonData: any )
        {
            jQuery.extend( this, jsonData);
    
            // apply the same principle to linked objects:
            if ( jsonData.Employees )
                this.Employees = jQuery.map( jsonData.Employees , (emp) => {
                    return new Employee ( emp );  });
        }
    
        calculateSalaries() : void { .... }
    }
    
    export class Employee
    {
        name: string;
        salary: number;
        city: string;
    
        constructor( jsonData: any )
        {
            jQuery.extend( this, jsonData);
    
            // case where your object's property does not match the json's:
            this.city = jsonData.town;
        }
    }
    

    In your ajax callback where you receive a company to calculate salaries:

    onReceiveCompany( jsonCompany : any ) 
    {
       let newCompany = new Company( jsonCompany );
    
       // call the methods on your newCompany object ...
       newCompany.calculateSalaries()
    }
    
    0 讨论(0)
  • 2020-11-22 09:13

    you can use Object.assign I don't know when this was added, I'm currently using Typescript 2.0.2, and this appears to be an ES6 feature.

    client.fetch( '' ).then( response => {
            return response.json();
        } ).then( json => {
            let hal : HalJson = Object.assign( new HalJson(), json );
            log.debug( "json", hal );
    

    here's HalJson

    export class HalJson {
        _links: HalLinks;
    }
    
    export class HalLinks implements Links {
    }
    
    export interface Links {
        readonly [text: string]: Link;
    }
    
    export interface Link {
        readonly href: URL;
    }
    

    here's what chrome says it is

    HalJson {_links: Object}
    _links
    :
    Object
    public
    :
    Object
    href
    :
    "http://localhost:9000/v0/public
    

    so you can see it doesn't do the assign recursively

    0 讨论(0)
  • 2020-11-22 09:14

    The 4th option described above is a simple and nice way to do it, which has to be combined with the 2nd option in the case where you have to handle a class hierarchy like for instance a member list which is any of a occurences of subclasses of a Member super class, eg Director extends Member or Student extends Member. In that case you have to give the subclass type in the json format

    0 讨论(0)
  • 2020-11-22 09:15

    I've been using this guy to do the job: https://github.com/weichx/cerialize

    It's very simple yet powerful. It supports:

    • Serialization & deserialization of a whole tree of objects.
    • Persistent & transient properties on the same object.
    • Hooks to customize the (de)serialization logic.
    • It can (de)serialize into an existing instance (great for Angular) or generate new instances.
    • etc.

    Example:

    class Tree {
      @deserialize public species : string; 
      @deserializeAs(Leaf) public leafs : Array<Leaf>;  //arrays do not need extra specifications, just a type.
      @deserializeAs(Bark, 'barkType') public bark : Bark;  //using custom type and custom key name
      @deserializeIndexable(Leaf) public leafMap : {[idx : string] : Leaf}; //use an object as a map
    }
    
    class Leaf {
      @deserialize public color : string;
      @deserialize public blooming : boolean;
      @deserializeAs(Date) public bloomedAt : Date;
    }
    
    class Bark {
      @deserialize roughness : number;
    }
    
    var json = {
      species: 'Oak',
      barkType: { roughness: 1 },
      leafs: [ {color: 'red', blooming: false, bloomedAt: 'Mon Dec 07 2015 11:48:20 GMT-0500 (EST)' } ],
      leafMap: { type1: { some leaf data }, type2: { some leaf data } }
    }
    var tree: Tree = Deserialize(json, Tree);
    
    0 讨论(0)
  • 2020-11-22 09:15

    Another option using factories

    export class A {
    
        id: number;
    
        date: Date;
    
        bId: number;
        readonly b: B;
    }
    
    export class B {
    
        id: number;
    }
    
    export class AFactory {
    
        constructor(
            private readonly createB: BFactory
        ) { }
    
        create(data: any): A {
    
            const createB = this.createB.create;
    
            return Object.assign(new A(),
                data,
                {
                    get b(): B {
    
                        return createB({ id: data.bId });
                    },
                    date: new Date(data.date)
                });
        }
    }
    
    export class BFactory {
    
        create(data: any): B {
    
            return Object.assign(new B(), data);
        }
    }
    

    https://github.com/MrAntix/ts-deserialize

    use like this

    import { A, B, AFactory, BFactory } from "./deserialize";
    
    // create a factory, simplified by DI
    const aFactory = new AFactory(new BFactory());
    
    // get an anon js object like you'd get from the http call
    const data = { bId: 1, date: '2017-1-1' };
    
    // create a real model from the anon js object
    const a = aFactory.create(data);
    
    // confirm instances e.g. dates are Dates 
    console.log('a.date is instanceof Date', a.date instanceof Date);
    console.log('a.b is instanceof B', a.b instanceof B);
    
    1. keeps your classes simple
    2. injection available to the factories for flexibility
    0 讨论(0)
提交回复
热议问题