8
0
Pārlūkot izejas kodu

Internal: Changed the way of data serialization.

Oskar Wróbel 7 gadi atpakaļ
vecāks
revīzija
76cf6bfbef
35 mainītis faili ar 235 papildinājumiem un 101 dzēšanām
  1. 2 2
      packages/ckeditor5-engine/src/dev-utils/enableenginedebug.js
  2. 5 1
      packages/ckeditor5-engine/src/model/node.js
  3. 11 0
      packages/ckeditor5-engine/src/model/operation/attributeoperation.js
  4. 11 0
      packages/ckeditor5-engine/src/model/operation/detachoperation.js
  5. 12 0
      packages/ckeditor5-engine/src/model/operation/insertoperation.js
  6. 8 0
      packages/ckeditor5-engine/src/model/operation/markeroperation.js
  7. 13 0
      packages/ckeditor5-engine/src/model/operation/mergeoperation.js
  8. 12 0
      packages/ckeditor5-engine/src/model/operation/moveoperation.js
  9. 3 3
      packages/ckeditor5-engine/src/model/operation/operation.js
  10. 11 0
      packages/ckeditor5-engine/src/model/operation/renameoperation.js
  11. 12 1
      packages/ckeditor5-engine/src/model/operation/rootattributeoperation.js
  12. 15 0
      packages/ckeditor5-engine/src/model/operation/splitoperation.js
  13. 12 0
      packages/ckeditor5-engine/src/model/operation/unwrapoperation.js
  14. 15 0
      packages/ckeditor5-engine/src/model/operation/wrapoperation.js
  15. 13 2
      packages/ckeditor5-engine/src/model/position.js
  16. 12 0
      packages/ckeditor5-engine/src/model/range.js
  17. 1 1
      packages/ckeditor5-engine/tests/dev-utils/enableenginedebug.js
  18. 0 9
      packages/ckeditor5-engine/tests/model/_utils/utils.js
  19. 1 2
      packages/ckeditor5-engine/tests/model/document.js
  20. 4 5
      packages/ckeditor5-engine/tests/model/documentfragment.js
  21. 10 8
      packages/ckeditor5-engine/tests/model/element.js
  22. 2 3
      packages/ckeditor5-engine/tests/model/nodelist.js
  23. 4 4
      packages/ckeditor5-engine/tests/model/operation/attributeoperation.js
  24. 2 3
      packages/ckeditor5-engine/tests/model/operation/detachoperation.js
  25. 4 5
      packages/ckeditor5-engine/tests/model/operation/insertoperation.js
  26. 4 5
      packages/ckeditor5-engine/tests/model/operation/markeroperation.js
  27. 4 5
      packages/ckeditor5-engine/tests/model/operation/moveoperation.js
  28. 2 3
      packages/ckeditor5-engine/tests/model/operation/nooperation.js
  29. 3 4
      packages/ckeditor5-engine/tests/model/operation/operation.js
  30. 3 4
      packages/ckeditor5-engine/tests/model/operation/renameoperation.js
  31. 4 4
      packages/ckeditor5-engine/tests/model/operation/rootattributeoperation.js
  32. 2 3
      packages/ckeditor5-engine/tests/model/position.js
  33. 14 2
      packages/ckeditor5-engine/tests/model/range.js
  34. 4 5
      packages/ckeditor5-engine/tests/model/text.js
  35. 0 17
      packages/ckeditor5-engine/tests/model/utils-tests/utils.js

+ 2 - 2
packages/ckeditor5-engine/src/dev-utils/enableenginedebug.js

@@ -427,7 +427,7 @@ function enableReplayerTools() {
 			this._appliedOperations = [];
 		}
 
-		this._appliedOperations.push( operation.toJSON() );
+		this._appliedOperations.push( operation );
 
 		return _modelApplyOperation.call( this, operation );
 	} );
@@ -455,7 +455,7 @@ function enableDocumentTools() {
 			this._operationLogs = [];
 		}
 
-		this._operationLogs.push( JSON.stringify( operation.toJSON() ) );
+		this._operationLogs.push( JSON.stringify( operation ) );
 
 		return _modelApplyOperation.call( this, operation );
 	} );

+ 5 - 1
packages/ckeditor5-engine/src/model/node.js

