Only this pageAll pages
Powered by GitBook
1 of 18

Fractale

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Examples

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Plugin

Fractale add other custom types which need other optional modules

Color

Need npm i -S teinte

const Colored = Fractale.create('Colored', {
    color: Fractale.Color
});

const colored = new Colored({ color: '#0f0f0f' });
console.log(colored.hsl());

Moment

If moment module is installed Date will be automatically transform to moment instance

Need npm i -S moment

const Article = Fractale.create('Article', {
    content: String,
    created_at: Date,
    updated_at: Date,
});

const article = new Article({
    content: 'This module is awesome',
    created_at: '2000-01-01',
    updated_at: '2000-01-01',
});
console.log(article.created_at.format('DD/MM/YYYY'));

Provider

Fractale lets you possibility to save and retrieve values in a storage

// To use Web localStorage
Fractale.memory.setProvider('local');

// To use Web sessionStorage
Fractale.memory.setProvider('session');

// To use Web cookie
Fractale.memory.setProvider('cookie', { expires: 6048e5 });

// To use Web IndexedDB
Fractale.memory.setProvider('idb', { database: 'ƒ_database' });

Introduction

Inspired from Mongoose, it allows you to modeling your data to be explicit on data format.

It will test value of each fields and link model between them.

Installation

npm install --save fractale

Documentation

You can found more documentation and examples here or wiki.

Dependencies

  • Usage of uuid to generate a unique id for model instances.

Optional :

  • Usage of moment for date.

  • Usage of teinte for color.

  • Usage of mongoose for bridge / provider.

  • Usage of prop-types for bridge.

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. Please make sure to update tests as appropriate.

See the contributing and code of conduct files for details

License

This project is licensed under the MIT License - see the LICENSE file for details

Authors

  • Jocelyn Faihy - Web developer and Blob of Internets - Jochlain

See also the list of contributors who participated in this project.

Thanks

Thanks BP

Simple model

Models

const Simple = Fractale.create("Simple", {
    mixed: undefined,
    boolean: Boolean,
    number: Number,
    bigint: BigInt,
    string: String,
    color: Color,
    date: Date,
    regexp: RegExp,
    buffer: ArrayBuffer,
    int8: Int8Array,
    uint8: Uint8Array,
    "uint8_clamped": Uint8ClampedArray,
    int16: Int16Array,
    uint16: Uint16Array,
    int32: Int32Array,
    uint32: Uint32Array,
    float32: Float32Array,
    float64: Float64Array,
    bigint64: BigInt64Array,
    biguint64: BigUint64Array
});

Run

const { Simple } = module.exports.get();

const instance = new Simple({
    mixed: 'It\'s dangerous to go alone! Take this.',
    buffer: 'RnJhY3RhbGUgYnVmZmVyIHNhdmU=',
    int8: 'RnJhY3RhbGUgSW50OEFycmF5IHNhdmU=',
    uint8: 'RnJhY3RhbGUgVWludDhBcnJheSBzYXZl',
    uint8_clamped: 'RnJhY3RhbGUgVWludDhDbGFtcGVkQXJyYXkgc2F2ZQ==',
    int16: 'RnJhY3RhbGUgSW50MTZBcnJheSBzYXZl',
    uint16: 'RnJhY3RhbGUgVWludDE2QXJyYXkgdW5pdCB0ZXN0',
    int32: 'RnJhY3RhbGUgSW50MzJBcnJheSB0ZXN0',
    uint32: 'RnJhY3RhbGUgVUludDMyQXJyYXkgdGVzdGluZw==',
    float32: 'RnJhY3RhbGUgRmxvYXQzMkFycmF5IHVuaXRzIHRlc3Q=',
    float64: 'RnJhY3RhbGUgRmxvYXQ2NEFycmF5IHVuaXRzIHRlc3Q=',
    bigint64: 'RnJhY3RhbGUgQmlnSW50NjRBcnJheSB1bml0IHRlc3Q=',
    biguint64: 'RnJhY3RhbGUgQmlnVUludDY0QXJyYXkgdGVzdGluZ3M=',
});

