Question: Question:
I am writing a Web application that communicates with a server using JSON in Typescript. When serializing / deserializing to JSON, the method is not included in JSON, so I thought about creating an object using the deserialized data, but I don't know how to do it.
Right now I'm writing the following code.
class Pen{
constructor(public color: String,public size:number) { }
draw(): void {
//do something
}
}
var json = JSON.stringify(new Pen("rgb(0,0,0)",10));
var pen_data = <Pen> JSON.parse(json);
//pen_data.draw(); //TypeError: pen_data.draw is not a function
//pen_dataからPenのオブジェクトを作りたい
var new_pen = new Pen(pen_data.color, pen_data.size);
However, this code needs to be rewritten one by one as the Pen properties increase, and if you want to do the same with other classes, you have to write similar code.
How can I write this code generically?
Answer: Answer:
I don't think there is a direct way. Whereas object-oriented "objects" deal with data and operations as a group, serialization saves or restores only the "data" part of an object, and in principle object-oriented "objects". This is because "objects" are incompatible with data persistence.
There are several possible solutions.
1. How to be patient and restore the properties according to the contents of each object
This is the sample code method presented by the questioner. Treat the Pen object and the corresponding JSON-formatted data completely differently. You have to call the constructor each time you deserialize, and modify the deserialization part each time the property increases or decreases. Anyway, it is troublesome and troublesome, and it is too redundant as the data structure is duplicated due to the existence of the Pen object and the corresponding JSON data, but it is a "right" method in a sense in object-oriented programming.
2. How to separate data and operations
Give up on the class and define draw
as a separate function rather than a method of the Pen class. The Pen
is not a class, but an interface, and the entity of the pen is just an object with no methods.
function draw(pen: Pen){
....
}
interface Pen {
color: String;
size: Number;
}
var blackPen: Pen = { color: "rgb(0,0,0)", size: 10 };
draw(blackPen);
var json = JSON.stringify(blackPen);
var pen = <Pen> JSON.parse(json);
draw(pen);
I think it's a simple and straightforward method, and it has the advantage that even if the data structure becomes more complicated, it can be serialized / deserialized with a single stringify/parse
. for example,
interface PaintTool {
penList: [Pen];
canvasWidth: Number;
canvasHeight: Number;
filePath: String;
}
Even for larger data structures that have Pen
inside, such as, if the object does not have a method inside, the entire data structure can be serialized / deserialized in one shot. However, it has the disadvantage that you can hardly use the features that are typical of object-oriented programming such as classes and prototypes. Not available when Pen
has subclasses and draw
is overridden.
3. How to use the library for serialization
There is a way to use a library like resurrect-js . There are some restrictions, not every object can be serialized, and I haven't checked if it can be used for a TypeScript "class", but it's probably close to what the questioner envisions. I think it's the way.
According to the Readme, resurrect-js seems to be able to write:
function Foo() {}
Foo.prototype.greet = function() { return "hello"; };
// Behavior is preserved:
var necromancer = new Resurrect();
var json = necromancer.stringify(new Foo());
var foo = necromancer.resurrect(json);
foo.greet(); // => "hello"
I think it's intuitive to use, such as calling Resurrect#stringify
method instead of JSON.strongify
and Resurrect#resurrect
instead of JSON.parse
. The method also seems to be conditionally serializable. It depends on the application, but it seems worth considering.
4. How to forcibly restore by metaprogramming
JavaScript provides a very flexible metaprogramming method that allows you to mess with __proto__ directly, or Object.keys
__proto__
find out the name of a property held by an object. You can check the constructor with the constructor
property, or you can set some conventions to store the object's prototype in JSON data in the form of a string, and when deserializing, dynamically search for and call that constructor, and the prototype object. I don't think it's impossible to set and restore by setting properties (reference: dynamic object construction in javascript? ).
In Java etc., there is a function to perform such serialization by making full use of reflection. To make an object serializable in Java, you need to define a no-argument constructor because it is dynamically deserialized in that way. That's the way to do it with resurrect-js mentioned in 3. However, such a method may not always be usable for your own purposes. For example, the questioner uses TypeScript, so resurrect-js made for JavaScript may not always be diverted. If you can't use a library like resurrect-js, you can implement a similar mechanism on your own for your own purposes.
If we implement such a mechanism independently by making full use of metaprogramming, it would be possible to realize automatic serialization / deserialization according to the increase or decrease of properties, but that is the case. Metaprogramming is complicated, buggy, and not easy to handle. TypeScript static typing also doesn't help. It's never easy, so you should think of metaprogramming as a last resort.
I would choose 2. If you're not obsessed with object-oriented programming, I think it's the simplest and most straightforward method. I don't think it's inconvenient to use object-oriented classes and prototypes in this situation. I think this serialization problem is a good example of the shortcomings of object-oriented programming that puts data and operations together. The cause of the problem was trying to persist and restore persistent data such as color
and size
and non-persistent operation called draw
method as an object called Pen
without separating them. Yes, if you separate the data and the operation, the problem will be solved naturally.