Concepts

Serializing

9min
slate's data model has been built with serialization in mind specifically, its text nodes are defined in a way that makes them easier to read at a glance, but also easy to serialize to common formats like html and markdown and, because slate uses plain json for its data, you can write serialization logic very easily plaintext for example, taking the value of an editor and returning plaintext import { node } from 'slate' const serialize = nodes => { return nodes map(n => node string(n)) join('\n') } here we're taking the children nodes of an editor as a nodes argument, and returning a plaintext representation where each top level node is separated by a single \n new line character for an input of const nodes = \[ { type 'paragraph', children \[{ text 'an opening paragraph ' }], }, { type 'quote', children \[{ text 'a wise quote ' }], }, { type 'paragraph', children \[{ text 'a closing paragraph!' }], }, ] you'd end up with an opening paragraph a wise quote a closing paragraph! notice how the quote block isn't distinguishable in any way, that's because we're talking about plaintext but you can serialize the data to anything you wantβ€”it's just json after all html for example, here's a similar serialize function for html import escapehtml from 'escape html' import { text } from 'slate' const serialize = node => { if (text istext(node)) { let string = escapehtml(node text) if (node bold) { string = `\<strong>${string}\</strong>` } return string } const children = node children map(n => serialize(n)) join('') switch (node type) { case 'quote' return `\<blockquote>\<p>${children}\</p>\</blockquote>` case 'paragraph' return `\<p>${children}\</p>` case 'link' return `\<a href="${escapehtml(node url)}">${children}\</a>` default return children } } this one is a bit more aware than the plaintext serializer above it's actually recursive so that it can keep iterating deeper through a node's children until it gets to the leaf text nodes and for each node it receives, it converts it to an html string it also takes a single node as input instead of an array, so if you passed in an editor like const editor = { children \[ { type 'paragraph', children \[ { text 'an opening paragraph with a ' }, { type 'link', url 'https //example com', children \[{ text 'link' }], }, { text ' in it ' }, ], }, { type 'quote', children \[{ text 'a wise quote ' }], }, { type 'paragraph', children \[{ text 'a closing paragraph!' }], }, ], // `editor` objects also have other properties that are omitted here } you'd receive back (line breaks added for legibility) \<p>an opening paragraph with a \<a href="https //example com">link\</a> in it \</p> \<blockquote>\<p>a wise quote \</p>\</blockquote> \<p>a closing paragraph!\</p> it's really that easy! deserializing another common use case in slate is doing the reverseβ€”deserializing this is when you have some arbitrary input and want to convert it into a slate compatible json structure for example, when someone pastes html into your editor and you want to ensure it gets parsed with the proper formatting for your editor slate has a built in helper for this the slate hyperscript package the most common way to use slate hyperscript is for writing jsx documents, for example when writing tests you might use it like so / @jsx jsx / import { jsx } from 'slate hyperscript' const input = ( \<fragment> \<element type="paragraph">a line of text \</element> \</fragment> ) and the jsx feature of your compiler (babel, typescript, etc ) would turn that input variable into const input = \[ { type 'paragraph', children \[{ text 'a line of text ' }], }, ] this is great for test cases, or places where you want to be able to write a lot of slate objects in a very readable form however! this doesn't help with deserialization but slate hyperscript isn't only for jsx it's just a way to build trees of slate content which happens to be exactly what you want to do when you're deserializing something like html for example, here's a deserialize function for html import { jsx } from 'slate hyperscript' const deserialize = el => { if (el nodetype === 3) { return el textcontent } else if (el nodetype !== 1) { return null } let children = array from(el childnodes) map(deserialize) if (children length === 0) { children = \[{ text '' }] } switch (el nodename) { case 'body' return jsx('fragment', {}, children) case 'br' return '\n' case 'blockquote' return jsx('element', { type 'quote' }, children) case 'p' return jsx('element', { type 'paragraph' }, children) case 'a' return jsx( 'element', { type 'link', url el getattribute('href') }, children ) default return el textcontent } } it takes in an el html element object and returns a slate fragment so if you have an html string, you can parse and deserialize it like so const html = ' ' const document = new domparser() parsefromstring(html, 'text/html') deserialize(document body) with this input \<p>an opening paragraph with a \<a href="https //example com">link\</a> in it \</p> \<blockquote>\<p>a wise quote \</p>\</blockquote> \<p>a closing paragraph!\</p> you'd end up with this output const fragment = \[ { type 'paragraph', children \[ { text 'an opening paragraph with a ' }, { type 'link', url 'https //example com', children \[{ text 'link' }], }, { text ' in it ' }, ], }, { type 'quote', children \[ { type 'paragraph', children \[{ text 'a wise quote ' }], }, ], }, { type 'paragraph', children \[{ text 'a closing paragraph!' }], }, ] and just like the serializing function, you can extend it to fit your exact domain model's needs
πŸ€”
Have a question?
Our super-smart AI,knowledgeable support team and an awesome community will get you an answer in a flash.
To ask a question or participate in discussions, you'll need to authenticate first.

ο»Ώ