_.test(instance.mixed, 'It\'s dangerous to go alone! Take this.', 'Error on simple accessor with type mixed');
_.test(instance.boolean, false, 'Error on simple accessor with type boolean');
_.test(instance.number, 31, 'Error on simple accessor with type number');
_.test(instance.bigint, 31n, 'Error on simple accessor with type bigint');
_.test(instance.string, 'Lorem ipsum', 'Error on simple accessor with type string');
_.test(instance.color.hex(), '#AA0000', 'Error on simple accessor with type color');
_.test(instance.date.format('DD/MM/YYYY'), '01/01/2000', 'Error on simple accessor with type date');
_.test(instance.regexp.toString(), /toto/g.toString(), 'Error on simple accessor with type bigint');
_.test(base64.encode(instance.buffer), 'RnJhY3RhbGUgYnVmZmVyIHNhdmU=', 'Error on simple accessor with type buffer');
_.test(base64.encode(instance.int8.buffer), 'RnJhY3RhbGUgSW50OEFycmF5IHNhdmU=', 'Error on simple accessor with type int8');
_.test(base64.encode(instance.uint8.buffer), 'RnJhY3RhbGUgVWludDhBcnJheSBzYXZl', 'Error on simple accessor with type uint8');
_.test(base64.encode(instance.uint8_clamped.buffer), 'RnJhY3RhbGUgVWludDhDbGFtcGVkQXJyYXkgc2F2ZQ==', 'Error on simple accessor with type uint8_clamped');
_.test(base64.encode(instance.int16.buffer), 'RnJhY3RhbGUgSW50MTZBcnJheSBzYXZl', 'Error on simple accessor with type int16');
_.test(base64.encode(instance.uint16.buffer), 'RnJhY3RhbGUgVWludDE2QXJyYXkgdW5pdCB0ZXN0', 'Error on simple accessor with type uint16');
_.test(base64.encode(instance.int32.buffer), 'RnJhY3RhbGUgSW50MzJBcnJheSB0ZXN0', 'Error on simple accessor with type int32');
_.test(base64.encode(instance.uint32.buffer), 'RnJhY3RhbGUgVUludDMyQXJyYXkgdGVzdGluZw==', 'Error on simple accessor with type uint32');
_.test(base64.encode(instance.float32.buffer), 'RnJhY3RhbGUgRmxvYXQzMkFycmF5IHVuaXRzIHRlc3Q=', 'Error on simple accessor with type float32');
_.test(base64.encode(instance.float64.buffer), 'RnJhY3RhbGUgRmxvYXQ2NEFycmF5IHVuaXRzIHRlc3Q=', 'Error on simple accessor with type float64');
_.test(base64.encode(instance.bigint64.buffer), 'RnJhY3RhbGUgQmlnSW50NjRBcnJheSB1bml0IHRlc3Q=', 'Error on simple accessor with type bigint64');
_.test(base64.encode(instance.biguint64.buffer), 'RnJhY3RhbGUgQmlnVUludDY0QXJyYXkgdGVzdGluZ3M=', 'Error on simple accessor with type biguint64');

instance.mixed = -1;
instance.boolean = true;
instance.number = 42;
instance.bigint = 42n;
instance.string = 'Dolor sit amet';
instance.color = 'rgba(0, 0, 255, 0)';

_.test(instance.mixed, -1, 'Error on simple accessor with type mixed after change');
_.test(instance.boolean, true, 'Error on simple accessor with type boolean after change');
_.test(instance.number, 42, 'Error on simple accessor with type number after change');
_.test(instance.bigint, 42n, 'Error on simple accessor with type number after change');
_.test(instance.string, 'Dolor sit amet', 'Error on simple accessor with type string after change');
_.test(instance.color.hex(), '#0000FF00', 'Error on simple accessor with type string after change');

resolve(instance);

Results

