5

I'm using typescript for a project and need to serialize a collection to json, save it to a file and later deserialize that file into a similar collection. The collection looks something like:

elements: Array<tool>

and my tool interface looks like:

export interface tool {
    name: string;
    draw(context:any);
}

and a tool implementation would look like:

export class textTool implements tool {
    name: string;
    fontSize:number;
    fontType:string;
    draw(context:any){
        // draws the control...
    }
}

I have few implementations of tool interface: textTool, imageTool and rectangleTool. The problem I need to solve is that when I deserialize the file content into a collection of tool I get just a regular object and not an instance of textTool for example.

I'm using JSON.stringify(elements) to create a json and JSON.parse(jsonText) to deserialize.

I understand that the parser has no way to know which type it should create an instance of given the json text has no information about it. I thought of adding a field or something to identify which class instance I need and manually 'new' that class. Any options where I don't need to manually parse the json to collection of tool (with proper types)?

8
  • 1
    There are plenty of alternatives: You can save different types to different files, or to one file but with different "sections" each for each type. You can also wrap each of your objects like: { "type": "textTool", "object": { ... } } Commented Jun 29, 2016 at 13:28
  • @NitzanTomer I agree that it will probably work, I wanted to double check whether there was something else or I was missing something. Commented Jun 29, 2016 at 13:37
  • "Better" is not an objective criteria. There might be a way to make it more objective by being more specific in what you mean by "better", but as it is this question invites opinions rather than facts. Commented Jun 29, 2016 at 13:42
  • You are aware though that using JSON.stringify and JSON.parse won't allow you to include functions right? As your interface does include a draw function. Commented Jun 29, 2016 at 13:42
  • @MikeMcCaughan. Thanks, I changed the question to be more objective. Commented Jun 29, 2016 at 13:44

2 Answers 2

7

As you said you can add a field type, create a mapping between type-string to implementation-class and then conversion code will be pretty straight forward:

export interface tool {
    type: string;
    name: string;
    draw(context:any): void;
}

class textTool implements tool {
    type:string = 'textTool';
    name:string;
    fontSize:number;
    fontType:string;

    draw(context:any):void {
    }
}

const typeMapping:any = {
    'textTool' : textTool
    //all other types
};
let json = '[{"type":"textTool", "name": "someName", "fontSize": 11}]';
let elements: Array<tool> = JSON.parse(json).map((i:any) => {
    let target:any = new typeMapping[i.type];
    for (const key in i) {
        target[key] = i[key];
    }
    return target;
});

* Cloning code is very simplistic, but it is good enough for plain objects.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. Let me try this on my environment.
is this (2019) still the way to go?
2

for plain objects try it:

function deserialize<T>(json): T[] {
    let collection: T[] = [];
    json.map((item: T) => {
        collection.push(item);
    });
    return collection;
}

and call it:

let tools: tool[] = deserialize<textTool>(json);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.