├── DESIGN.md └── README.md /DESIGN.md: -------------------------------------------------------------------------------- 1 | # Design 2 | 3 | A key goal of the Dat and SSB networks is to make software development as open and free as possible. This includes enabling free extension to JSON schemas so that new apps can add features without lengthy community planning. This document explores a set of principles, patterns, and conventions for creating extensible, self-describing JSON. 4 | 5 | Background reading: 6 | 7 | - [Don't Make Me Think (About Linked Data)](https://berjon.com/linked-data/) 8 | - [Beaker#820](https://github.com/beakerbrowser/beaker/issues/820) 9 | 10 | ## Table of Contents 11 | 12 | 13 | 14 | 15 | 16 | - [Principles](#principles) 17 | - [Users cannot be forced](#users-cannot-be-forced) 18 | - [Munging is better than planning](#munging-is-better-than-planning) 19 | - [Ergonomics matter](#ergonomics-matter) 20 | - [When to use JSON-LZ](#when-to-use-json-lz) 21 | - [Requirements](#requirements) 22 | - [Good ergonomics within Javascript](#good-ergonomics-within-javascript) 23 | - [Post-hoc compatibility](#post-hoc-compatibility) 24 | - [Well-defined fallback behaviors](#well-defined-fallback-behaviors) 25 | - [Arbitrary vocabulary identifiers](#arbitrary-vocabulary-identifiers) 26 | - [Concerns](#concerns) 27 | - [Fatal ambiguity](#fatal-ambiguity) 28 | - [Keyname collisions](#keyname-collisions) 29 | - [Validation assumptions](#validation-assumptions) 30 | - [Patterns](#patterns) 31 | - [Duck-typing](#duck-typing) 32 | - [Conventional `type` field](#conventional-type-field) 33 | - [Conventional `schema` field](#conventional-schema-field) 34 | - [Field iteration by namespace](#field-iteration-by-namespace) 35 | - [Why don't we just use JSON-LD?](#why-dont-we-just-use-json-ld) 36 | 37 | 38 | 39 | ## Principles 40 | 41 | ### Users cannot be forced 42 | 43 | In an open network, there are no leaders or authorities. Every application is free to read and write data according to their own needs. Developers can only be expected to follow their incentives. There is no "Right Way," only useful patterns. 44 | 45 | ### Munging is better than planning 46 | 47 | Because it is impossible to plan for all possible schemas, developers should expect to transform between multiple different semantics to create interoperability. This is known as ["munging."](https://en.wikipedia.org/wiki/Mung_(computer_term)) 48 | 49 | Compatability between schemas must be doable as an afterthought. Otherwise, then too much upfront coordination will be required and we won't get compatible data. 50 | 51 | ### Ergonomics matter 52 | 53 | Developers should not have to futz with strange keynames or read long documents prior to writing their applications. Also, the less tooling (external libraries) the better. 54 | 55 | URLs should not be used as the identifiers for schemas. Publishing a schema is time-consuming and requires the developer to maintain the document at the given URL, which most developers won't bother with. Developers should instead be allowed to use "unique-enough" identifiers which are unlikely to collide, and which will provide good information on a search. 56 | 57 | ## When to use JSON-LZ 58 | 59 | Apps are not expected to use JSON-LZ until they need to support multiple vocabularies. This is in keeping with our philosophy that "munging after" is better than "planning before." Put another way: you can't get every developer to do the "Right Thing," so you should make your apps safely handle the "Wrong Thing." 60 | 61 | The default assumption is that JSON will be created without any vocabulary metadata. JSON-LZ apps are **always expected** to use [duck-typing](https://en.wikipedia.org/wiki/Duck_typing) and custom identifying attributes (such as a `type`) as their primary solution to validating their inputs. If a JSON input fits its schema-shape, then apps can assume vocabulary conformance. 62 | 63 | As communities grow and integrate more applications around a shared dataset, they will encounter issues due to ambiguities in schemas. At this point, they can add JSON-LZ metadata and tooling. (Of course, you can add JSON-LZ as early as you want.) The level of strictness used can increase as the complexity of the issues increases. The goal is to be as relaxed as possible and therefore support the most possible inputs without introducing "fatal ambiguities." When ambiguities begin to degrade the UX, the strictness around vocabularies can be increased. 64 | 65 | ## Requirements 66 | 67 | ### Good ergonomics within Javascript 68 | 69 | JSON-LZ should be fun and easy to use. Developers should not have to futz with strange keynames or read long documents like this one prior to writing their applications. Further, it should involve minimal tooling to leverage; an app should not have to import a library to support JSON-LZ. 70 | 71 | ### Post-hoc compatibility 72 | 73 | Compatability between schemas must be doable as an afterthought. Otherwise, then too much upfront coordination will be required and we won't get compatible data. 74 | 75 | ### Well-defined fallback behaviors 76 | 77 | Ambiguous interpretations of data can sometimes result in critical mistakes. This should be avoided. 78 | 79 | For instance, in a social media application, a "status update" JSON might be extended to include an `"audience"` field. The field's goal would be to control the visibility of a message; for instance, "only show this status-update to Bob." If that field is not interpretted correctly by a client, the message would be visible to the wrong audiences. This is fatal ambiguity caused by partial support; the client understood the parts of the JSON that meant "status update" but it didn't understand the part that said "only show this to Bob." 80 | 81 | Fatal ambiguities make it difficult (if not impossible) for developers to freely extend their schemas. Because they have no way to signal the danger of partial support, they can only hope that all other clients will add full support in the near future. This will stifle development. 82 | 83 | To address this, applications need a mechanism to identify ambiguities and fall back to well-defined behaviors. If a schema adds a feature which *must* be supported by an application for the message to have meaning, then the JSON-LZ tooling should to be able to identify the ambiguity and trigger one of the fallback behaviors. (Fallbacks may range from "render the simple form" to "render a descriptive error" to "render nothing at all.") 84 | 85 | ### Arbitrary vocabulary identifiers 86 | 87 | Publishing a schema is time-consuming and requires the developer to maintain the document at the given URL, which most developers won't bother with. Developers should instead be allowed to use "unique-enough" identifiers which are unlikely to collide, and which will provide good information on a search. 88 | 89 | ## Concerns 90 | 91 | ### Fatal ambiguity 92 | 93 | **Description**: Errors caused by partial support for a schema. 94 | 95 | **Example**: *Bob* extends a schema used by *Alice* with an `audience` field to convey which users should be shown the message. *Alice* does not understand the new field and therefore shows the message to the wrong audience. *Bob* needs a way to convey the importance of the new `audience` field so that *Alice* ignores the message if she doesn't understand that field. 96 | 97 | **Discussion**: This can be avoided by fields which describe required semantics. In SSB, the `type` field does this. I propose using a separate `schema` field to convey semantics, because `type` is separately useful for conveying what a JSON represents (kind/class). Conveying semantics in the `type` would incentivize *Alice* to change type from `"message"` to (eg) `"message-with-audience"` and we'd lose the kind/class equivalence. A `schema` field can also convey "unrequired" semantics in addition to "required" semantics, which makes it less strict. 98 | 99 | ### Keyname collisions 100 | 101 | **Description**: Collisions between two schemas which make interoperation impossible. 102 | 103 | **Example**: Independently of each other, *Alice* and *Bob* add a `date` field to an `Event` schema. *Alice*'s `date` field is a `String` which indicates when the event should occur, while *Bob*'s `date` field is a `Boolean` which indicates whether the event is a romantic encounter. Later, *Alice* tries to integrate *Bob*'s schema and discovers the collision makes this impossible. 104 | 105 | **Discussion**: The main purpose of semantics is to create namespaces which disambiguate collisions such as this. The proposed `schema` field makes it possible for *Alice* to identify the exact namespace of a given `date` and process it accordingly. However, AFAIK there's no simple way to merge the two schemas together. Either *Alice* or *Bob* will need to change their schema. The "Field iteration by namespace" pattern makes this less of a problem. 106 | 107 | ### Validation assumptions 108 | 109 | **Description**: Unstated assumptions about how to validate a schema result in unexpected breakages. 110 | 111 | **Example**: AppA tolerates a scalar OR array for `{"foo":}` while AppB only accepts a scalar. AppB therefore fails validation more frequently and thus appears to "break randomly." 112 | 113 | **Discussion**: This will definitely happen but I don't know of any good ways to solve it. The cure would probably be worse than the disease. I suggest we don't worry about this as spec-writers. 114 | 115 | ## Patterns 116 | 117 | ### Duck-typing 118 | 119 | **Description**: Validate inputs by examining the structure of the JSON. If the structure does not match expectations, or is missing key information, the JSON should be ignored as nonconformant. 120 | 121 | ### Conventional `type` field 122 | 123 | **Description**: Identify kind/class of a JSON input using a conventional toplevel `type` field. Example values: `"note"`, `"status-update"`, `"event"`, `"profile"`, `"code-module"`. 124 | 125 | ```jsx= 126 | { 127 | "type": "event", 128 | "name": "JSON-LZ Working Group Meeting", 129 | "startDate": "2018-01-21T19:30:00.000Z", 130 | "endDate": "2018-01-21T20:30:00.000Z" 131 | } 132 | ``` 133 | 134 | **Discussion**: In SSB, the type is used to convey both kind/class and semantics. I suggest using a separate `schema` field to convey semantics, because `type` is separately useful for conveying kind/class (see the discussion in "Fatal Ambiguity"). It might work to suggest that `type` conveys semantics if `schema` is missing. 135 | 136 | ### Conventional `schema` field 137 | 138 | **Description**: Identify the semantics of an object using a conventional toplevel `schema` field. This field is [described in detail here](README.md#vocabulary-metadata). 139 | 140 | ```jsx= 141 | { 142 | "schema": "alice-calendar-app", 143 | "type": "event", 144 | "name": "JSON-LZ Working Group Meeting", 145 | "startDate": "2018-01-21T19:30:00.000Z", 146 | "endDate": "2018-01-21T20:30:00.000Z" 147 | } 148 | ``` 149 | 150 | **Discussion**: The [full spec](README.md#vocabulary-metadata) for this field makes it possible to "paint" every attribute with an informal namespace ID, which addresses the "Keyname collisions" concern. This field can also mark the namespaces which are required, solving the "Fatal ambigiuties" concern. 151 | 152 | ### Field iteration by namespace 153 | 154 | **Description**: The conventional `schema` field makes it possible to iterate the fields in a namespace. See [this example](README.md#transforming-between-vocabularies) for reference. This is useful for normalizing and transforming between schemas. 155 | 156 | ```jsx= 157 | jlz.iterate(doc, 'activitystreams', (key, value, path) => { 158 | // example values for {"inReplyTo": {"summary": "Previous note"}} 159 | console.log(key) // => 'summary' 160 | console.log(value) // => 'Previous note' 161 | console.log(path) // => 'inReplyTo.summary' 162 | }) 163 | ``` 164 | 165 | 166 | ## Why don't we just use JSON-LD? 167 | 168 | JSON-LD has more features than we need. The graph data model is not useful in our work and adds a lot of complexity. Also, the requirement to use URLs as vocabularity identifiers is very tedious for developers. 169 | 170 | More importantly than those two issues, JSON-LD adds flexibility in the wrong places. 171 | 172 | Consider the two following JSON-LD objects: 173 | 174 | ```js 175 | const obj1 = { 176 | "@context": { 177 | "schema": "http://schema.org/", 178 | }, 179 | "@type": "schema:Person", 180 | "schema:name": "Manu Sporny", 181 | "schema:knows": "http://foaf.me/gkellogg#me" 182 | } 183 | const obj2 = { 184 | "@context": "http://schema.org/", 185 | "@type": "Person", 186 | "name": "Manu Sporny", 187 | "knows": "http://foaf.me/gkellogg#me" 188 | } 189 | ``` 190 | 191 | These objects are schematically-equivalent but have differing structures. This is a problem. Javascript developers rely on structure to validate and process their inputs. Before either of these objects can be processed, they need to be normalized into a standard form. 192 | 193 | The potential to transform structures automatically with expand/compact is neat, but in practice it means that reliable use of JSON-LD *requires* expansion and compaction because multiple object structures can map to the same schemas. That's a lot more tooling than we want to require for good support. 194 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSON LaZy (LZ) 2 | 3 | Simple self-describing JSON. Includes tools to: 4 | 5 | - Identify the schemas of JSON documents, 6 | - Transform between schemas, and 7 | - Detect ambiguities and abort. 8 | 9 | See [DESIGN.md](DESIGN.md) for more information. 10 | 11 | **Status:** Work in progress. 12 | 13 | ## Table of Contents 14 | 15 | 16 | 17 | 18 | 19 | - [Example objects](#example-objects) 20 | - [No schema](#no-schema) 21 | - [One schema](#one-schema) 22 | - [Two schemas](#two-schemas) 23 | - [API](#api) 24 | - [jlz.detectSupport(obj, schemaIds)](#jlzdetectsupportobj-schemaids) 25 | - [jlz.getSchemaFor(obj, attrPath)](#jlzgetschemaforobj-attrpath) 26 | - [jlz.iterate(obj, schemaId, fn)](#jlziterateobj-schemaid-fn) 27 | - [Schema metadata](#schema-metadata) 28 | - [name](#name) 29 | - [attrs](#attrs) 30 | - [required](#required) 31 | - [Attribute Paths](#attribute-paths) 32 | - [How to use JSON-LZ](#how-to-use-json-lz) 33 | - [Validating objects](#validating-objects) 34 | - [Transforming between schemas](#transforming-between-schemas) 35 | - [Detecting schema support](#detecting-schema-support) 36 | - [An example of "fatal ambiguity"](#an-example-of-fatal-ambiguity) 37 | - [How do I avoid fatal ambiguities?](#how-do-i-avoid-fatal-ambiguities) 38 | - [How often should I use `"required": true` in my JSON?](#how-often-should-i-use-required-true-in-my-json) 39 | 40 | 41 | 42 | ## Example objects 43 | 44 | Lazy is a way to describe what schemas your JSON is using. It's an optional convention. It's helpful when you start working with other developers but don't have any way to coordinate with them (for instance, because you're not on a team together). By adding schema information, you give other developers hints about what your objects are. 45 | 46 | ### No schema 47 | 48 | Schemas are optional. This JSON has no schema declaration. 49 | 50 | ```json 51 | { 52 | "name": "JSON-LZ Working Group Meeting", 53 | "startDate": "2018-01-21T19:30:00.000Z", 54 | "endDate": "2018-01-21T20:30:00.000Z" 55 | } 56 | ``` 57 | 58 | ### One schema 59 | 60 | A schema can help other developers read your data. This object uses the `"alices-calendar-app"` schema. 61 | 62 | ```json 63 | { 64 | "schema": "alices-calendar-app", 65 | "name": "JSON-LZ Working Group Meeting", 66 | "startDate": "2018-01-21T19:30:00.000Z", 67 | "endDate": "2018-01-21T20:30:00.000Z" 68 | } 69 | ``` 70 | 71 | ### Two schemas 72 | 73 | Multiple schemas can be combined on one JSON. Most attributes in this object are part of the `"alices-calendar-app"` schema. The `"requested"` and `"deadlineDate"` attributes are part of the `"bobs-rsvps"` schema. 74 | 75 | ```json 76 | { 77 | "schema": [ 78 | "alices-calendar-app", 79 | {"name": "bobs-rsvps", "attrs": "rsvp.*", "required": true} 80 | ], 81 | "name": "JSON-LZ Working Group Meeting", 82 | "startDate": "2018-01-21T19:30:00.000Z", 83 | "endDate": "2018-01-21T20:30:00.000Z", 84 | "rsvp": { 85 | "requested": true, 86 | "deadlineDate": "2018-01-18T19:30:00.000Z" 87 | } 88 | } 89 | ``` 90 | 91 | This JSON also uses the `"required": true` flag on the `"bobs-rsvps"` schema. See ["Schema metadata"](#schema-metadata) for more information. 92 | 93 | ## API 94 | 95 | When you have the `"schema"` convention in your JSON, you can leverage the information to run some helpful operations. 96 | 97 | ### jlz.detectSupport(obj, schemaIds) 98 | 99 | This method is a simple way to detect whether your app can handle some JSON. 100 | 101 | There are 4 possible responses: 102 | 103 | - `.full === true` You support all of the JSON's declared schemas. 104 | - `.partial === true` You support some of the declared schemas. 105 | - `.incompatible === true` You don't support one of the required schemas. 106 | - `.inconclusive === true` The JSON doesn't declare its schemas. 107 | 108 | ```js 109 | var support = JSONLZ.detectSupport(obj, ['alices-calendar-app', 'bobs-rsvps']) 110 | if (support.full) { 111 | // 100% supported 112 | } 113 | if (support.partial) { 114 | // some schemas are missing but should work fine 115 | } 116 | if (support.incompatible) { 117 | // unable to process object because required schemas are missing 118 | // (in practice this is the only result we MUST worry about) 119 | } 120 | if (support.inconclusive) { 121 | // the object has no JSON-LZ metadata 122 | } 123 | ``` 124 | 125 | ### jlz.getSchemaFor(obj, attrPath) 126 | 127 | This helper lets you find the schema for a given attribute. If the JSON describes its own schema, you'll be given an identifier. 128 | 129 | ```js 130 | jlz.getSchemaFor(doc, 'text') // => 'bll-fritter' 131 | jlz.getSchemaFor(doc, 'content') // => 'https://www.w3.org/ns/activitystreams' 132 | ``` 133 | 134 | ### jlz.iterate(obj, schemaId, fn) 135 | 136 | This helper finds all attributes in the JSON of a given schema. It will iterate each of the attribute, calling `fn` with identifying information. 137 | 138 | ```js 139 | jlz.iterate(doc, 'https://www.w3.org/ns/activitystreams', (key, value, path) => { 140 | // example values for {"inReplyTo": {"summary": "Previous note"}} 141 | console.log(key) // => 'summary' 142 | console.log(path) // => 'inReplyTo.summary' 143 | console.log(value) // => 'Previous note' 144 | }) 145 | ``` 146 | 147 | ## Schema metadata 148 | 149 | The JSON-LZ metadata is placed in the toplevel `"schema"` attribute. It is an array of objects which describe the schemas of the attributes. 150 | 151 | ```js 152 | { 153 | "schema": [/* your schema objects */], 154 | /* the rest of the data */ 155 | } 156 | ``` 157 | 158 | The values in the `"schema"` array are called "schema objects." They follow the following schema: 159 | 160 | ``` 161 | { 162 | name: String. 163 | attrs: optional Array. Default undefined. 164 | required: optional Boolean. Default false. 165 | } 166 | ``` 167 | 168 | If a string value is given in `schema`, it will expand to an object with the default values. For instance, the string 'foo-schema' will expand to the following object: 169 | 170 | ```js 171 | { 172 | name: 'foo-schema', 173 | attrs: undefined, 174 | required: false 175 | } 176 | ``` 177 | 178 | ### name 179 | 180 | The name is the only required attribute. It may be any string value, including URLs. You should try to use long and non-generic names to avoid accidental collisions. For instance, instead of just `'status-update'`, you should prepend your org name, eg `'genericorp-status-update'`. 181 | 182 | You should use a URL for the name if you plan to publish and maintain documentation at the location. If not, then you should use a string that's unique enough that it will be easy to search against (and therefore discover the docs via a search engine). 183 | 184 | ### attrs 185 | 186 | The `attrs` attribute describes which attributes in the object use the schema. It should be an array of attribute paths. (See [Attribute Paths](#attribute-paths).) 187 | 188 | When multiple attribute paths match, the most specific (that is, longest) will be used. Therefore, for the object `{foo:{bar:{baz: true}}}`, the path `foo.bar` will match everything inside for `bar` (include `baz`). However, if the path `foo.bar.baz` is present, then that will take precedent over the `foo.bar` selector. If there are multiple matching selectors of the same length, the first to match will be used (schema objects are processed in order). 189 | 190 | If `attrs` is set to `undefined`, then the schema will be used as the default schema for any attribute that does not have an explicitly-set schema. Only the first schema-object with an undefined `attrs` will be the default. 191 | 192 | ### required 193 | 194 | If true, the schema must be supported by the reading application for the JSON to have meaning. An application which does not support a required schema must not use the input JSON. 195 | 196 | ## Attribute Paths 197 | 198 | The "Attribute Path" is a very simple language for identifying attributes within the JSON object. It supports two special characters: 199 | 200 | - `.` Separates attribute references. 201 | - `*` Wildcard. Matches all attribute names 202 | 203 | Examples: 204 | 205 | ``` 206 | name => 'Bob' 207 | location => {state: 'Texas', city: 'Austin'} 208 | location.state => 'Texas' 209 | location.* => ['Texas', 'Austin'] 210 | friends => [{name: 'Alice'}, {name: 'Carla'}] 211 | friends.* => [{name: 'Alice'}, {name: 'Carla'}] 212 | friends.0 => {name: 'Alice'} 213 | friends.0.name => 'Alice' 214 | friends.1.name => 'Carla' 215 | friends.*.name => ['Alice', 'Carla'] 216 | ``` 217 | 218 | Input object: 219 | 220 | ``` 221 | { 222 | name: 'Bob', 223 | location: { 224 | state: 'Texas', 225 | city: 'Austin' 226 | }, 227 | friends: [ 228 | {name: 'Alice'}, 229 | {name: 'Carla'} 230 | ] 231 | } 232 | ``` 233 | 234 | ## How to use JSON-LZ 235 | 236 | JSON-LZ uses metadata to "paint" the attributes of a JSON document with schema declarations. Schemas are then used to help identify the meanings of attributes, transform between different schemas, and fallback to safe defaults in the case ambiguous meaning. It is *not* an alternative to validation. 237 | 238 | See ["When to use JSON-LZ"](DESIGN.md#when-to-use-json-lz) for more information about why JSON-LZ exists and when to use it in your app. 239 | 240 | ### Validating objects 241 | 242 | Applications should validate their input JSON by checking the structure. Tools such as [JSON-Schema](http://json-schema.org/) are useful for accomplishing this. JSON-LZ does not help with validation - it only helps with checking for ambiguities in declared schemas and for transforming between schemas. 243 | 244 | ### Transforming between schemas 245 | 246 | Sometimes there are competing schemas that encode the same data. If you think you can still use the data, you can transform the data to fit the structure you use. This is sometimes called "munging" the data. 247 | 248 | For example, suppose you have two "RSVP" schemas: `'bobs-rsvps'` and `'carlas-rsvps'`. 249 | 250 | The `'bobs-rsvps'` schema looks like: 251 | 252 | ```json 253 | {"rsvp": {"requested": true, "deadlineDate": "..."}} 254 | ``` 255 | 256 | While the `'carlas-rsvps'` schema looks like: 257 | 258 | ```json 259 | {"rsvpIsRequested": true, "rsvpDeadline": "..."} 260 | ``` 261 | 262 | We can convert from `'carlas-rsvps'` to `'bobs-rsvps'` by iterating over each attribute in the `'carlas-rsvps'` schema: 263 | 264 | ```js 265 | obj.rsvp = obj.rsvp || {} 266 | JSONLZ.iterate(obj, 'carlas-rsvps', (key, value, path) => { 267 | if (key === 'rsvpIsRequested') { 268 | obj.rsvp.requested = value 269 | } 270 | if (key === 'rsvpDeadline') { 271 | obj.rsvp.deadlineDate = value 272 | } 273 | }) 274 | ``` 275 | 276 | Here's an example object that would work with this technique: 277 | 278 | ```js 279 | { 280 | "schema": [ 281 | "alices-calendar-app", 282 | {"name": "carlas-rsvps", "attrs": ["rsvpIsRequested", "rsvpDeadline"]} 283 | ], 284 | "name": "JSON-LZ Working Group Meeting", 285 | "startDate": "2018-01-21T19:30:00.000Z", 286 | "endDate": "2018-01-21T20:30:00.000Z", 287 | "rsvpIsRequested": true, 288 | "rsvpDeadline": "2018-01-18T19:30:00.000Z" 289 | } 290 | ``` 291 | 292 | And here is what the output object would look like: 293 | 294 | ```js 295 | { 296 | // ... 297 | "name": "JSON-LZ Working Group Meeting", 298 | "startDate": "2018-01-21T19:30:00.000Z", 299 | "endDate": "2018-01-21T20:30:00.000Z", 300 | "rsvp": { 301 | "requested": true, 302 | "deadlineDate": "2018-01-18T19:30:00.000Z" 303 | } 304 | } 305 | ``` 306 | 307 | ### Detecting schema support 308 | 309 | JSON-LZ includes a method `detectSupport()` for detecting the compatibility between your app and a JSON's declared schemas. This method is important for avoiding "fatal ambiguity." 310 | 311 | #### An example of "fatal ambiguity" 312 | 313 | Suppose you have a social media application with "status update" JSONs, and one of the users' applications extends the JSON to include an `"audience"` field. The field's goal would be to control the visibility of a message; for instance, "only show this status-update to Bob." If that field is not interpretted correctly by a client, the message would be visible to the wrong audiences. 314 | 315 | This is fatal ambiguity caused by partial support; the client understood the parts of the JSON that meant "status update" but it didn't understand the part that said "only show this to Bob." 316 | 317 | #### How do I avoid fatal ambiguities? 318 | 319 | As a schema developer, you use the `"required": true` attribute in your schema object. This signals that the JSON data would be *misunderstood* without fully supporting that schema. 320 | 321 | ```js 322 | { 323 | "schema": { 324 | { 325 | "name": "my-critical-schema", 326 | "required": true // this schema MUST be supported! 327 | } 328 | } 329 | // ... 330 | } 331 | ``` 332 | 333 | As an app developer, you use the `detectSupport()` method on inputs and you pass in the list of schemas that you fully support. If the returned object has the `.incompatible` flag set, you should ignore the JSON, or perhaps save it in debugging storage for the user to diagnose. 334 | 335 | ```js 336 | var support = JSONLZ.detectSupport(obj, ['alices-calendar-app', 'bobs-rsvps']) 337 | if (support.full) { 338 | // 100% supported 339 | } 340 | if (support.partial) { 341 | // some schemas are missing but should work fine 342 | } 343 | if (support.incompatible) { 344 | // unable to process object because required schemas are missing 345 | // (in practice this is the only result we MUST worry about) 346 | } 347 | if (support.inconclusive) { 348 | // the object has no JSON-LZ metadata 349 | } 350 | ``` 351 | 352 | #### How often should I use `"required": true` in my JSON? 353 | 354 | **Very rarely!** The only time you should include it is if the misinterpretation (or non-interpretation) of a field would create a major issue. In most cases, partial support of an object's schemas will not create issues, so you should leave the schema as unrequired. 355 | 356 | Required schemas are a way to say "hide this object if you don't understand this schema fully." Use it selectively. 357 | 358 | 359 | --------------------------------------------------------------------------------