{
    "uuid": "764a4649-6de0-4ae3-ab0c-7009cc16fcc8",
    "mixed": -1,
    "boolean": true,
    "number": 42,
    "bigint": "42",
    "string": "Dolor sit amet",
    "color": "#0000FF00",
    "date": "2000-01-01T02:00:00.000Z",
    "regexp": "/toto/g",
    "buffer": "RnJhY3RhbGUgYnVmZmVyIHNhdmU=",
    "int8": "RnJhY3RhbGUgSW50OEFycmF5IHNhdmU=",
    "uint8": "RnJhY3RhbGUgVWludDhBcnJheSBzYXZl",
    "uint8_clamped": "RnJhY3RhbGUgVWludDhDbGFtcGVkQXJyYXkgc2F2ZQ==",
    "int16": "RnJhY3RhbGUgSW50MTZBcnJheSBzYXZl",
    "uint16": "RnJhY3RhbGUgVWludDE2QXJyYXkgdW5pdCB0ZXN0",
    "int32": "RnJhY3RhbGUgSW50MzJBcnJheSB0ZXN0",
    "uint32": "RnJhY3RhbGUgVUludDMyQXJyYXkgdGVzdGluZw==",
    "float32": "RnJhY3RhbGUgRmxvYXQzMkFycmF5IHVuaXRzIHRlc3Q=",
    "float64": "RnJhY3RhbGUgRmxvYXQ2NEFycmF5IHVuaXRzIHRlc3Q=",
    "bigint64": "RnJhY3RhbGUgQmlnSW50NjRBcnJheSB1bml0IHRlc3Q=",
    "biguint64": "RnJhY3RhbGUgQmlnVUludDY0QXJyYXkgdGVzdGluZ3M="
}

Usage

Declaration

Instanciation

Modification

Array helpers

'use strict';

const Fractale = require('fractale');

const KeyValuePair = Fractale.create(
    'KeyValuePair', /* Name of your model (required) */
    { /* Model's schema */
        key: String,
        value: undefined,
    }
);

/* More complete example */
const Model = Fractale.create(
    'Model',
    {
        mixed: undefined,
        boolean: Boolean,
        number: Number,
        string: String,
        date: Date,
        boards: [String],
        metadata: { key: String },
        inception: KeyValuePair,
        collection: [{ key: String, value: null }],
        self: Fractale.SELF, // the Model itself
    }
);

/* Full example */
const Full = Fractale.create(
    'Full',
    Model, // Full inherit Model
    {
        declareAfter: Fractale.from('After'),
        withOption: Fractale.with(String, { validator: { in: ['Foo','Bar'] } }),
    }
);

const After = Fractale.create('After', {
    start: Fractale.with(Date, { required: true, default: '2000-01-01' }),
    end: Date,
});
const instance = new Model({
    mixed: 'Great !',
    boolean: true,
    number: 42,
    string: 'Hello world',
    boards: ['Lorem ipsum', 'dolores sit amet'],
    metadata: { key: 'AZERTYUIOP' },
    inception: { key: 'key', value: 1 },
    collections: [
        { key: 'foo', value: 123 },
        { key: 'bar', value: 456 }
    ]
});

console.log(instance.serialize());
/* 
> { 
    mixed: 'Great !',
    boolean: true,
    number: 42,
    string: 'Hello world',
    boards: ['Lorem ipsum', 'dolores sit amet'],
    metadata: { key: 'AZERTYUIOP' },
    inception: { key: 'key', value: 1 },
    collections: [
        { key: 'foo', value: 123 },
        { key: 'bar', value: 456 }
    ]
}
*/

/* Copy props to another instance */
// Method 1
const full = Full.from(instance);

// Method 2
const serialized = instance.serialize();
delete serialized.uuid;
const clone = new Full(serialized);
console.log(instance.mixed); // > true
instance.mixed = 123;
console.log(instance.mixed); // > 123

console.log(instance.boolean); // > true
instance.boolean = false;
console.log(instance.boolean); // > false

console.log(instance.number); // > 42
instance.number = 12;
console.log(instance.number); // > 12

console.log(instance.string); // > 'Hello world'
instance.string = 'Lorem ipsum';
console.log(instance.string); // > 'Lorem ipsum'

console.log(instance.boards[0]); // > 'Lorem ipsum'
instance.boards[0] = 'Hello world';
console.log(instance.boards[0]); // > 'Hello World'

console.log(instance.metadata.key); // > 'AZERTYUIOP' 
instance.metadata.key = 'foo';
console.log(instance.metadata.key); // > 'foo' 

console.log(instance.inception); // > KeyValuePair { key: 'key', value: 1 }
instance.inception = new KeyValuePair({ key: 'new_key', value: 'new_value' });
console.log(instance.inception); // > KeyValuePair { key: 'new_key', value: 'new_value' }

