All Projects → danielearwicker → Json Mobx

danielearwicker / Json Mobx

Licence: mit
Simple undo/redo and persistence for MobX

Programming Languages

typescript
32286 projects

Projects that are alternatives of or similar to Json Mobx

Flatcc
FlatBuffers Compiler and Library in C for C
Stars: ✭ 434 (+456.41%)
Mutual labels:  json, serialization
Fastjson
A fast JSON parser/generator for Java.
Stars: ✭ 23,997 (+30665.38%)
Mutual labels:  json, serialization
Airframe
Essential Building Blocks for Scala
Stars: ✭ 442 (+466.67%)
Mutual labels:  json, serialization
Leopotamgrouplibraryunity
Tools library for unity 3d game engine: animator graph helpers, serialization (json), localization, event routing (eventbus, ui actions), embedded scripting, uGui xml markup, threading, tweening, in-memory protection and other helpers (pure C#)
Stars: ✭ 373 (+378.21%)
Mutual labels:  json, serialization
Tech1 Benchmarks
Java JMH Benchmarks repository. No Longer Supported.
Stars: ✭ 50 (-35.9%)
Mutual labels:  json, serialization
Handyjson
A handy swift json-object serialization/deserialization library
Stars: ✭ 3,913 (+4916.67%)
Mutual labels:  json, serialization
Groot
From JSON to Core Data and back.
Stars: ✭ 533 (+583.33%)
Mutual labels:  json, serialization
Fspickler
A fast multi-format message serializer for .NET
Stars: ✭ 299 (+283.33%)
Mutual labels:  json, serialization
Eminim
JSON serialization framework for Nim, works from a Stream directly to any type and back. Depends only on stdlib.
Stars: ✭ 32 (-58.97%)
Mutual labels:  json, serialization
Play Json Extra
playframework2 json extra module. provide convenience functions for define Format, Reads, Writes
Stars: ✭ 20 (-74.36%)
Mutual labels:  json, serialization
Velocypack
A fast and compact format for serialization and storage
Stars: ✭ 347 (+344.87%)
Mutual labels:  json, serialization
Simplify Core
Simplify 为简化重复的JAVA代码而生,基于JDK8,无其它jar包依赖,提供序列化,json parse/generator,日期处理,asm && jdkproxy 实现动态代理功能 等常见操作。
Stars: ✭ 65 (-16.67%)
Mutual labels:  json, serialization
Kim
Kim: A JSON Serialization and Marshaling framework
Stars: ✭ 326 (+317.95%)
Mutual labels:  json, serialization
Jsoniter Scala
Scala macros for compile-time generation of safe and ultra-fast JSON codecs
Stars: ✭ 410 (+425.64%)
Mutual labels:  json, serialization
Bebop
An extremely simple, fast, efficient, cross-platform serialization format
Stars: ✭ 305 (+291.03%)
Mutual labels:  json, serialization
Iguana
universal serialization engine
Stars: ✭ 481 (+516.67%)
Mutual labels:  json, serialization
Shineframe
高性能超轻量级C++开发库及服务器编程框架
Stars: ✭ 274 (+251.28%)
Mutual labels:  json, serialization
Kotlinx.serialization
Kotlin multiplatform / multi-format serialization
Stars: ✭ 3,550 (+4451.28%)
Mutual labels:  json, serialization
Jackson Module Kotlin
Module that adds support for serialization/deserialization of Kotlin (http://kotlinlang.org) classes and data classes.
Stars: ✭ 830 (+964.1%)
Mutual labels:  json, serialization
Fhir.js
Node.JS library for serializing/deserializing FHIR resources between JS/JSON and XML using various node.js XML libraries
Stars: ✭ 61 (-21.79%)
Mutual labels:  json, serialization

json-mobx

Simple undo/redo and persistence for MobX

Build Status Coverage Status

npm install --save json-mobx

Based on a single trivial concept: an object must have a mutable property called json that holds its JSON representation (and by JSON we mean a plain object tree that can be round-tripped via JSON).

As the json property is mutable, it means you can restore the object to a prior state by assigning to its json property. This is in contrast to most serialization systems which deserialize by creating a brand new tree of objects. Here we tend towards reconciling or minimally updating an existing tree to bring it into line with the provided JSON.

This is particularly suited to situations where an object is not pure data but is also dependent on (or depended on by) the "environment". This is closely related to the way React components can use componentDidMount and componentWillUnmount to wire themselves into environmental dependencies. Or to put it another way, objects have a life-cycle.

It also means that, thanks to MobX, implementing Undo/Redo is very easy. Prior states can be captured efficiently, and can be "loaded into" a live object hierarchy with minimal impact on unchanged objects.

Example demonstrating Undo/Redo

Long read on the philosophy, inspiration, relationship to other projects

In these notes we will use the term live object to refer to objects that have a json computed property.

The @json decorator

For typical live objects it is a pain to write the json computed property by hand. So we provide a json decorator:

export class Widget  {

    @json @observable left = 0;
    @json @observable top = 0;
    @json @observable width = 1;
    @json @observable height = 1;

    @json @observable title = "New widget";
}

This class automagically gains a hidden json property. It is defined by a MobX computed so it only regenerates the JSON representation if anything changes.

Saving and Loading

There are also helper functions json.save and json.load. These select the correct way to serialize based on the kind of object. Note that json.load does not return a new de-serialized object. Rather, it updates the object passed to it.

So:

const w = new Widget();

const j = json.save(w);

const w2 = new Widget();
json.load(w2, j);

If you use the @json decorator on a property that refers to an object with its own json implementation, that implementation will be used to persist that object, so ultimately you have control over what is saved/loaded and what side-effects this can have (this is important when objects may be "wired up" to external dependencies).

This means that stateful objects form a tree in which each object has a single owner. Where a @json property refers to another live object, consider marking it readonly; instead of allocating a new object, you will be reconfiguring the existing one. (This is not an absolute rule, but usually makes sense.)

Arrays

There is built-in support for arrays. The save and load functions descend recursively into the items of arrays. Arrays of plain objects or primitives are not treated any differently to primitives.

More interestingly, you can construct an array property using json.arrayOf(SomeClass) instead of plain []:

class FancyItem {
    @json firstName = "Homer";
    @json lastName = "Simpson";
}

class HasFancyArray {
    @json readonly fancyArray = json.arrayOf(FancyItem);

    // instead of:
    // @json fancyArray = [];
}

Again, note the use of readonly on the fancyArray property. This is a good idea because if you accidentally assigned a new (ordinary) array to fancyArray it would lose the ability to perform reconciliation.

Behind the scenes, the items of the array will be stamped with unique IDs, which are later used for reconciliation. When a prior state of HasFancyArray is restored by json.load, it will match up the data with the right objects by ID. It will also use the FancyItem constructor to default-construct any additional objects specified in the saved state, so it can load into them.

The type parameter of json.arrayOf is constrained so it must be a new-able class constructor that takes no arguments. If it has a method dispose, that must require no arguments (it will be automatically called whenever json.load discards an existing item from the array).

This reconciliation process is closely analogous to React's treatement of the virtual DOM. The optional dispose method works like componentWillUnmount, providing objects with an opportunity to detach themselves from any environmental dependencies before they are abandoned.

Also the auto-generated ID stamped onto each array item plays a similar role to React's key prop. The major difference is that you don't need to specify the ID manually.

If you want to use the ID as a React key, you can get it with json.idOf(item). But note that it will return the value 0 until its containing array has been saved. (If you attach an Undo system to your root object, this will happen automatically).

Polymorph

As you can create a class with a json computed property to define a custom serialization technique, it is easy to extend this library. One very common requirement is to refer to an object whose precise type may vary. This is supported by the built-in Polymorph class, though in reality it is just a (very simple) example of a class with a custom json computed property.

A polymorph is a container that holds exactly one object, of a type that may change. It doesn't just refer to the object; it owns it, so it can optionally dispose it when necessary.

To construct it, you specify the string that names the initial type and a factory function that constructs an instance of the type (the constructor immediately calls it to get the initial instance).

constructor(type: string, private factory: (type: string) => T) ...

It has a readonly property target which is the current owned instance (note: this property's value may change. It is readonly, not const!)

It has get and set methods that operate on the object type. So p.set("MisterTickle") will assign a new instance of MisterTickle (that is, whatever is returned when the factory is called with "MisterTickle"). If you're familiar with bidi-mobx you'll have noticed that this makes it a BoxedValue holding the type of the object, so it can be bound to a SelectString.

It implements the json property so that the format is

{
    type: string,
    settings: { ... }
}

The settings part depends on the type: Polymorph simply uses json.load and json.save to take care of it.

When the type changes, the previous instance has its dispose method called, if any. Also Polymorph itself implements dispose by calling on to the current instance's dispose, if any.

Undo

When you construct an Undo object you pass it the root live object and it immediately captures the current state. It does this inside autorun, so if the state changes it will be recaptured. The second time this happens, the previous state is pushed onto the undo stack. Undo has public properties canUndo and canRedo, and methods undo and redo, so you can link those up to a couple of toolbar buttons in an editor.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].