@@ -381,7 +381,11 @@ export default class Node {
 		const json = {};
 
 		if ( this._attrs.size ) {
-			json.attributes = [ ...this._attrs ];
+			json.attributes = Array.from( this._attrs ).reduce( ( result, attr ) => {
+				result[ attr[ 0 ] ] = attr[ 1 ];
+
+				return result;
+			}, {} );
 		}
 
 		return json;

+ 11 - 0
packages/ckeditor5-engine/src/model/operation/attributeoperation.js

@@ -110,6 +110,17 @@ export default class AttributeOperation extends Operation {
 	/**
 	 * @inheritDoc
 	 */
+	toJSON() {
+		const json = super.toJSON();
+
+		json.range = this.range.toJSON();
+
+		return json;
+	}
+
+	/**
+	 * @inheritDoc
+	 */
 	_validate() {
 		if ( !this.range.isFlat ) {
 			/**

+ 11 - 0
packages/ckeditor5-engine/src/model/operation/detachoperation.js

@@ -56,6 +56,17 @@ export default class DetachOperation extends Operation {
 	/**
 	 * @inheritDoc
 	 */
+	toJSON() {
+		const json = super.toJSON();
+
+		json.sourcePosition = this.sourcePosition.toJSON();
+
+		return json;
+	}
+
+	/**
+	 * @inheritDoc
+	 */
 	_validate() {
 		if ( this.sourcePosition.root.document ) {
 			/**

+ 12 - 0
packages/ckeditor5-engine/src/model/operation/insertoperation.js

@@ -131,6 +131,18 @@ export default class InsertOperation extends Operation {
 	/**
 	 * @inheritDoc
 	 */
+	toJSON() {
+		const json = super.toJSON();
+
+		json.position = this.position.toJSON();
+		json.nodes = this.nodes.toJSON();
+
+		return json;
+	}
+
+	/**
+	 * @inheritDoc
+	 */
 	static get className() {
 		return 'engine.model.operation.InsertOperation';
 	}

+ 8 - 0
packages/ckeditor5-engine/src/model/operation/markeroperation.js

@@ -109,6 +109,14 @@ export default class MarkerOperation extends Operation {
 	toJSON() {
 		const json = super.toJSON();
 
+		if ( this.oldRange ) {
+			json.oldRange = this.oldRange.toJSON();
+		}
+
+		if ( this.newRange ) {
+			json.newRange = this.newRange.toJSON();
+		}
+
 		delete json._markers;
 
 		return json;

+ 13 - 0
packages/ckeditor5-engine/src/model/operation/mergeoperation.js

@@ -155,6 +155,19 @@ export default class MergeOperation extends Operation {
 	/**
 	 * @inheritDoc
 	 */
+	toJSON() {
+		const json = super.toJSON();
+
+		json.sourcePosition = json.sourcePosition.toJSON();
+		json.targetPosition = json.targetPosition.toJSON();
+		json.graveyardPosition = json.graveyardPosition.toJSON();
+
+		return json;
+	}
+
+	/**
+	 * @inheritDoc
+	 */
 	static get className() {
 		return 'engine.model.operation.MergeOperation';
 	}

+ 12 - 0
packages/ckeditor5-engine/src/model/operation/moveoperation.js

@@ -178,6 +178,18 @@ export default class MoveOperation extends Operation {
 	/**
 	 * @inheritDoc
 	 */
+	toJSON() {
+		const json = super.toJSON();
+
+		json.sourcePosition = this.sourcePosition.toJSON();
+		json.targetPosition = this.targetPosition.toJSON();
+
+		return json;
+	}
+
+	/**
+	 * @inheritDoc
+	 */
 	static get className() {
 		return 'engine.model.operation.MoveOperation';
 	}

+ 3 - 3
packages/ckeditor5-engine/src/model/operation/operation.js

@@ -7,8 +7,6 @@
  * @module engine/model/operation/operation
  */
 
-import { clone } from 'lodash-es';
-
 /**
  * Abstract base operation class.
  *
@@ -94,7 +92,9 @@ export default class Operation {
 	 * @returns {Object} Clone of this object with the operation property replaced with string.
 	 */
 	toJSON() {
-		const json = clone( this, true );
+		// This method creates only a shallow copy, all nested objects should be defined separately.
+		// See https://github.com/ckeditor/ckeditor5-engine/issues/1477.
+		const json = Object.assign( {}, this );
 
 		json.__className = this.constructor.className;
 

+ 11 - 0
packages/ckeditor5-engine/src/model/operation/renameoperation.js

@@ -119,6 +119,17 @@ export default class RenameOperation extends Operation {
 	/**
 	 * @inheritDoc
 	 */
+	toJSON() {
+		const json = super.toJSON();
+
+		json.position = this.position.toJSON();
+
+		return json;
+	}
+
+	/**
+	 * @inheritDoc
+	 */
 	static get className() {
 		return 'engine.model.operation.RenameOperation';
 	}

+ 12 - 1
packages/ckeditor5-engine/src/model/operation/rootattributeoperation.js

@@ -165,6 +165,17 @@ export default class RootAttributeOperation extends Operation {
 	/**
 	 * @inheritDoc
 	 */
+	toJSON() {
+		const json = super.toJSON();
+
+		json.root = this.root.toJSON();
+
+		return json;
+	}
+
+	/**
+	 * @inheritDoc
+	 */
 	static get className() {
 		return 'engine.model.operation.RootAttributeOperation';
 	}
@@ -186,7 +197,7 @@ export default class RootAttributeOperation extends Operation {
 			 */
 			throw new CKEditorError(
 				'rootattribute-operation-fromjson-no-root: Cannot create RootAttributeOperation. Root with specified name does not exist.',
-				{ rootName: json }
+				{ rootName: json.root }
 			);
 		}
 

+ 15 - 0
packages/ckeditor5-engine/src/model/operation/splitoperation.js

@@ -169,6 +169,21 @@ export default class SplitOperation extends Operation {
 	/**
 	 * @inheritDoc
 	 */
+	toJSON() {
+		const json = super.toJSON();
+
+		json.position = this.position.toJSON();
+
+		if ( this.graveyardPosition ) {
+			json.graveyardPosition = this.graveyardPosition.toJSON();
+		}
+
+		return json;
+	}
+
+	/**
+	 * @inheritDoc
+	 */
 	static get className() {
 		return 'engine.model.operation.SplitOperation';
 	}

+ 12 - 0
packages/ckeditor5-engine/src/model/operation/unwrapoperation.js

@@ -133,6 +133,18 @@ export default class UnwrapOperation extends Operation {
 	/**
 	 * @inheritDoc
 	 */
+	toJSON() {
+		const json = super.toJSON();
+
+		json.position = this.position.toJSON();
+		json.graveyardPosition = this.graveyardPosition.toJSON();
+
+		return json;
+	}
+
+	/**
+	 * @inheritDoc
+	 */
 	static get className() {
 		return 'engine.model.operation.UnwrapOperation';
 	}

+ 15 - 0
packages/ckeditor5-engine/src/model/operation/wrapoperation.js

@@ -166,6 +166,21 @@ export default class WrapOperation extends Operation {
 	/**
 	 * @inheritDoc
 	 */
+	toJSON() {
+		const json = super.toJSON();
+
+		json.position = this.position.toJSON();
+
+		if ( json.graveyardPosition ) {
+			json.graveyardPosition = this.graveyardPosition.toJSON();
+		}
+
+		return json;
+	}
+
+	/**
+	 * @inheritDoc
+	 */
 	static get className() {
 		return 'engine.model.operation.WrapOperation';
 	}

+ 13 - 2
packages/ckeditor5-engine/src/model/position.js

@@ -791,6 +791,17 @@ export default class Position {
 	}
 
 	/**
+	 * @inheritDoc
+	 */
+	toJSON() {
+		return {
+			root: this.root.toJSON(),
+			path: Array.from( this.path ),
+			stickiness: this.stickiness
+		};
+	}
+
+	/**
 	 * Creates position at the given location. The location can be specified as:
 	 *
 	 * * a {@link module:engine/model/position~Position position},
@@ -915,7 +926,7 @@ export default class Position {
 	 */
 	static fromJSON( json, doc ) {
 		if ( json.root === '$graveyard' ) {
-			const pos = new Position( doc.graveyard, json.path );
+			const pos = new Position( doc.graveyard, Array.from( json.path ) );
 			pos.stickiness = json.stickiness;
 
 			return pos;
@@ -934,7 +945,7 @@ export default class Position {
 			);
 		}
 
-		const pos = new Position( doc.getRoot( json.root ), json.path );
+		const pos = new Position( doc.getRoot( json.root ), Array.from( json.path ) );
 		pos.stickiness = json.stickiness;
 
 		return pos;

+ 12 - 0
packages/ckeditor5-engine/src/model/range.js

@@ -469,6 +469,18 @@ export default class Range {
 		return this.start.getCommonAncestor( this.end );
 	}
 
+	/**
+	 * Converts `Range` to plain object and returns it.
+	 *
+	 * @returns {Object} `Node` converted to plain object.
+	 */
+	toJSON() {
+		return {
+			start: this.start.toJSON(),
+			end: this.end.toJSON()
+		};
+	}
+
 	_getTransformedByInsertOperation( operation, spread = false ) {
 		return this._getTransformedByInsertion( operation.position, operation.howMany, spread );
 	}

+ 1 - 1
packages/ckeditor5-engine/tests/dev-utils/enableenginedebug.js

@@ -644,7 +644,7 @@ describe( 'debug tools', () => {
 
 			const stringifiedOperations = model.getAppliedOperations();
 
-			expect( stringifiedOperations ).to.equal( JSON.stringify( insert.toJSON() ) );
+			expect( stringifiedOperations ).to.equal( JSON.stringify( insert ) );
 		} );
 
 		it( 'createReplayer()', () => {

+ 0 - 9
packages/ckeditor5-engine/tests/model/_utils/utils.js

@@ -37,15 +37,6 @@ export function getNodesAndText( range ) {
 }
 
 /**
- * Returns object JSON representation. It passes an object by JSON.stringify and JSON.parse functions.
- *
- * @param {Object|Array} object
- */
-export function jsonParseStringify( object ) {
-	return JSON.parse( JSON.stringify( object ) );
-}
-
-/**
  * Returns a {@link engine.model.Node} or if it starts at given offset, or {@link engine.model.TextProxy} with one
  * character, if given offset is occupied by a {@link engine.model.Text}.
  *

+ 1 - 2
packages/ckeditor5-engine/tests/model/document.js

@@ -12,7 +12,6 @@ import Range from '../../src/model/range';
 import Collection from '@ckeditor/ckeditor5-utils/src/collection';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import count from '@ckeditor/ckeditor5-utils/src/count';
-import { jsonParseStringify } from './_utils/utils';
 
 describe( 'Document', () => {
 	let model, doc;
@@ -500,7 +499,7 @@ describe( 'Document', () => {
 	} );
 
 	it( 'should be correctly converted to json', () => {
-		const serialized = jsonParseStringify( doc );
+		const serialized = doc.toJSON();
 
 		expect( serialized.selection ).to.equal( '[engine.model.DocumentSelection]' );
 		expect( serialized.model ).to.equal( '[engine.model.Model]' );

+ 4 - 5
packages/ckeditor5-engine/tests/model/documentfragment.js

@@ -7,7 +7,6 @@ import Element from '../../src/model/element';
 import Text from '../../src/model/text';
 import TextProxy from '../../src/model/textproxy';
 import DocumentFragment from '../../src/model/documentfragment';
-import { jsonParseStringify } from '../../tests/model/_utils/utils';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
 describe( 'DocumentFragment', () => {
@@ -263,7 +262,7 @@ describe( 'DocumentFragment', () => {
 		it( 'should serialize empty document fragment', () => {
 			const frag = new DocumentFragment();
 
-			expect( jsonParseStringify( frag ) ).to.deep.equal( [] );
+			expect( frag.toJSON() ).to.deep.equal( [] );
 		} );
 
 		it( 'should serialize document fragment with children', () => {
@@ -274,7 +273,7 @@ describe( 'DocumentFragment', () => {
 
 			const frag = new DocumentFragment( [ one, two, three ] );
 
-			expect( jsonParseStringify( frag ) ).to.deep.equal( [
+			expect( frag.toJSON() ).to.deep.equal( [
 				{ name: 'one' },
 				{
 					name: 'two',
@@ -293,7 +292,7 @@ describe( 'DocumentFragment', () => {
 		it( 'should create document fragment without children', () => {
 			const frag = new DocumentFragment();
 
-			const serialized = jsonParseStringify( frag );
+			const serialized = frag.toJSON();
 			const deserialized = DocumentFragment.fromJSON( serialized );
 
 			expect( deserialized.isEmpty ).to.be.true;
@@ -304,7 +303,7 @@ describe( 'DocumentFragment', () => {
 			const foo = new Text( 'foo' );
 			const frag = new DocumentFragment( [ p, foo ] );
 
-			const serialized = jsonParseStringify( frag );
+			const serialized = frag.toJSON();
 			const deserialized = DocumentFragment.fromJSON( serialized );
 
 			expect( deserialized.childCount ).to.equal( 2 );

+ 10 - 8
packages/ckeditor5-engine/tests/model/element.js

@@ -7,7 +7,6 @@ import Node from '../../src/model/node';
 import Element from '../../src/model/element';
 import Text from '../../src/model/text';
 import TextProxy from '../../src/model/textproxy';
-import { jsonParseStringify } from '../../tests/model/_utils/utils';
 import count from '@ckeditor/ckeditor5-utils/src/count';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
@@ -320,14 +319,17 @@ describe( 'Element', () => {
 		it( 'should serialize empty element', () => {
 			const element = new Element( 'one' );
 
-			expect( jsonParseStringify( element ) ).to.deep.equal( { name: 'one' } );
+			expect( element.toJSON() ).to.deep.equal( { name: 'one' } );
 		} );
 
 		it( 'should serialize element with attributes', () => {
 			const element = new Element( 'one', { foo: true, bar: false } );
 
-			expect( jsonParseStringify( element ) ).to.deep.equal( {
-				attributes: [ [ 'foo', true ], [ 'bar', false ] ],
+			expect( element.toJSON() ).to.deep.equal( {
+				attributes: {
+					foo: true,
+					bar: false
+				},
 				name: 'one'
 			} );
 		} );
@@ -340,7 +342,7 @@ describe( 'Element', () => {
 
 			const node = new Element( null, null, [ one, two, three ] );
 
-			expect( jsonParseStringify( node ) ).to.deep.equal( {
+			expect( node.toJSON() ).to.deep.equal( {
 				children: [
 					{ name: 'one' },
 					{
@@ -362,7 +364,7 @@ describe( 'Element', () => {
 		it( 'should create element without attributes', () => {
 			const el = new Element( 'el' );
 
-			const serialized = jsonParseStringify( el );
+			const serialized = el.toJSON();
 
 			const deserialized = Element.fromJSON( serialized );
 
@@ -374,7 +376,7 @@ describe( 'Element', () => {
 		it( 'should create element with attributes', () => {
 			const el = new Element( 'el', { foo: true } );
 
-			const serialized = jsonParseStringify( el );
+			const serialized = el.toJSON();
 
 			const deserialized = Element.fromJSON( serialized );
 
@@ -390,7 +392,7 @@ describe( 'Element', () => {
 			const text = new Text( 'foo' );
 			const el = new Element( 'el', null, [ p, text ] );
 
-			const serialized = jsonParseStringify( el );
+			const serialized = el.toJSON();
 
 			const deserialized = Element.fromJSON( serialized );
 

+ 2 - 3
packages/ckeditor5-engine/tests/model/nodelist.js

@@ -6,7 +6,6 @@
 import NodeList from '../../src/model/nodelist';
 import Element from '../../src/model/element';
 import Text from '../../src/model/text';
-import { jsonParseStringify } from '../../tests/model/_utils/utils';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
 describe( 'NodeList', () => {
@@ -195,11 +194,11 @@ describe( 'NodeList', () => {
 
 	describe( 'toJSON', () => {
 		it( 'should serialize empty node list', () => {
-			expect( jsonParseStringify( new NodeList() ) ).to.deep.equal( [] );
+			expect( ( new NodeList() ).toJSON() ).to.deep.equal( [] );
 		} );
 
 		it( 'should serialize node list with nodes', () => {
-			expect( jsonParseStringify( nodes ) ).to.deep.equal( [
+			expect( nodes.toJSON() ).to.deep.equal( [
 				{ name: 'p' },
 				{ data: 'foo' },
 				{ name: 'image' }

+ 4 - 4
packages/ckeditor5-engine/tests/model/operation/attributeoperation.js

@@ -10,7 +10,6 @@ import Position from '../../../src/model/position';
 import Range from '../../../src/model/range';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import count from '@ckeditor/ckeditor5-utils/src/count';
-import { jsonParseStringify } from '../../../tests/model/_utils/utils';
 
 describe( 'AttributeOperation', () => {
 	let model, doc, root;
@@ -377,16 +376,17 @@ describe( 'AttributeOperation', () => {
 				doc.version
 			);
 
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 
 			expect( serialized.__className ).to.equal( 'engine.model.operation.AttributeOperation' );
+
 			expect( serialized ).to.deep.equal( {
 				__className: 'engine.model.operation.AttributeOperation',
 				baseVersion: 0,
 				key: 'key',
 				newValue: 'newValue',
 				oldValue: null,
-				range: jsonParseStringify( range )
+				range: range.toJSON()
 			} );
 		} );
 	} );
@@ -402,7 +402,7 @@ describe( 'AttributeOperation', () => {
 				doc.version
 			);
 
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 			const deserialized = AttributeOperation.fromJSON( serialized, doc );
 
 			expect( deserialized ).to.deep.equal( op );

+ 2 - 3
packages/ckeditor5-engine/tests/model/operation/detachoperation.js

@@ -5,7 +5,6 @@
 
 import Model from '../../../src/model/model';
 import DetachOperation from '../../../src/model/operation/detachoperation';
-import { jsonParseStringify } from '../../../tests/model/_utils/utils';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import Position from '../../../src/model/position';
 import DocumentFragment from '../../../src/model/documentfragment';
@@ -61,12 +60,12 @@ describe( 'DetachOperation', () => {
 			const position = Position.createBefore( element );
 			const op = new DetachOperation( position, 1 );
 
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 
 			expect( serialized ).to.deep.equal( {
 				__className: 'engine.model.operation.DetachOperation',
 				baseVersion: null,
-				sourcePosition: jsonParseStringify( position ),
+				sourcePosition: position.toJSON(),
 				howMany: 1
 			} );
 		} );

+ 4 - 5
packages/ckeditor5-engine/tests/model/operation/insertoperation.js

@@ -11,7 +11,6 @@ import MoveOperation from '../../../src/model/operation/moveoperation';
 import Position from '../../../src/model/position';
 import Text from '../../../src/model/text';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
-import { jsonParseStringify } from '../../../tests/model/_utils/utils';
 
 describe( 'InsertOperation', () => {
 	let model, doc, root;
@@ -223,13 +222,13 @@ describe( 'InsertOperation', () => {
 			const op = new InsertOperation( position, new Text( 'x' ), doc.version );
 			op.shouldReceiveAttributes = true;
 
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 
 			expect( serialized ).to.deep.equal( {
 				__className: 'engine.model.operation.InsertOperation',
 				baseVersion: 0,
-				nodes: jsonParseStringify( new NodeList( [ new Text( 'x' ) ] ) ),
-				position: jsonParseStringify( position ),
+				nodes: ( new NodeList( [ new Text( 'x' ) ] ) ).toJSON(),
+				position: position.toJSON(),
 				shouldReceiveAttributes: true
 			} );
 		} );
@@ -244,7 +243,7 @@ describe( 'InsertOperation', () => {
 				doc.version
 			);
 
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 			const deserialized = InsertOperation.fromJSON( serialized, doc );
 
 			expect( deserialized ).to.deep.equal( op );

+ 4 - 5
packages/ckeditor5-engine/tests/model/operation/markeroperation.js

@@ -7,7 +7,6 @@ import Model from '../../../src/model/model';
 import Text from '../../../src/model/text';
 import Range from '../../../src/model/range';
 import MarkerOperation from '../../../src/model/operation/markeroperation';
-import { jsonParseStringify } from '../../model/_utils/utils';
 
 function matchRange( range ) {
 	return sinon.match( rangeToMatch => rangeToMatch.isEqual( range ) );
@@ -135,14 +134,14 @@ describe( 'MarkerOperation', () => {
 	describe( 'toJSON', () => {
 		it( 'should create proper serialized object', () => {
 			const op = new MarkerOperation( 'name', null, range, model.markers, doc.version, true );
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 
 			expect( serialized ).to.deep.equal( {
 				__className: 'engine.model.operation.MarkerOperation',
 				baseVersion: 0,
 				name: 'name',
 				oldRange: null,
-				newRange: jsonParseStringify( range ),
+				newRange: range.toJSON(),
 				affectsData: true
 			} );
 		} );
@@ -152,7 +151,7 @@ describe( 'MarkerOperation', () => {
 		it( 'should create proper MarkerOperation from json object #1', () => {
 			const op = new MarkerOperation( 'name', null, range, model.markers, doc.version, true );
 
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 			const deserialized = MarkerOperation.fromJSON( serialized, doc );
 
 			expect( deserialized ).to.deep.equal( op );
@@ -162,7 +161,7 @@ describe( 'MarkerOperation', () => {
 			// Gotta love 100% CC.
 			const op = new MarkerOperation( 'name', range, null, model.markers, doc.version, true );
 
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 			const deserialized = MarkerOperation.fromJSON( serialized, doc );
 
 			expect( deserialized ).to.deep.equal( op );

+ 4 - 5
packages/ckeditor5-engine/tests/model/operation/moveoperation.js

@@ -9,7 +9,6 @@ import Position from '../../../src/model/position';
 import Element from '../../../src/model/element';
 import Text from '../../../src/model/text';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
-import { jsonParseStringify } from '../../../tests/model/_utils/utils';
 
 describe( 'MoveOperation', () => {
 	let model, doc, root, gy;
@@ -270,14 +269,14 @@ describe( 'MoveOperation', () => {
 			const targetPosition = new Position( root, [ 1, 0 ] );
 			const op = new MoveOperation( sourcePosition, 1, targetPosition, doc.version );
 
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 
 			expect( serialized ).to.deep.equal( {
 				__className: 'engine.model.operation.MoveOperation',
 				baseVersion: 0,
 				howMany: 1,
-				sourcePosition: jsonParseStringify( op.sourcePosition ),
-				targetPosition: jsonParseStringify( op.targetPosition )
+				sourcePosition: op.sourcePosition.toJSON(),
+				targetPosition: op.targetPosition.toJSON()
 			} );
 		} );
 	} );
@@ -288,7 +287,7 @@ describe( 'MoveOperation', () => {
 			const targetPosition = new Position( root, [ 1, 0 ] );
 			const op = new MoveOperation( sourcePosition, 1, targetPosition, doc.version );
 
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 			const deserialized = MoveOperation.fromJSON( serialized, doc );
 
 			expect( deserialized ).to.deep.equal( op );

+ 2 - 3
packages/ckeditor5-engine/tests/model/operation/nooperation.js

@@ -5,7 +5,6 @@
 
 import Model from '../../../src/model/model';
 import NoOperation from '../../../src/model/operation/nooperation';
-import { jsonParseStringify } from '../../../tests/model/_utils/utils';
 
 describe( 'NoOperation', () => {
 	let model, noop, doc;
@@ -36,7 +35,7 @@ describe( 'NoOperation', () => {
 
 	describe( 'toJSON', () => {
 		it( 'should create proper json object', () => {
-			const serialized = jsonParseStringify( noop );
+			const serialized = noop.toJSON();
 
 			expect( serialized ).to.deep.equal( {
 				__className: 'engine.model.operation.NoOperation',
@@ -47,7 +46,7 @@ describe( 'NoOperation', () => {
 
 	describe( 'fromJSON', () => {
 		it( 'should create proper NoOperation from json object', () => {
-			const serialized = jsonParseStringify( noop );
+			const serialized = noop.toJSON();
 			const deserialized = NoOperation.fromJSON( serialized, doc );
 
 			expect( deserialized ).to.deep.equal( noop );

+ 3 - 4
packages/ckeditor5-engine/tests/model/operation/operation.js

@@ -5,7 +5,6 @@
 
 import Batch from '../../../src/model/batch';
 import Operation from '../../../src/model/operation/operation';
-import { jsonParseStringify } from '../../../tests/model/_utils/utils';
 
 describe( 'Operation', () => {
 	it( 'should save its base version', () => {
@@ -32,7 +31,7 @@ describe( 'Operation', () => {
 		it( 'should create proper json object #1', () => {
 			const op = new Operation( 4 );
 
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 
 			expect( serialized ).to.deep.equal( {
 				__className: 'engine.model.operation.Operation',
@@ -45,7 +44,7 @@ describe( 'Operation', () => {
 			const batch = new Batch();
 			batch.addOperation( op );
 
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 
 			expect( serialized ).to.deep.equal( {
 				__className: 'engine.model.operation.Operation',
@@ -58,7 +57,7 @@ describe( 'Operation', () => {
 		it( 'should create proper Operation from json object', () => {
 			const op = new Operation( 4 );
 
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 			const deserialized = Operation.fromJSON( serialized );
 
 			expect( deserialized ).to.deep.equal( op );

+ 3 - 4
packages/ckeditor5-engine/tests/model/operation/renameoperation.js

@@ -8,7 +8,6 @@ import Element from '../../../src/model/element';
 import RenameOperation from '../../../src/model/operation/renameoperation';
 import Position from '../../../src/model/position';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
-import { jsonParseStringify } from '../../../tests/model/_utils/utils';
 
 describe( 'RenameOperation', () => {
 	const oldName = 'oldName';
@@ -106,12 +105,12 @@ describe( 'RenameOperation', () => {
 	describe( 'toJSON', () => {
 		it( 'should create proper serialized object', () => {
 			const op = new RenameOperation( Position.createAt( root, 'end' ), oldName, newName, doc.version );
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 
 			expect( serialized ).to.deep.equal( {
 				__className: 'engine.model.operation.RenameOperation',
 				baseVersion: 0,
-				position: jsonParseStringify( op.position ),
+				position: op.position.toJSON(),
 				newName: 'newName',
 				oldName: 'oldName'
 			} );
@@ -122,7 +121,7 @@ describe( 'RenameOperation', () => {
 		it( 'should create proper AttributeOperation from json object', () => {
 			const op = new RenameOperation( Position.createAt( root, 'end' ), oldName, newName, doc.version );
 
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 			const deserialized = RenameOperation.fromJSON( serialized, doc );
 
 			expect( deserialized ).to.deep.equal( op );

+ 4 - 4
packages/ckeditor5-engine/tests/model/operation/rootattributeoperation.js

@@ -8,7 +8,6 @@ import DocumentFragment from '../../../src/model/documentfragment';
 import Element from '../../../src/model/element';
 import RootAttributeOperation from '../../../src/model/operation/rootattributeoperation';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
-import { jsonParseStringify } from '../../../tests/model/_utils/utils';
 
 describe( 'RootAttributeOperation', () => {
 	let model, doc, root;
@@ -268,7 +267,7 @@ describe( 'RootAttributeOperation', () => {
 				doc.version
 			);
 
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 
 			expect( serialized.__className ).to.equal( 'engine.model.operation.RootAttributeOperation' );
 			expect( serialized ).to.deep.equal( {
@@ -286,7 +285,7 @@ describe( 'RootAttributeOperation', () => {
 		it( 'should create proper RootAttributeOperation from json object', () => {
 			const op = new RootAttributeOperation( root, 'key', null, 'newValue', doc.version );
 
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
 			const deserialized = RootAttributeOperation.fromJSON( serialized, doc );
 
 			expect( deserialized ).to.deep.equal( op );
@@ -301,7 +300,8 @@ describe( 'RootAttributeOperation', () => {
 				doc.version
 			);
 
-			const serialized = jsonParseStringify( op );
+			const serialized = op.toJSON();
+
 			serialized.root = 'no-root';
 
 			expect( () => {

+ 2 - 3
packages/ckeditor5-engine/tests/model/position.js

@@ -11,7 +11,6 @@ import TextProxy from '../../src/model/textproxy';
 import Position from '../../src/model/position';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
-import { jsonParseStringify } from '../../tests/model/_utils/utils';
 
 describe( 'Position', () => {
 	let doc, root, otherRoot, p, ul, li1, li2, f, o, z, b, a, r, foz, bar;
@@ -805,7 +804,7 @@ describe( 'Position', () => {
 		it( 'should serialize position', () => {
 			const position = new Position( root, [ 0 ] );
 
-			const serialized = jsonParseStringify( position );
+			const serialized = position.toJSON();
 
 			expect( serialized ).to.deep.equal( { root: 'main', path: [ 0 ], stickiness: 'toNone' } );
 		} );
@@ -814,7 +813,7 @@ describe( 'Position', () => {
 			const position = new Position( doc.graveyard, [ 0 ] );
 			position.stickiness = 'toPrevious';
 
-			const serialized = jsonParseStringify( position );
+			const serialized = position.toJSON();
 
 			expect( serialized ).to.deep.equal( { root: '$graveyard', path: [ 0 ], stickiness: 'toPrevious' } );
 		} );

+ 14 - 2
packages/ckeditor5-engine/tests/model/range.js

@@ -18,7 +18,6 @@ import SplitOperation from '../../src/model/operation/splitoperation';
 import WrapOperation from '../../src/model/operation/wrapoperation';
 import UnwrapOperation from '../../src/model/operation/unwrapoperation';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
-import { jsonParseStringify } from '../../tests/model/_utils/utils';
 
 describe( 'Range', () => {
 	let doc, range, start, end, root, otherRoot, gy;
@@ -1268,9 +1267,22 @@ describe( 'Range', () => {
 		} );
 	} );
 
+	describe( 'toJSON()', () => {
+		it( 'should serialize range', () => {
+			const range = new Range( new Position( root, [ 1 ] ), new Position( root, [ 3 ] ) );
+
+			const serialized = range.toJSON();
+
+			expect( serialized ).to.deep.equal( {
+				start: { root: 'main', path: [ 1 ], stickiness: 'toNext' },
+				end: { root: 'main', path: [ 3 ], stickiness: 'toPrevious' }
+			} );
+		} );
+	} );
+
 	describe( 'fromJSON()', () => {
 		it( 'should create range from given JSON object', () => {
-			const serialized = jsonParseStringify( range );
+			const serialized = range.toJSON();
 			const deserialized = Range.fromJSON( serialized, doc );
 
 			expect( deserialized ).to.deep.equal( range );

+ 4 - 5
packages/ckeditor5-engine/tests/model/text.js

@@ -5,7 +5,6 @@
 
 import Text from '../../src/model/text';
 import Node from '../../src/model/node';
-import { jsonParseStringify } from '../../tests/model/_utils/utils';
 
 describe( 'Text', () => {
 	describe( 'constructor()', () => {
@@ -67,8 +66,8 @@ describe( 'Text', () => {
 		it( 'should serialize text node', () => {
 			const text = new Text( 'foo', { bold: true } );
 
-			expect( jsonParseStringify( text ) ).to.deep.equal( {
-				attributes: [ [ 'bold', true ] ],
+			expect( text.toJSON() ).to.deep.equal( {
+				attributes: { bold: true },
 				data: 'foo'
 			} );
 		} );
@@ -78,7 +77,7 @@ describe( 'Text', () => {
 		it( 'should create text node', () => {
 			const text = new Text( 'foo', { bold: true } );
 
-			const serialized = jsonParseStringify( text );
+			const serialized = text.toJSON();
 			const deserialized = Text.fromJSON( serialized );
 
 			expect( deserialized.data ).to.equal( 'foo' );
@@ -87,7 +86,7 @@ describe( 'Text', () => {
 
 		it( 'should support unicode', () => {
 			const textQ = new Text( 'நி' );
-			const json = jsonParseStringify( textQ );
+			const json = textQ.toJSON();
 
 			expect( json ).to.deep.equal( {
 				data: 'நி'

+ 0 - 17
packages/ckeditor5-engine/tests/model/utils-tests/utils.js

@@ -5,7 +5,6 @@
 
 import {
 	getNodesAndText,
-	jsonParseStringify,
 	itemAt,
 	getText,
 	createRangeOnElementOnly
@@ -37,22 +36,6 @@ describe( 'getNodesAndText', () => {
 	} );
 } );
 
-describe( 'jsonParseStringify', () => {
-	class Foo {
-		constructor( ra ) {
-			this.ra = ra;
-		}
-	}
-
-	it( 'should return cleaned object', () => {
-		const foo = new Foo( { bar: 'bar' } );
-
-		const fooJsoned = jsonParseStringify( foo );
-		expect( fooJsoned ).to.not.be.instanceOf( Foo );
-		expect( fooJsoned ).to.deep.equal( { ra: { bar: 'bar' } } );
-	} );
-} );
-
 describe( 'itemAt', () => {
 	let foo, img, bar, element;