console.log(instance.inception); // > KeyValuePair { key: 'new_key', value: 'new_value' }
instance.inception.value = 'updated_value';
console.log(instance.inception); // > KeyValuePair { key: 'new_key', value: 'updated_value' }

console.log(instance.collections[0].key); // > 'foo'
instance.collections[0].key = 'pass';
instance.collections[0] = { value: 789 };
console.log(instance.collections[0].key); // > 'pass'
console.log(instance.collections[0].value); // > 789

console.log(instance.serialize());
/* 
> { 
    mixed: 123,
    boolean: false,
    number: 12,
    string: 'Lorem ipsum',
    boards: ['Hello world', 'dolores sit amet'],
    metadata: { key: 'foo' },
    collections: [
        { key: 'pass', value: 789 },
        { key: 'bar', value: 456 }
    ],
    inception: { key: 'new_key', value: 'updated_value' }
}
*/
/* Array methods use */
instance.collections.push({ key: 'azertyuiop', value: 2 });
instance.collections = instance.collections.concat([{ key: 'new_key', value: 3 }, { key: 'N3W_K3Y', value: 4 }]);
console.log(instance.serialize().collections);
/*
> [
    { key: 'pass', value: 789 },
    { key: 'bar', value: 456 },
    { key: 'qwerty', value: 1 },
    { key: 'new_key', value: 3 },
    { key: 'N3W_K3Y', value: 4 }
]
*/

Bridge

/! Experimental

Fractale lets you possibility to transform models to other types

Transform to Mongoose Model

Need npm i -S mongoose

const model = Model.toMongoose();

Transform to PropTypes

Need npm i -S prop-types

const propTypes = Model.toPropTypes();

Philosophy

Performance

From a JSON of 10.45M:

  • Deserialization: ~0.895s => 11.68Mo/s

  • Read: ~0.001s

  • Serialization: ~0.304s => 34.37Mo/s

From a JSON of 339.06M:

  • Deserialization: ~3.301s => 102.71Mo/s

  • Read: ~0.001s

  • Serialization: ~1.173s => 289.05Mo/s

From a JSON of 1.27G:

  • Deserialization: ~12.942s => 98.22Mo/s

  • Read: ~0s

  • Serialization: ~6.089s => 208.77Mo/s

Metadata model

Models

Run

Results

Inception model

Models

Run

Results

const Metadata = Fractale.create("Metadata", {
    metadata: {
        key: String,
        data: {
            key: String,
            value: undefined
        }
    }
});
const { Metadata } = module.exports.get();
const instance = new Metadata({
    metadata: { key: 'Foo', data: { key: 'Bar', value: 12 } }
});

_.test(instance.metadata.key, 'Foo', 'Error on metadata accessor');

const metadata = instance.metadata;
metadata.key = 'decomposition';

_.test(instance.metadata.key, 'decomposition', 'Error on metadata accessor with decomposition');

instance.metadata.key = 'dot';
_.test(instance.metadata.key, 'dot', 'Error on metadata accessor with dot');

instance.metadata = { key: 'assign' };
_.test(instance.metadata.key, 'assign', 'Error on metadata accessor with assign');
_.test(instance.metadata.data.key, 'Bar', 'Error on metadata accessor with assign');

instance.metadata = { data: { key: 'after', value: 13 } };
_.test(instance.metadata.key, 'assign', 'Error on metadata accessor with bracket');
_.test(instance.metadata.data.key, 'after', 'Error on metadata accessor with bracket');
_.test(instance.metadata.data.value, 13, 'Error on metadata accessor with bracket');

resolve(instance);
{
    "uuid": "6c63fcf6-323a-4cb7-8bd1-afdb4d73c086",
    "metadata": {
        "uuid": "daf1d856-b57c-41a7-9a5e-407188f5f24f",
        "key": "assign",
        "data": {
            "uuid": "c22f5b47-3434-45cb-86e7-fba5134347d5",
            "key": "after",
            "value": 13
        }
    }
}
const Inception_Parent = Fractale.create("Inception_Parent", {
    value: String
});

const Inception_Child = Fractale.create("Inception_Child", {
    parent: Inception_Parent,
    value: String
});
const { Inception_Child } = module.exports.get();
const child = new Inception_Child({
    parent: { value: 'foo' },
    value: 'bar',
});

_.test(child.parent.value, 'foo', 'Error on inception setter with dot');

const parent = child.parent;
const value = 'hello world';
parent.value = value;
_.test(child.parent.value, value, 'Error on inception setter with dot');

child.parent.value = 'foo';
_.test(child.parent.value, 'foo', 'Error on deep setter with dot');

resolve(child);
{
    "uuid": "d2dd4dd1-f369-4ed8-821e-5040deed075b",
    "parent": {
        "uuid": "33ba3961-cc86-4dbc-9945-b0691bd36d93",
        "value": "foo"
    },
    "value": "bar"
}

Collection model

Models

const Collection_Item = Fractale.create("Collection_Item", {
    value: String
});

const Collection_Category = Fractale.create("Collection_Category", {
    items: [
        Collection_Item
    ]
});

Run

const { Collection_Category, Collection_Item } = module.exports.get();
const item = new Collection_Item({ value: 'foo' });
const category = new Collection_Category({ items: [item, { value: 'bar' }] });

_.test(category.items[0].value, 'foo', 'Error on collection accessor with brace');
_.test(category.items[1].value, 'bar', 'Error on collection accessor with brace');

let changed = false;
category.addEventListener('change', () => changed = true);
category.items[0].value = 'hello';

if (!changed) {
    throw new Error('Error on collection change event');
}
_.test(category.items[0].value, 'hello', 'Error on collection accessor with brace after change');
_.test(category.items.first.value, 'hello', 'Error on collection method first accessor');

category.items.push({ value: 'world' });

_.test(category.items.last.value, 'world', 'Error on collection method last accessor');
_.test(category.items.map(item => item.value).join(', '), 'hello, bar, world', 'Error on array method map accessor');
_.test(category.items.filter(item => item.value === 'hello').map(item => item.value).join(', '), 'hello', 'Error on array method filter accessor');
_.test(category.items.reduce((accu, item) => `${accu} ${item.value}`, '').trim(), 'hello bar world', 'Error on array method reduce accessor');

category.items.remove(item);
if (category.items.length !== 2) throw new Error('Error on array method remove accessor');

resolve(category);

Results

{
    "uuid": "269189c4-9f16-47db-bfe0-094ed8a2a676",
    "items": [
        {
            "uuid": "6edf2663-1fb9-4b76-8c4d-724f5ef70da1",
            "value": "bar"
        },
        {
            "uuid": "522ec9e9-2920-415d-a239-79fb620227fa",
            "value": "world"
        }
    ]
}

RegExp key model

Models

const ModelWithRegExpAsKey = Fractale.create("ModelWithRegExpAsKey", {
    "/item_\\d+/i": String
});

Run

const { ModelWithRegExpAsKey } = module.exports.get();
const instance = new ModelWithRegExpAsKey({
    item_1: 'Foo',
    item_2: 'Bar',
});

_.test(instance.item_1, 'Foo', 'Error on regexp key accessor with type string');
_.test(instance.item_2, 'Bar', 'Error on regexp key accessor with type string');

resolve(instance);

Results

{
    "uuid": "1e35cacd-193c-4cff-bbbb-d687f6023426",
    "item_1": "Foo",
    "item_2": "Bar"
}

Inheritance model

Models

const Inheritance_Child = Fractale.create("Inheritance_Child", {
    value: String
});

const Inheritance_Parent = Fractale.create("Inheritance_Parent", Inheritance_Child, {
    children: [
        Inheritance_Child
    ]
});

Run

const { Inheritance_Parent } = module.exports.get();
const instance = new Inheritance_Parent({
    value: 'Hello',
    children: [
        { value: 'world' },
        { value: 'you' },
    ]
});

_.test(instance.sayHelloTo(0), 'Hello world', `Expected "Hello world" got "${instance.sayHelloTo(0)}"`);
_.test(instance.sayHelloTo(1), 'Hello you', 'Error on parent method call');
_.test(instance.toUpperCase(), 'HELLO', 'Error on method inheritance');

resolve(instance);

Results

{
    "uuid": "095ee570-53b7-418e-856d-1cb869b7f8cd",
    "value": "Hello",
    "children": [
        {
            "uuid": "d01623e5-8c49-4fc9-b7a8-94ae79a87dff",
            "value": "world"
        },
        {
            "uuid": "2c54c27b-b05b-4344-9e49-29985e8b8383",
            "value": "you"
        }
    ]
}

Self-reference model

Models

const Self = Fractale.create("Self", {
    self: Self,
    value: String
});

Run

const { Self } = module.exports.get();
const instance_1 = new Self({ value: 'foo' });
const instance_2 = new Self({ self: { self: instance_1, value: 'bar' }, value: 'hello' });

_.test(instance_1.value, 'foo', 'Error on deep accessor variable name');
_.test(instance_2.self.value, 'bar', 'Error on self-reference accessor');
_.test(instance_2.self.self.value, 'foo', 'Error on double self-reference accessor');
_.test(instance_2.self.self.self, undefined, 'Error infinite self-reference accessor');

resolve(instance_2);

Results

{
    "uuid": "9ad98dd3-6e65-41e0-a592-998c68aafe95",
    "self": {
        "uuid": "3365521f-4db8-4856-a5a2-5dacda8648ba",
        "self": {
            "uuid": "49e72f5e-faa3-4948-a162-3f4abdb792f6",
            "value": "foo"
        },
        "value": "bar"
    },
    "value": "hello"
}

Options

'use strict';

const Fractale = require('fractale');

Fractale.setOptions({
    use_moment: () => { try { require('moment'); return true; } catch (error) { return false; }}, // Specify to fractale to transform date to moment instance
});

Field options

'use strict';

const Fractale = require('fractale');

const Child = Fractale.create('Child', {
    mixed: undefined,
    boolean: Boolean,
    number: Number,
    string: String,
});

const Parent = Fractale.create('Parent', Child, {
    parent: Fractale.with(Fractale.SELF, {
        // Pass number of parent to number of great-parent
        through: ['number'],
    }),
    children: [
        Fractale.with(Child, {
            // Pass number of parent to mixed of child
            // Pass string of parent to string of child
            through: { number: 'mixed', string: 'string' },
        })      
    ],
});

Field validators

'use strict';

const Fractale = require('fractale');

const Simple = Fractale.create('Simple', {
    anyway: Fractale.with(undefined, {
        validator: (value) => value !== 'A value'
    }),
    mixed: Fractale.with(undefined, {
        validator: {
            in: ['foo', 'bar', 42]
        }
    }),
    number: Fractale.with(Number, {
        validator: {
            gt: 17,
            gte: 18,
            lt: 51,
            lte: 50,
            between: [18, 50]
        }
    }),
    date: Fractale.with(Date, {
        validator: {
            gt: '2019-11-17',
            gte: new Date('2019-11-18'),
            lte: moment('2019-11-20'),
            lt: moment(),
            between: [18] // If one value is between ... and today or today and ...
        }
    }),
    string: Fractale.with(String, {
        validator: {
            like: /bar$/
        }
    }),
});
Graph - Complexity x Rate
Graph - Size x Rate

Global options

Models

const Global_Options = Fractale.create("Global_Options", {
    transformed: Number
});

Run

const { Global_Options } = module.exports.get();

const instance = new Global_Options({
    transformed: 39,
});

_.test(instance.transformed, 39, 'Error on global option transform accessor with type percentage');

instance.transformed = '10%';

_.test(instance.transformed, 0.1, 'Error on global option transform accessor with type percentage');

resolve(instance);

Results

{
    "uuid": "325dda13-7f10-4bc1-a212-c7eaa7e9beb6",
    "transformed": 0.1
}

Form usage

Models

const Form_Parent = Fractale.create("Form_Parent", {
    value: String
});

const Form_Child = Fractale.create("Form_Child", {
    parent: Form_Parent,
    value: String
});

Run

const { Form_Parent, Form_Child } = module.exports.get();
const parent = new Form_Parent({ value: 'foo' });
const child = new Form_Child({ parent: parent.uuid, value: 'bar' });

_.test(child.value, 'bar', 'Error on form setter');
_.test(child.parent.value, 'foo', 'Error on form setter');

resolve(child);

Results

{
    "uuid": "250940e8-d1a4-4476-89e9-fcc882f55c64",
    "parent": {
        "uuid": "ab7cde95-30ed-4bdf-8872-0b03a537e3a2",
        "value": "foo"
    },
    "value": "bar"
}