├── .gitignore ├── CNAME ├── README.md ├── jf2-context.jsonld ├── publish ├── 2016-07-28-FPWD │ └── index-src.html ├── 2017-06-12-WD │ └── index-src.html └── 2017-06-27-WD │ └── index-src.html └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | jf2.spec.indieweb.org -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JF2 Editor's Draft 2 | Simplified JSON format of post streams 3 | 4 | Contributions to this specification are governed by the W3C IPR Policy. Pull requests will be accepted only if the submitter was a member of the W3C Social WG but issues can be opened by anyone. 5 | -------------------------------------------------------------------------------- /jf2-context.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "http://www.w3.org/TR/activitystreams-core/activitystreams2-context", 4 | { 5 | "mf": "http://microformats.org/wiki/", 6 | "vcard": "http://www.w3.org/2006/vcard/ns#", 7 | "jf2": "https://github.com/w3c-social/Social-Syntax-Brainstorming/wiki/jf2#", 8 | "iwc": "http://indiewebcamp.com/", 9 | 10 | "Adr": "mf:h-adr", 11 | "Card": "mf:h-card", 12 | "Entry": "mf:h-entry", 13 | "Feed": "mf:h-feed", 14 | "Item": "mf:h-item", 15 | "Listing": "mf:h-listing", 16 | "Product": "mf:h-product", 17 | "Recipe": "mf:h-recipe", 18 | "Resume": "mf:h-resume", 19 | "Review": "mf:h-review", 20 | "Review-aggregate": "mf:h-review-aggregate", 21 | "Cite": "mf:h-cite", 22 | 23 | "additional-name": "vcard:additional-name", 24 | "adr": "vcard:adr", 25 | "anniversary": "vcard:anniversary", 26 | "bday": "vcard:bday", 27 | "category": "vcard:category", 28 | "country-name": "vcard:country-name", 29 | "email": "vcard:hasEmail", 30 | "family-name": "vcard:family-name", 31 | "given-name": "vcard:given-name", 32 | "honorific-prefix": "vcard:honorific-prefix", 33 | "honorific-suffix": "vcard:honorific-suffix", 34 | "impp": "vcard:hasInstantMessage", 35 | "nickname": "vcard:nickname", 36 | "org": "vcard:organization-name", 37 | "organization-unit": "vcard:organizational-unit", 38 | "postal-code": "vcard:hasPostalCode", 39 | "street-address": "vcard:street-address", 40 | "locality": "vcard:locality", 41 | "region": "vcard:region", 42 | "tel": "vcard:hasTelephone", 43 | "tz": "vcard:tz", 44 | "uid": "vcard:hasUID", 45 | "fn": "vcard:fn", 46 | 47 | "affiliation": "mf:microformats2#p-affiliation", 48 | "audio": "mf:microformats2#u-audio", 49 | "best": "mf:microformats2#p-best", 50 | "brand": "mf:microformats2#p-brand", 51 | "contact": "mf:microformats2#p-contact", 52 | "count": "mf:microformats2#p-count", 53 | "education": "mf:microformats2#p-education", 54 | "experience": "mf:microformats2#p-experience", 55 | "extended-address": "mf:microformats2#p-extended-address", 56 | "gender-identity": "mf:microformats2#p-gender-identity", 57 | "identifier": "mf:microformats2#u-identifier", 58 | "ingredient": "mf:microformats2#p-ingredient", 59 | "instructions": "mf:microformats2#e-instructions", 60 | "job-title": "mf:microformats2#p-job-title", 61 | "key": "mf:microformats2#u-key", 62 | "label": "mf:microformats2#p-label", 63 | "note": "mf:microformats2#p-note", 64 | "nutrition": "mf:microformats2#p-nutrition", 65 | "post-office-box": "mf:microformats2#p-post-office-box", 66 | "price": "mf:microformats2#p-price", 67 | "rating": "mf:microformats2#p-rating", 68 | "rev": "mf:microformats2#dt-rev", 69 | "review": "mf:microformats2#p-review", 70 | "reviewed": "mf:microformats2#dt-reviewed", 71 | "reviewer": "mf:microformats2#p-reviewer", 72 | "role": "mf:microformats2#p-role", 73 | "skill": "mf:microformats2#p-skill", 74 | "sort-string": "mf:microformats2#p-sort-string", 75 | "video": "mf:microformats2#u-video", 76 | "votes": "mf:microformats2#p-votes", 77 | "worst": "mf:microformats2#p-worst", 78 | "yield": "mf:microformats2#p-yield", 79 | "publication": "mf:h-cite#publication", 80 | "accessed": "mf:h-cite#dt-accessed", 81 | "syndication": "mf:rel-syndication", 82 | "shortlink": "mf:rel-shortlink", 83 | "rsvp": "mf:rsvp", 84 | "sex": "mf:h-card#p-sex", 85 | "gender": "mf:h-card#p-gender-identity", 86 | 87 | "mf2ex": "mf:microformats2-experimental-properties#", 88 | "x-dietary-preference": "mf2ex:p-x-dietary-preference", 89 | "x-pronoun-nominative": "mf2ex:p-x-pronoun-nominative", 90 | "x-pronoun-oblique": "mf2ex:p-x-pronoun-oblique", 91 | "x-pronoun-possessive": "mf2ex:p-x-pronoun-posessive", 92 | "x-sexual-preference": "mf2ex:p-x-sexual-preference", 93 | "x-username": "mf2ex:h-x-username", 94 | 95 | "comment": "iwc:comments", 96 | "like-of": "iwc:like", 97 | "repost-of": "iwc:repost", 98 | "invitee": "iwc:invite" 99 | } 100 | ] 101 | } 102 | -------------------------------------------------------------------------------- /publish/2016-07-28-FPWD/index-src.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JF2 Post Serialization Format 5 | 6 | 8 | 72 | 73 | 74 | 75 | 76 |
77 |

78 | This document describes a JSON serialization format to describe simple 79 | streams of data as well as single objects of data for data transfer and 80 | processing. 81 |

82 |
83 | 84 |
85 |
86 | 87 |
88 |

Author's Note

89 |

90 | This document is an attempt to unify various simplified versions of the 91 | Microformats-2 representative JSON format. As such, much of this document 92 | is likely to change as various implementations contribute input. 93 |

94 | 95 |

96 | This document is being drafted as a Non-Rec Track document, and is intended 97 | to end up a Working Group Note to describe one potential minimal method of representing 98 | a social web object. 99 |

100 |
101 | 102 |
103 |

Introduction

104 |

105 | JF2 is a JSON based document format that describes lists and single entries 106 | of information. The primary use case for JF2 is to create a JSON format for 107 | social web post objects to be processed directly by client software and by other 108 | servers. 109 |

110 |

111 | The name JF2 comes from its origins in being a direct parsed format of 112 | Microformats-2 (MF2) data from html. Rather than defining a new vocabulary, 113 | JF2 uses the vocabulary defined by [[microformats2]] 114 | however any other suitable vocabulary. 115 |

116 |

117 | It is relevant to note that JF2 objects have been most useful as programming objects, not 118 | as a serialization format. This document using javascript object notation for serializing 119 | to explain structure, but any language will have its own native structure in which JF2 would 120 | be stored. 121 |

122 |
123 | 124 |
125 |

Use Cases

126 |

127 | JF2 has evolved as a result of a variety of use-cases for different implementations 128 | exploring ways to simplify their existing use of canonical parsed microformats2 JSON 129 | output. All of these use cases in particular are simply to have a single format for 130 | the storage and use of social web objects. 131 |

132 |

133 | Webmention.io is a service that provides webmention 134 | processing and verification on behalf of other sites. It uses a simple JSON object to 135 | transfer a processed webmention back to the client site's javascript for display. The 136 | attributes are intended to be processed only by the client site's javascript code, not 137 | by anything else. 138 |

139 |

140 | mf2util is a utility library that provides a layer on top 141 | of microformats processing. The library returns only a simple JSON object and strips 142 | off any unnecessary information leaving the library user only the most essential information. 143 |

144 |

145 | Various services (xray, unmung, socialstreams) 146 | provide conversion from microformats pages in to JF2 for quick inspection to 147 | validate proper semantics. 148 |

149 |
150 | 151 |
152 |

Syntax

153 |

154 | JF2 consists of JSON objects which are defined by a type property that will specify 155 | the vocabulary of the object. Properties are attached to these objects which will contain 156 | either a single string, a single object, an array of strings, or an array of objects. 157 | Arrays that have only a single item SHOULD be condensed into only the single containing item. 158 | Any property of an object MAY be a single item or an array of items except for reserved words 159 | defined below. 160 |

161 |
162 |

Reserved Keywords

163 |

164 | The following keywords are reserved and cannot be used as property names in vocabularies. 165 |

166 | 195 |
196 | 197 |
198 |

Posts

199 | 200 |
201 |

Post Objects

202 |

203 | A post is composed of a "type" property, and one or more additional properties that describe the post. 204 |

205 |

206 | The "type" property has a value that describes the vocabulary of this post. Common values include "entry", "card", etc. See Microformats 2 vocabularies for the full list when using a microformats based vocabulary. 207 |

208 |

209 | Any additional properties in the post object are considered part of the post's vocabulary. 210 |

211 | 212 |
213 |
214 |

Post Properties

215 |

216 | The list of valid post properties is defined by the vocabularies. This allows new vocabularies to be developed outside the development of the syntax. 217 |

218 |

219 | Most values will be strings. If a property (such as `author` for example) references another 220 | object, it may be serialized in two ways: as an object serialized as the property value or as 221 | the unique identifier or URL where the object can be found. See Using References 222 |

223 |

224 | Values may also be arrays if the vocabulary allows for multiple values of the property. 225 |

226 | 227 |
228 |
229 |

Example Post

230 | 231 |
232 |             {
233 |               "type": "entry",
234 |               "published": "2015-10-20T15:49:00-0700",
235 |               "url": "http://example.com/post/fsjeuu8372",
236 |               "author": {
237 |                 "type": "card",
238 |                 "name": "Alice",
239 |                 "url": "http://alice.example.com",
240 |                 "photo": "http://alice.example.com/photo.jpg"
241 |               },
242 |               "name": "Hello World",
243 |               "content": "This is a blog post",
244 |               "category": "Posts"
245 |             }
246 |           
247 | 248 |
249 |             {
250 |               "type": "entry",
251 |               "published": "2015-10-20T15:49:00-0700",
252 |               "url": "http://example.com/like/r23eugi02c",
253 |               "author": {
254 |                 "type": "card",
255 |                 "name": "Alice",
256 |                 "url": "http://alice.example.com",
257 |                 "photo": "http://alice.example.com/photo.jpg"
258 |               },
259 |               "like-of": "http://bob.example.com/post/100",
260 |               "category": ["Likes", "Posts"]
261 |             }
262 |           
263 |
264 |
265 | 266 |
267 |

Author

268 |

269 | An author is represented by the h-card vocabulary, and consists of a name, photo URL, URL to the author profile, and [others](http://microformats.org/wiki/h-card). This is represented by the following JSON. 270 |

271 | 272 |
273 |           {
274 |             "type": "card",
275 |             "name": "Aaron Parecki",
276 |             "photo": "http://aaronparecki.com/photo.jpg",
277 |             "url": "http://aaronparecki.com/"
278 |           }
279 |         
280 |
281 | 282 |
283 |

HTML Content

284 |

285 | By default, any string value should be interpreted as literal plaintext. This means when displaying a string in an HTML page, it must be HTML escaped. 286 |

287 | 288 |

289 | If the value of a property is to be interpreted as HTML, it MUST be enclosed in an object noting its content-type as follows. 290 |

291 | 292 |
293 |           {
294 |             "type": "entry",
295 |             "content": {
296 |               "content-type": "text/html",
297 |               "value": "<b>Hello World</b>"
298 |             }
299 |           }
300 |         
301 | 302 |
303 |

Multiple URLs for video/audio/picture

304 |

305 | Since HTML video/audio/picture tags may have multiple URLs, we need a way to convey this information in the JSON representation. 306 |

307 | 308 |
309 |             <div class="h-entry">
310 |               <video class="u-video" width="640" height="360" preload controls>
311 |                 <source src="sample_h264.mov" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"' />
312 |                 <source src="sample_ogg.ogv" type='video/ogg; codecs="theora, vorbis"' />
313 |                 <source src="sample_webm.webm" type='video/webm; codecs="vp8, vorbis"' />
314 |               </video>
315 |             </div>
316 |           
317 | 318 |
319 |             {
320 |               "type": "entry",
321 |               "video": [
322 |                 {
323 |                   "content-type": "video/mp4",
324 |                   "url": "sample_h264.mov"
325 |                 },
326 |                 {
327 |                   "content-type": "video/ogg",
328 |                   "url": "sample_ogg.ogg"
329 |                 },
330 |                 {
331 |                   "content-type": "video/webm",
332 |                   "url": "sample_webm.webm"
333 |                 }
334 |               ]
335 |             }
336 |           
337 | 338 |
339 |
340 |
341 | 342 |
343 |

Using References

344 |

345 | The purpose of the `references` property is to exclude any non-authoritative 346 | data from the defined object. To do this, non-authoritative data is moved so 347 | that implementations looking to process only authoritative data may simply ignore 348 | the references property and fetch any data that would be contained there from its 349 | authoritative source. 350 |

351 |

352 | If a property is a reference to an object that is defined authoritatively in 353 | some other location, the `references` property SHOULD be used. The property 354 | SHOULD contain only the unique identifier or URL where the authoritative data 355 | may be found. In the references object, the URL or unique identifier MAY 356 | be entered as the key field and a serialization of the referenced object MAY 357 | be provided. This serialization of the referenced object MAY be incomplete 358 | so as to provide only necessary data. 359 |

360 |

361 | Parsing implementations SHOULD fetch data from the authoritative source instead of using 362 | the references object. 363 |

364 |
365 |

Example of References

366 |
367 |             {
368 |               "type": "entry",
369 |               "published": "2015-10-20T15:49:00-0700",
370 |               "url": "http://example.com/post/fsjeuu8372",
371 |               "author": "http://alice.example.com",
372 |               "name": "Hello World",
373 |               "content": "This is a blog post",
374 |               "category": "Posts",
375 |               "references": {
376 |                 "http://alice.example.com": {
377 |                   "type": "card",
378 |                   "name": "Alice",
379 |                   "url": "http://alice.example.com",
380 |                   "photo": "http://alice.example.com/photo.jpg"
381 |                 }
382 |               }
383 |             }
384 |           
385 | 386 |
387 |             {
388 |               "type": "entry",
389 |               "published": "2015-10-20T15:49:00-0700",
390 |               "url": "http://example.com/like/r23eugi02c",
391 |               "author": {
392 |                 "type": "card",
393 |                 "name": "Alice",
394 |                 "url": "http://alice.example.com",
395 |                 "photo": "http://alice.example.com/photo.jpg"
396 |               },
397 |               "like-of": "http://bob.example.com/post/100",
398 |               "category": ["Likes", "Posts"],
399 |               "references": {
400 |                 "http://bob.example.com/post/100": {
401 |                   "type": "entry",
402 |                   "published": "2015-10-18T12:33:00-0700",
403 |                   "url": "http://bob.example.com/post/100",
404 |                   "author": "http://bob.example.com",
405 |                   "name": "My First Post",
406 |                   "content": "This is my first post on my new blog, I hope you like it"
407 |                 },
408 |                 "http://bob.example.com": {
409 |                   "type": "card",
410 |                   "name": "Bob",
411 |                   "url": "http://bob.example.com",
412 |                   "photo": "http://bob.example.com/mypicture.jpg"
413 |                 }
414 |               }
415 |             }
416 |           
417 |
418 | 419 |
420 |
421 |

Collections

422 | 423 |

424 | Posts can live inside of collections. A collection may be a home page feed, or a feed of other posts such as a list of contacts, a list of things someone has liked, etc. There is no requirement that all posts in a collection need to be of the same type. 425 |

426 | 427 |

428 | The collection may also have its own properties such as "name" or "author". 429 |

430 | 431 |
432 |         {
433 |           "type": "feed",
434 |           "url": "http://alice.example.com/collectionurl",
435 |           "name": "Alice's Home Page",
436 |           "author": {
437 |             "type": "card",
438 |             "name": "Alice",
439 |             "url": "http://alice.example.com",
440 |             "photo": "http://alice.example.com/photo"
441 |           },
442 |           "children": [
443 |             { ... },
444 |             { ... }
445 |           ]
446 |         }
447 |       
448 | 449 |
450 |

Multiple items on a page

451 |

452 | If an HTML page contains multiple top-level items, (most commonly found when a page contains a list of [[h-entry]] objects), the parser creates an implicit top-level collection with no properties. 453 |

454 | 455 |
456 |           {
457 |             "children": [
458 |               { ... },
459 |               { ... }
460 |             ]
461 |           }
462 |         
463 | 464 |
465 |
466 | 467 |
468 |

Deriving the Syntax

469 |

470 | This syntax is derived from HTML with Microformats-2, converted to JSON [[microformats2-parsing]] converted to a simplified JSON. The examples below illustrate the process. 471 |

472 | 473 |
474 |

Deriving a Note

475 | 476 |

HTML + Microformats

477 |
478 |           <article class="h-entry">
479 |             <h1 class="p-name">Hello World</h1>
480 |             <p>Published by <a class="p-author h-card" href="http://example.com/">A. Developer</a>
481 |                on <a href="http://example.com/2015/10/21" class="u-url"><time class="dt-published" datetime="2015-10-21T12:00:00-0700">October 21<sup>st</sup>, 2015</time></a>
482 |            
483 |             <p class="p-summary">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus imperdiet ultrices pulvinar.</p>
484 |            
485 |             <div class="e-content"><p>Donec dapibus enim lacus, <i>a vehicula magna bibendum non</i>. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in.</p></div>
486 |           </article>
487 |         
488 | 489 |

Parsed Microformats JSON

490 | 491 |
492 |         {
493 |           "items": [
494 |             {
495 |               "type": [
496 |                 "h-entry"
497 |               ],
498 |               "properties": {
499 |                 "author": [
500 |                   {
501 |                     "type": [
502 |                       "h-card"
503 |                     ],
504 |                     "properties": {
505 |                       "name": [
506 |                         "A. Developer"
507 |                       ],
508 |                       "url": [
509 |                         "http:\/\/example.com\/"
510 |                       ]
511 |                     },
512 |                     "value": "A. Developer"
513 |                   }
514 |                 ],
515 |                 "name": [
516 |                   "Hello World"
517 |                 ],
518 |                 "summary": [
519 |                   "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus imperdiet ultrices pulvinar."
520 |                 ],
521 |                 "url": [
522 |                   "http:\/\/example.com\/2015\/10\/21"
523 |                 ],
524 |                 "published": [
525 |                   "2015-10-21T12:00:00-0700"
526 |                 ],
527 |                 "content": [
528 |                   {
529 |                     "html": "<p>Donec dapibus enim lacus, <i>a vehicula magna bibendum non</i>. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in.</p>",
530 |                     "value": "Donec dapibus enim lacus, a vehicula magna bibendum non. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in."
531 |                   }
532 |                 ]
533 |               }
534 |             }
535 |           ]
536 |         }
537 |         
538 | 539 |

Simplified JSON

540 | 541 |
542 |         {
543 |           "type": "entry",
544 |           "author": {
545 |             "type": "card",
546 |             "url": "http://example.com",
547 |             "name": "A. Developer"
548 |           },
549 |           "url": "http://example.com/2015/10/21",
550 |           "published": "2015-10-21T12:00:00-0700",
551 |           "name": "Hello World",
552 |           "summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus imperdiet ultrices pulvinar.",
553 |           "content": {
554 |             "value": "<p>Donec dapibus enim lacus, <i>a vehicula magna bibendum non</i>. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in.</p>",
555 |             "content-type": "text/html"
556 |           }
557 |         }
558 | 
559 |         
560 | 561 |
562 |
563 |

JSON-LD Consideration

564 |

565 | JF2 documents all have an implicit @context field which is optional. This @context can be found at 566 | http://www.w3.org/ns/jf2 567 | and is provided only to make conversion to [[JSON-LD]] format possible. Most JF2 will process 568 | fine in JSON-LD systems, however, this support is not guaranteed and those wishing to 569 | use JF2 in JSON-LD may need to modify serialization slightly. 570 |

571 |
572 |
573 |
574 |

Implementations

575 |

576 | The following is a list of currently known implementations of JF2-like formats and their current differences to this document 577 |

578 | 579 |
580 |

Webmention.io

581 |

582 | Webmention.io is a hosted service for handling webmentions. 583 | Webmentions are accessed from an API that that returns a set of objects in JSON format similar to JF2. 584 | The key difference is the inclusion of some additional data wrapping the objects 585 | (source, verified, verified_data, etc) and all wrapped in a "links" array. 586 | Code available at https://github.com/aaronpk/webmention.io 587 |

588 |
589 | 590 |
591 |

XRay

592 |

593 | X-Ray is a tool to return structured data from any URL. 594 | Code available at https://github.com/aaronpk/XRay 595 |

596 |
597 | 598 |
599 |

Unmung

600 |

601 | Unmung is a tool for converting various formats. 602 | Code available at https://github.com/kevinmarks/unmung 603 |

604 |
605 | 606 | 607 |
608 |

SocialStreams

609 |

610 | SocialStreams is a translation service for converting social formats. 611 | Code available at https://github.com/dissolve/socialstream 612 |

613 |
614 | 615 |
616 |

mf2util

617 |

618 | Mf2util is a utility library for interpreting microformats2. 619 | Code available at https://github.com/kylewm/mf2util 620 |

621 |
622 | 623 | 624 | 625 |
626 |
627 |

Acknowledgements

628 |

629 | The authors wish to thank the Microformats, IndieWebCamp, Pump.io, and Activity Streams communities for their continued work 630 | in building the social web and helping define standards such as this one. This includes, but is certainly not limited to, 631 | Aaron Parecki, 632 | Benjamin Goering, 633 | Christopher Webber, 634 | Dave Wilkinson II, 635 | James Snell, 636 | Kyle Mahan, 637 | and 638 | Tantek Çelik. 639 |

640 |
641 | 642 | 643 | -------------------------------------------------------------------------------- /publish/2017-06-12-WD/index-src.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JF2 Post Serialization Format 5 | 6 | 8 | 95 | 96 | 97 | 98 | 99 |
100 |

101 | This document describes a JSON serialization format to describe simple 102 | streams of data as well as single objects of data for data transfer and 103 | processing. 104 |

105 |
106 | 107 |
108 |
109 | 110 |
111 |

Author's Note

112 |

113 | This document is an attempt to unify various simplified versions of the 114 | Microformats-2 representative JSON format. As such, much of this document 115 | is likely to change as various implementations contribute input. 116 |

117 |
118 | 119 |
120 |

Introduction

121 |

122 | JF2 is a JSON based document format that describes lists and single entries 123 | of information. The primary use case for JF2 is to create a JSON format for 124 | social web post objects to be processed directly by client software and by other 125 | servers. 126 |

127 |

128 | The name JF2 comes from its origins in being a direct parsed format of 129 | Microformats-2 (MF2) data from html. Rather than defining a new vocabulary, 130 | JF2 uses the vocabulary defined by [[microformats2]] 131 | however any other suitable vocabulary could be used. JF2 is vocabulary 132 | independent except for its profiles which are fully vocabulary aware. 133 |

134 |
135 | 136 |
137 |

Use Cases

138 |

139 | JF2 has evolved as a result of a variety of use-cases for different implementations 140 | exploring ways to simplify their existing use of canonical parsed microformats2 JSON 141 | output. All of these use cases in particular are simply to have a single format for 142 | the storage and use of social web objects. 143 |

144 |

145 | Webmention.io is a service that provides webmention 146 | processing and verification on behalf of other sites. It uses a simple JSON object to 147 | transfer a processed webmention back to the client site's javascript for display. The 148 | attributes are intended to be processed only by the client site's javascript code, not 149 | by anything else. 150 |

151 |

152 | mf2util is a utility library that provides a layer on top 153 | of microformats processing. The library returns only a simple JSON object and strips 154 | off any unnecessary information leaving the library user only the most essential information. 155 |

156 |

157 | Various services (xray, unmung, socialstreams) 158 | provide conversion from microformats pages in to JF2 for quick inspection to 159 | validate proper semantics. 160 |

161 |
162 | 163 |
164 |

Syntax

165 |

166 | JF2 consists of JSON objects which are defined by a type property that will specify 167 | the vocabulary of the object. Properties are attached to these objects which will contain 168 | either a single string, a single object, an array of strings, or an array of objects. 169 | Arrays that have only a single item SHOULD be condensed into only the single containing item. 170 | Any property of an object MAY be a single item or an array of items except for reserved words 171 | defined below. 172 |

173 |
174 |

Reserved Keywords

175 |

176 | The following keywords are reserved and cannot be used as property names in vocabularies. 177 |

178 | 211 |
212 | 213 |
214 |

Posts

215 | 216 |
217 |

Post Objects

218 |

219 | A post is composed of a "type" property, and one or more additional properties that describe the post. 220 |

221 |

222 | The "type" property has a value that describes the vocabulary of this post. Common values include "entry", "card", etc. See Microformats 2 vocabularies for the full list when using a microformats based vocabulary. 223 |

224 |

225 | Any additional properties in the post object are considered part of the post's vocabulary. 226 |

227 | 228 |
229 |
230 |

Post Properties

231 |

232 | The list of valid post properties is defined by the vocabularies. This allows new vocabularies to be developed outside the development of the syntax. 233 |

234 |

235 | Most values will be strings. If a property (such as `author` for example) references another 236 | object, it may be serialized in two ways: as an object serialized as the property value or as 237 | the unique identifier or URL where the object can be found. See Using References 238 |

239 |

240 | Values may also be arrays if the vocabulary allows for multiple values of the property. 241 |

242 | 243 |
244 |
245 |

Example Post

246 | 247 |
 248 |             {
 249 |               "type": "entry",
 250 |               "published": "2015-10-20T15:49:00-0700",
 251 |               "url": "http://example.com/post/fsjeuu8372",
 252 |               "author": {
 253 |                 "type": "card",
 254 |                 "name": "Alice",
 255 |                 "url": "http://alice.example.com",
 256 |                 "photo": "http://alice.example.com/photo.jpg"
 257 |               },
 258 |               "name": "Hello World",
 259 |               "content": "This is a blog post",
 260 |               "category": "Posts"
 261 |             }
 262 |           
263 | 264 |
 265 |             {
 266 |               "type": "entry",
 267 |               "published": "2015-10-20T15:49:00-0700",
 268 |               "url": "http://example.com/like/r23eugi02c",
 269 |               "author": {
 270 |                 "type": "card",
 271 |                 "name": "Alice",
 272 |                 "url": "http://alice.example.com",
 273 |                 "photo": "http://alice.example.com/photo.jpg"
 274 |               },
 275 |               "like-of": "http://bob.example.com/post/100",
 276 |               "category": ["Likes", "Posts"]
 277 |             }
 278 |           
279 |
280 |
281 | 282 |
283 |

Author

284 |

285 | An author is represented by the h-card vocabulary, and consists of a name, photo URL, URL to the author profile, and [others](http://microformats.org/wiki/h-card). This is represented by the following JSON. 286 |

287 | 288 |
 289 |           {
 290 |             "type": "card",
 291 |             "name": "Aaron Parecki",
 292 |             "photo": "http://aaronparecki.com/photo.jpg",
 293 |             "url": "http://aaronparecki.com/"
 294 |           }
 295 |         
296 |
297 | 298 |
299 |

HTML Content

300 |

301 | By default, any string value should be interpreted as literal plaintext. 302 | This means when displaying a string in an HTML page, it must be HTML escaped. 303 |

304 | 305 |

306 | If the value of a property is to be interpreted as HTML, it MUST be enclosed in 307 | an object and placed under the "html" property as follows. If a plaintext version 308 | is also available, that is placed under the "text" property. 309 |

310 | 311 |
 312 |           {
 313 |             "type": "entry",
 314 |             "content": {
 315 |               "html": "<p>Hello World</p>"
 316 |               "text": "Hello World"
 317 |             }
 318 |           }
 319 |         
320 | 321 |
322 |

Multiple URLs for video/audio/picture

323 |

324 | Since HTML video/audio/picture tags may have multiple URLs, we need a way to convey this information in the JSON representation. 325 |

326 | 327 |
 328 |             <div class="h-entry">
 329 |               <video class="u-video" width="640" height="360" preload controls>
 330 |                 <source src="sample_h264.mov" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"' />
 331 |                 <source src="sample_ogg.ogv" type='video/ogg; codecs="theora, vorbis"' />
 332 |                 <source src="sample_webm.webm" type='video/webm; codecs="vp8, vorbis"' />
 333 |               </video>
 334 |             </div>
 335 |           
336 | 337 |
 338 |             {
 339 |               "type": "entry",
 340 |               "video": [
 341 |                 {
 342 |                   "content-type": "video/mp4",
 343 |                   "url": "sample_h264.mov"
 344 |                 },
 345 |                 {
 346 |                   "content-type": "video/ogg",
 347 |                   "url": "sample_ogg.ogg"
 348 |                 },
 349 |                 {
 350 |                   "content-type": "video/webm",
 351 |                   "url": "sample_webm.webm"
 352 |                 }
 353 |               ]
 354 |             }
 355 |           
356 | 357 |
358 |
359 |
360 | 361 |
362 |

Using References

363 |

364 | The purpose of the `references` property is to exclude any non-authoritative 365 | data from the defined object. To do this, non-authoritative data is moved so 366 | that implementations looking to process only authoritative data may simply ignore 367 | the references property and fetch any data that would be contained there from its 368 | authoritative source. 369 |

370 |

371 | If a property is a reference to an object that is defined authoritatively in 372 | some other location, the `references` property SHOULD be used. The property 373 | SHOULD contain only the unique identifier or URL where the authoritative data 374 | may be found. In the references object, the URL or unique identifier MAY 375 | be entered as the key field and a serialization of the referenced object MAY 376 | be provided. This serialization of the referenced object MAY be incomplete 377 | so as to provide only necessary data. 378 |

379 |

380 | Parsing implementations SHOULD fetch data from the authoritative source instead of using 381 | the references object. 382 |

383 |
384 |

Example of References

385 |
 386 |             {
 387 |               "type": "entry",
 388 |               "published": "2015-10-20T15:49:00-0700",
 389 |               "url": "http://example.com/post/fsjeuu8372",
 390 |               "author": "http://alice.example.com",
 391 |               "name": "Hello World",
 392 |               "content": "This is a blog post",
 393 |               "category": "Posts",
 394 |               "references": {
 395 |                 "http://alice.example.com": {
 396 |                   "type": "card",
 397 |                   "name": "Alice",
 398 |                   "url": "http://alice.example.com",
 399 |                   "photo": "http://alice.example.com/photo.jpg"
 400 |                 }
 401 |               }
 402 |             }
 403 |           
404 | 405 |
 406 |             {
 407 |               "type": "entry",
 408 |               "published": "2015-10-20T15:49:00-0700",
 409 |               "url": "http://example.com/like/r23eugi02c",
 410 |               "author": {
 411 |                 "type": "card",
 412 |                 "name": "Alice",
 413 |                 "url": "http://alice.example.com",
 414 |                 "photo": "http://alice.example.com/photo.jpg"
 415 |               },
 416 |               "like-of": "http://bob.example.com/post/100",
 417 |               "category": ["Likes", "Posts"],
 418 |               "references": {
 419 |                 "http://bob.example.com/post/100": {
 420 |                   "type": "entry",
 421 |                   "published": "2015-10-18T12:33:00-0700",
 422 |                   "url": "http://bob.example.com/post/100",
 423 |                   "author": "http://bob.example.com",
 424 |                   "name": "My First Post",
 425 |                   "content": "This is my first post on my new blog, I hope you like it"
 426 |                 },
 427 |                 "http://bob.example.com": {
 428 |                   "type": "card",
 429 |                   "name": "Bob",
 430 |                   "url": "http://bob.example.com",
 431 |                   "photo": "http://bob.example.com/mypicture.jpg"
 432 |                 }
 433 |               }
 434 |             }
 435 |           
436 |
437 | 438 |
439 |
440 |

Collections

441 | 442 |

443 | Posts can live inside of collections. A collection may be a home page feed, or a feed of other posts such as a list of contacts, a list of things someone has liked, etc. There is no requirement that all posts in a collection need to be of the same type. 444 |

445 | 446 |

447 | The collection may also have its own properties such as "name" or "author". 448 |

449 | 450 |
 451 |         {
 452 |           "type": "feed",
 453 |           "url": "http://alice.example.com/collectionurl",
 454 |           "name": "Alice's Home Page",
 455 |           "author": {
 456 |             "type": "card",
 457 |             "name": "Alice",
 458 |             "url": "http://alice.example.com",
 459 |             "photo": "http://alice.example.com/photo"
 460 |           },
 461 |           "children": [
 462 |             { ... },
 463 |             { ... }
 464 |           ]
 465 |         }
 466 |       
467 | 468 |
469 |

Multiple items on a page

470 |

471 | If an HTML page contains multiple top-level items, (most commonly found when a page contains a list of [[h-entry]] objects), the parser creates an implicit top-level collection with no properties. 472 |

473 | 474 |
 475 |           {
 476 |             "children": [
 477 |               { ... },
 478 |               { ... }
 479 |             ]
 480 |           }
 481 |         
482 | 483 |
484 |
485 | 486 |
487 |

Deriving the Syntax

488 |

489 | This syntax is derived from HTML with Microformats-2, converted to JSON [[microformats2-parsing]] converted to a simplified JSON. The examples below illustrate the process. 490 |

491 | 492 |
493 |

Deriving a Note

494 | 495 |

HTML + Microformats

496 |
 497 |           <article class="h-entry">
 498 |             <h1 class="p-name">Hello World</h1>
 499 |             <p>Published by <a class="p-author h-card" href="http://example.com/">A. Developer</a>
 500 |                on <a href="http://example.com/2015/10/21" class="u-url"><time class="dt-published" datetime="2015-10-21T12:00:00-0700">October 21<sup>st</sup>, 2015</time></a>
 501 |            
 502 |             <p class="p-summary">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus imperdiet ultrices pulvinar.</p>
 503 |            
 504 |             <div class="e-content"><p>Donec dapibus enim lacus, <i>a vehicula magna bibendum non</i>. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in.</p></div>
 505 |           </article>
 506 |         
507 | 508 |

Parsed Microformats JSON

509 | 510 |
 511 |         {
 512 |           "items": [
 513 |             {
 514 |               "type": [
 515 |                 "h-entry"
 516 |               ],
 517 |               "properties": {
 518 |                 "author": [
 519 |                   {
 520 |                     "type": [
 521 |                       "h-card"
 522 |                     ],
 523 |                     "properties": {
 524 |                       "name": [
 525 |                         "A. Developer"
 526 |                       ],
 527 |                       "url": [
 528 |                         "http:\/\/example.com\/"
 529 |                       ]
 530 |                     },
 531 |                     "value": "A. Developer"
 532 |                   }
 533 |                 ],
 534 |                 "name": [
 535 |                   "Hello World"
 536 |                 ],
 537 |                 "summary": [
 538 |                   "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus imperdiet ultrices pulvinar."
 539 |                 ],
 540 |                 "url": [
 541 |                   "http:\/\/example.com\/2015\/10\/21"
 542 |                 ],
 543 |                 "published": [
 544 |                   "2015-10-21T12:00:00-0700"
 545 |                 ],
 546 |                 "content": [
 547 |                   {
 548 |                     "html": "<p>Donec dapibus enim lacus, <i>a vehicula magna bibendum non</i>. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in.</p>",
 549 |                     "value": "Donec dapibus enim lacus, a vehicula magna bibendum non. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in."
 550 |                   }
 551 |                 ]
 552 |               }
 553 |             }
 554 |           ]
 555 |         }
 556 |         
557 | 558 |

Simplified JSON

559 | 560 |
 561 |         {
 562 |           "type": "entry",
 563 |           "author": {
 564 |             "type": "card",
 565 |             "url": "http://example.com",
 566 |             "name": "A. Developer"
 567 |           },
 568 |           "url": "http://example.com/2015/10/21",
 569 |           "published": "2015-10-21T12:00:00-0700",
 570 |           "name": "Hello World",
 571 |           "summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus imperdiet ultrices pulvinar.",
 572 |           "content": {
 573 |             "value": "<p>Donec dapibus enim lacus, <i>a vehicula magna bibendum non</i>. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in.</p>",
 574 |             "text": "Donec dapibus enim lacus, a vehicula magna bibendum non. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in."
 575 |           }
 576 |         }
 577 | 
 578 |         
579 | 580 |
581 |
582 |

JSON-LD Consideration

583 |

584 | JF2 documents all have an implicit @context field which is optional. This @context can be found at 585 | http://www.w3.org/ns/jf2 586 | and is provided only to make conversion to [[JSON-LD]] format possible. Most JF2 will process 587 | fine in JSON-LD systems, however, this support is not guaranteed and those wishing to 588 | use JF2 in JSON-LD may need to modify serialization slightly. 589 |

590 |
591 |
592 |
593 |

Profiles

594 |
595 |

Intro

596 |

597 | JF2 on its own is intentionally vocabulary independent, that is, any value may contain a single value, a 598 | set of values, or a structure and there are no requirements that fields be present. While this keeps 599 | the fidelity of the original [[microformats2]] encoded html, some consuming code can be simplified by 600 | adding vocabulary requirements. This creates the need for vocabulary aware profiles of JF2, where 601 | the profile adds additional constraints on the fields present. 602 |

603 |
604 |
605 |

JF2 Feed

606 | 607 |
608 |

Introduction

609 |

610 | XML formats have heavily dominated serializations of social activity feeds for blogs and news feeds for years. 611 | Several attempts have been created over the years to attempt to bring these to a JSON format, most recently 612 | [[jsonfeed-v1]], which JF2 Feed gets the majority of its requirements from. JF2 Feed is an attempt to bring 613 | some of the advantages of these vocabulary aware specifications under a standard vocabulary from 614 | [[microformats2]]. 615 |

616 | 617 |

618 | Fields described below have additional requirements to be considered a valid JF2 Feed, however they may contain 619 | any number of additional fields from [[h-feed]], [[h-entry]], and [[h-card]]. The entries below are codified as 620 | they are likely the most useful for feed readers and additional data is expected to be fetchable at the entry URL, 621 | preferrably though valid [[microformats2]] markup. 622 |

623 |
624 | 625 |
626 |

Required Fields and Required Formats

627 |

628 | The following fields have additional constraints to be a valid JF2 Feed that only apply 629 | to the top level feed object, its child entry object, and any properties of them. 630 |

631 | 632 |
    633 |
  • type MUST be defined with a value of "feed" on the top level entry. 634 | All other sub-object items MUST NOT have a type value of "feed". All direct children 635 | of the top level feed object MUST have a value of "entry". 636 |
  • 637 |
  • name MUST be defined on the top level "feed" object. This value MUST 638 | be a single string value. Any direct children of the top level item SHOULD have this 639 | property and if present MUST be a single string value. 640 |
  • 641 |
  • url SHOULD be defined on the top level "feed" object. This value MUST 642 | be a single string value and is expected to contain the URL of the data which this 643 | JF2 Feed describes. Additionally, if present in the author property object, or any direct 644 | child entry object, it MUST contain a single string value only and that value must be a [[URL]]. 645 |
  • 646 |
  • photo MUST be a single string value if present on the top level feed 647 | object, its author property object, or any direct child entry object and MUST be a valid [[URL]]. 648 |
  • 649 |
  • uid MUST be present on any entry object which is a direct child of the 650 | top level feed object. This property MUST be a single string value and MUST uniquely 651 | identify this entry object. This MAY be a duplicate of the entry's url property. 652 |
  • 653 |
  • published SHOULD be present on any entry object which is a direct 654 | child of the top level feed object. If present, this property MUST be a single string 655 | value and MUST be formatted as specified by [[ISO8601]]. 656 |
  • 657 |
  • updated MAY be present on any entry object which is a direct 658 | child of the top level feed object. If present, this property MUST be a single string 659 | value and MUST be formatted as specified by [[ISO8601]]. 660 |
  • 661 |
  • category MAY be present on any entry object which is a direct 662 | child of the top level feed object. If present, this property MUST be an array of 663 | string values. 664 |
  • 665 |
  • author MAY be present on the top level feed or second level entry 666 | objects. If present, it MUST be an object and it MUST contain at least a name, url, 667 | or photo property. 668 |
  • 669 |
  • content MAY be present on any entry object which is a direct 670 | child of the top level feed object. If present, this property MUST be an object as 671 | described in the html content regardless if only a text/plain 672 | version is available. 673 |
  • 674 |
  • summary MAY be present on any second level entry object. If present, 675 | it MUST be a single string. 676 |
  • 677 |
  • video or audio MAY be present on any second level entry object. If present, 678 | it MUST be an object as described in multiple urls section with at least 679 | a 'url' property which MUST be a single string and a valid [[URL]]. 680 |
  • 681 | 682 |
683 |

684 | The format of published and updated fields may change from [[ISO8601]] to [[RFC3339]] or use [[microformats2]]'s more liberal date field. Please discuss on github issues 685 |

686 | 687 |
688 | 689 |
690 |

Discovery

691 |

692 | The JF2 Feed for a page may be published as HTTP Link header [[RFC5988]], or as an HTML 693 | <link> or <a> tag element with the following attributes. 694 |

rel="alternate" type="application/jf2feed+json" href="http://www.example.com/jf2feed.json" 
695 |

696 |

697 | Discovery via <link> and/or <a> is being considered for removal, please leave your feedback on this via github issues. 698 |

699 |
700 |
701 |

JF2 Feed Example

702 | 703 |
 704 |             {
 705 |                 "type": "feed",
 706 |                 "url": "https://example.org/myfeed.html",
 707 |                 "name": "Brent Simmons’s Microblog",
 708 |                 "author": {
 709 |                     "type": "card",
 710 |                     "name": "Brent Simmons",
 711 |                     "url": "http://example.org/",
 712 |                     "photo": "https://example.org/avatar.png"
 713 |                 },
 714 |                 "children": [
 715 |                     {
 716 |                         "type": "entry",
 717 |                         "uid": "https://example.org/2347259",
 718 |                         "url": "https://example.org/2347259",
 719 |                         "content": {
 720 |                           "text": "Cats are neat. \n\nhttps://example.org/cats"
 721 |                         },
 722 |                         "published": "2016-02-09T14:22:00-07:00"
 723 |                     }
 724 |                 ]
 725 |             }
 726 | 
 727 |           
728 |
729 |
730 |

JSON Feed to JF2 conversion

731 | 732 |

733 | As it is not sane to do this for every feed serialization currently available, 734 | this section is likely to only be temporary in the spec and may be moved to a 735 | more permanant home elsewhere. 736 |

737 | 738 |

739 | JF2 Feed was based heavily on [[jsonfeed-v1]] and as such it is quite trivial to process a 740 | JSON Feed as a JF2 Feed with only minor changes. This section discusses the conversion that 741 | must be made for this process, most of which is simply conversion of properties to their 742 | [[microformats2]] equivalent. 743 |

744 | 745 |

746 | Conversion of the top level sturcture 747 |

748 |
    749 |
  • Drop or ignore the "version" property
  • 750 |
  • Create a property "type" with the value "feed"
  • 751 |
  • Rename the properties "title" to "name", "home_page_url" to "url", "icon" to "photo", and "description" to "summary"
  • 752 |
  • Modify the "author" property as described below
  • 753 |
  • Rename the "items" property to "children" and modify as described below
  • 754 |
755 | 756 |

757 | Conversion of the author property on the top level or on any lower level entry 758 |

759 |
    760 |
  • Rename the property 'avatar' to 'photo'
  • 761 |
762 | 763 |

764 | Conversion each entry in items 765 |

766 |
    767 |
  • Rename the 'id' property to 'uid'
  • 768 |
  • Convert content_html and content_text in to a single object under the property "content" with properties "html" and "text", leaving out either if not present, or the entire property if neither are present.
  • 769 |
  • Rename the properties 770 | "title" to "name", 771 | "image" to "photo", 772 | "date_published" to "published", 773 | "date_modified" to "updated", 774 | "tags" to "category", 775 | "banner_image" to "featured"
  • 776 |
  • Drop or ignore the 'external_url' property. If content is not set, it may be useful to set content['text'] to the value of 'external_url'
  • 777 |
  • Keep "summary" property the same
  • 778 |
  • 779 | For each entry in the "attachments" array, 780 |
      781 |
    1. Rename "mime-type" to "content-type".
    2. 782 |
    3. Keep the "url" property as is.
    4. 783 |
    5. If the "content-type" field begins with "video/", add the item to the "video" property array.
    6. 784 |
    7. If the "content-type" field begins with "photo/", add the item to the "photo" property array.
    8. 785 |
    786 | 787 |
  • 788 | 789 | 790 |
791 |

792 | It may be benefitial to drop any properties other than those mentioned to avoid conflicts with any future [[microformats2]] properties. 793 |

794 | 795 | 796 |
797 |
798 |
799 | 800 |
801 |

Extension

802 |

803 | JF2 MAY be extended by the [[microformats2]] extension mechanism. The 'x-*' properties created in this 804 | way MAY be present in any serialization of JF2. Parsers MUST NOT halt on any unknown properties they 805 | encounter. 806 |

807 |
808 | 809 |
810 |

Language And Internationalization

811 |

812 | In order to allow any language to be serialized in JF2, the 'lang' value can be set on any object to inform 813 | annotate the natural language of the text. Many often ask for control structures like directionality of text 814 | and multiple language serializations. Directionality of text can be accomplished with UTF-8 control characters 815 | which can be added to any of the values in the document. In addition, any content inside of 'html' properties 816 | can have any markup as defined by [[HTML5]]. 817 |

818 |

819 | Multiple serializations of the same text has not been seen to be needed in practice. 820 | The suggested path for accomplishing this with JF2 is to have these hosted at different locations. 821 | For example, it is common practice on websites to host an alternate language version of a site under a different 822 | directory structure for each language (such as /en/, /jp/, /fr/, etc). 823 |

824 |
825 | 826 |
827 |

Implementations

828 |

829 | The following is a list of currently known implementations of JF2-like formats and their current differences to this document 830 |

831 | 832 |
833 |

Webmention.io

834 |

835 | Webmention.io is a hosted service for handling webmentions. 836 | Webmentions are accessed from an API that that returns a set of objects in JSON format similar to JF2. 837 | The key difference is the inclusion of some additional data wrapping the objects 838 | (source, verified, verified_data, etc) and all wrapped in a "links" array. 839 | Code available at https://github.com/aaronpk/webmention.io 840 |

841 |
842 | 843 |
844 |

XRay

845 |

846 | X-Ray is a tool to return structured data from any URL. 847 | Code available at https://github.com/aaronpk/XRay 848 |

849 |
850 | 851 |
852 |

Unmung

853 |

854 | Unmung is a tool for converting various formats. 855 | Code available at https://github.com/kevinmarks/unmung 856 |

857 |
858 | 859 | 860 |
861 |

SocialStreams

862 |

863 | SocialStreams is a translation service for converting social formats. 864 | Code available at https://github.com/dissolve/socialstream 865 |

866 |
867 | 868 |
869 |

mf2util

870 |

871 | Mf2util is a utility library for interpreting microformats2. 872 | Code available at https://github.com/kylewm/mf2util 873 |

874 |
875 |
876 | 877 |
878 |

Privacy Considerations

879 | 880 |

881 | As with any serialization format, JF2 streams can potentially contain private and personally identifiable information. 882 | As such, producing and consuming implementations SHOULD take a default stance that all information in these documents are 883 | private and take steps to ensure the privacy of their users. This can include transmission only over secure connections, 884 | limiting access to streams, deletion of stored secure information, or any other steps that would apply to any private 885 | information. 886 |

887 |
888 | 889 |
890 |

Security Considerations

891 | 892 |

893 | Publisher and Consumers of any publicly accessible or third party JF2 streams SHOULD take reasonable preventative 894 | measures to ensure against malicious code embedded in the stream and filter out such material wherever possible. 895 |

896 | 897 |

898 | Publisher and Consumers of any publicly accessible or third party JF2 streams that contain HTML content SHOULD 899 | take additional steps to ensure that there is no malicious code such as cross-site scripting attacks contained 900 | in the HTML content. 901 |

902 | 903 |

904 | JF2 Streams are JSON Documents and are subject to the same security considerations described in [[!RFC7159]]. 905 |

906 | 907 |
908 | 909 | 910 | 911 |
912 | 913 |

IANA Considerations

914 | 915 |
916 |

The application/jf2feed+json Media Type

917 | 918 |

919 | This specification registers the application/jf2feed+json MIME Media Type specifically for identifying documents 920 | conforming to the JF2 format with the JF2 Feed profile. 921 |

922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 955 | 956 |
Type name: application
Subtype name: jf2feed+json
Required parameters: None
Optional parameters: None
Encoding considerations: 943 | Binary, as per [[RFC6839]], section 3.1; the charset parameter is not used and byte-order marks are not permitted, as per [[RFC7159]], sections 11 and 8.1 944 |
Security considerations: See [[RFC7159]] section 12 and the Security Considerations section of this specification.
Contact: 953 | Ben Roberts <ben@thatmustbe.me> 954 |
957 | 958 |
959 | 960 |
961 | 962 | 963 |
964 |

Change Log

965 |
966 |

Changes from 28 July 2016 FPWD to this version

967 | 968 |

This section lists changes from the 28 July 2016 FPWD to this version

969 | 970 | 982 | 983 |
984 |
985 | 986 |
987 |

Acknowledgements

988 |

989 | The authors wish to thank the Microformats, IndieWebCamp, Pump.io, and Activity Streams communities for their continued work 990 | in building the social web and helping define standards such as this one. This includes, but is certainly not limited to, 991 | Aaron Parecki, 992 | Benjamin Goering, 993 | Brent Simmons, 994 | Christopher Webber, 995 | Dave Wilkinson II, 996 | James Snell, 997 | Jonathan LaCour, 998 | Kyle Mahan, 999 | Manton Reece, 1000 | Pelle Wessman, 1001 | and 1002 | Tantek Çelik. 1003 |

1004 |
1005 | 1006 | 1007 | -------------------------------------------------------------------------------- /publish/2017-06-27-WD/index-src.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JF2 Post Serialization Format 5 | 6 | 8 | 95 | 96 | 97 | 98 | 99 |
100 |

101 | This document describes a JSON serialization format to describe simple 102 | streams of data as well as single objects of data for data transfer and 103 | processing. 104 |

105 |
106 | 107 |
108 |
109 | 110 |
111 |

Author's Note

112 |

113 | This document is an attempt to unify various simplified versions of the 114 | Microformats-2 representative JSON format. As such, much of this document 115 | is likely to change as various implementations contribute input. 116 |

117 |
118 | 119 |
120 |

Introduction

121 |

122 | JF2 is a JSON based document format that describes lists and single entries 123 | of information. The primary use case for JF2 is to create a JSON format for 124 | social web post objects to be processed directly by client software and by other 125 | servers. 126 |

127 |

128 | The name JF2 comes from its origins in being a direct parsed format of 129 | Microformats-2 (MF2) data from html. Rather than defining a new vocabulary, 130 | JF2 uses the vocabulary defined by [[microformats2]] 131 | however any other suitable vocabulary could be used. JF2 is vocabulary 132 | independent except for its profiles which are fully vocabulary aware. 133 |

134 |
135 | 136 |
137 |

Use Cases

138 |

139 | JF2 has evolved as a result of a variety of use-cases for different implementations 140 | exploring ways to simplify their existing use of canonical parsed microformats2 JSON 141 | output. All of these use cases in particular are simply to have a single format for 142 | the storage and use of social web objects. 143 |

144 |

145 | Webmention.io is a service that provides webmention 146 | processing and verification on behalf of other sites. It uses a simple JSON object to 147 | transfer a processed webmention back to the client site's javascript for display. The 148 | attributes are intended to be processed only by the client site's javascript code, not 149 | by anything else. 150 |

151 |

152 | mf2util is a utility library that provides a layer on top 153 | of microformats processing. The library returns only a simple JSON object and strips 154 | off any unnecessary information leaving the library user only the most essential information. 155 |

156 |

157 | Various services (xray, unmung, socialstreams) 158 | provide conversion from microformats pages in to JF2 for quick inspection to 159 | validate proper semantics. 160 |

161 |
162 | 163 |
164 |

Syntax

165 |

166 | JF2 consists of JSON objects which are defined by a type property that will specify 167 | the vocabulary of the object. Properties are attached to these objects which will contain 168 | either a single string, a single object, an array of strings, or an array of objects. 169 | Arrays that have only a single item SHOULD be condensed into only the single containing item. 170 | Any property of an object MAY be a single item or an array of items except for reserved words 171 | defined below. 172 |

173 |
174 |

Reserved Keywords

175 |

176 | The following keywords are reserved and cannot be used as property names in vocabularies. 177 |

178 | 211 |
212 | 213 |
214 |

Posts

215 | 216 |
217 |

Post Objects

218 |

219 | A post is composed of a "type" property, and one or more additional properties that describe the post. 220 |

221 |

222 | The "type" property has a value that describes the vocabulary of this post. Common values include "entry", "card", etc. See Microformats 2 vocabularies for the full list when using a microformats based vocabulary. 223 |

224 |

225 | Any additional properties in the post object are considered part of the post's vocabulary. 226 |

227 | 228 |
229 |
230 |

Post Properties

231 |

232 | The list of valid post properties is defined by the vocabularies. This allows new vocabularies to be developed outside the development of the syntax. 233 |

234 |

235 | Most values will be strings. If a property (such as `author` for example) references another 236 | object, it may be serialized in two ways: as an object serialized as the property value or as 237 | the unique identifier or URL where the object can be found. See Using References 238 |

239 |

240 | Values may also be arrays if the vocabulary allows for multiple values of the property. 241 |

242 | 243 |
244 |
245 |

Example Post

246 | 247 |
 248 |             {
 249 |               "type": "entry",
 250 |               "published": "2015-10-20T15:49:00-0700",
 251 |               "url": "http://example.com/post/fsjeuu8372",
 252 |               "author": {
 253 |                 "type": "card",
 254 |                 "name": "Alice",
 255 |                 "url": "http://alice.example.com",
 256 |                 "photo": "http://alice.example.com/photo.jpg"
 257 |               },
 258 |               "name": "Hello World",
 259 |               "content": "This is a blog post",
 260 |               "category": "Posts"
 261 |             }
 262 |           
263 | 264 |
 265 |             {
 266 |               "type": "entry",
 267 |               "published": "2015-10-20T15:49:00-0700",
 268 |               "url": "http://example.com/like/r23eugi02c",
 269 |               "author": {
 270 |                 "type": "card",
 271 |                 "name": "Alice",
 272 |                 "url": "http://alice.example.com",
 273 |                 "photo": "http://alice.example.com/photo.jpg"
 274 |               },
 275 |               "like-of": "http://bob.example.com/post/100",
 276 |               "category": ["Likes", "Posts"]
 277 |             }
 278 |           
279 |
280 |
281 | 282 |
283 |

Author

284 |

285 | An author is represented by the h-card vocabulary, and consists of a name, photo URL, URL to the author profile, and [others](http://microformats.org/wiki/h-card). This is represented by the following JSON. 286 |

287 | 288 |
 289 |           {
 290 |             "type": "card",
 291 |             "name": "Aaron Parecki",
 292 |             "photo": "http://aaronparecki.com/photo.jpg",
 293 |             "url": "http://aaronparecki.com/"
 294 |           }
 295 |         
296 |
297 | 298 |
299 |

HTML Content

300 |

301 | By default, any string value should be interpreted as literal plaintext. 302 | This means when displaying a string in an HTML page, it must be HTML escaped. 303 |

304 | 305 |

306 | If the value of a property is to be interpreted as HTML, it MUST be enclosed in 307 | an object and placed under the "html" property as follows. If a plaintext version 308 | is also available, that is placed under the "text" property. 309 |

310 | 311 |
 312 |           {
 313 |             "type": "entry",
 314 |             "content": {
 315 |               "html": "<p>Hello World</p>"
 316 |               "text": "Hello World"
 317 |             }
 318 |           }
 319 |         
320 | 321 |
322 |

Multiple URLs for video/audio/picture

323 |

324 | Since HTML video/audio/picture tags may have multiple URLs, we need a way to convey this information in the JSON representation. 325 |

326 | 327 |
 328 |             <div class="h-entry">
 329 |               <video class="u-video" width="640" height="360" preload controls>
 330 |                 <source src="sample_h264.mov" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"' />
 331 |                 <source src="sample_ogg.ogv" type='video/ogg; codecs="theora, vorbis"' />
 332 |                 <source src="sample_webm.webm" type='video/webm; codecs="vp8, vorbis"' />
 333 |               </video>
 334 |             </div>
 335 |           
336 | 337 |
 338 |             {
 339 |               "type": "entry",
 340 |               "video": [
 341 |                 {
 342 |                   "content-type": "video/mp4",
 343 |                   "url": "sample_h264.mov"
 344 |                 },
 345 |                 {
 346 |                   "content-type": "video/ogg",
 347 |                   "url": "sample_ogg.ogg"
 348 |                 },
 349 |                 {
 350 |                   "content-type": "video/webm",
 351 |                   "url": "sample_webm.webm"
 352 |                 }
 353 |               ]
 354 |             }
 355 |           
356 | 357 |
358 |
359 |
360 | 361 |
362 |

Using References

363 |

364 | The purpose of the `references` property is to exclude any non-authoritative 365 | data from the defined object. To do this, non-authoritative data is moved so 366 | that implementations looking to process only authoritative data may simply ignore 367 | the references property and fetch any data that would be contained there from its 368 | authoritative source. 369 |

370 |

371 | If a property is a reference to an object that is defined authoritatively in 372 | some other location, the `references` property SHOULD be used. The property 373 | SHOULD contain only the unique identifier or URL where the authoritative data 374 | may be found. In the references object, the URL or unique identifier MAY 375 | be entered as the key field and a serialization of the referenced object MAY 376 | be provided. This serialization of the referenced object MAY be incomplete 377 | so as to provide only necessary data. 378 |

379 |

380 | Parsing implementations SHOULD fetch data from the authoritative source instead of using 381 | the references object. 382 |

383 |
384 |

Example of References

385 |
 386 |             {
 387 |               "type": "entry",
 388 |               "published": "2015-10-20T15:49:00-0700",
 389 |               "url": "http://example.com/post/fsjeuu8372",
 390 |               "author": "http://alice.example.com",
 391 |               "name": "Hello World",
 392 |               "content": "This is a blog post",
 393 |               "category": "Posts",
 394 |               "references": {
 395 |                 "http://alice.example.com": {
 396 |                   "type": "card",
 397 |                   "name": "Alice",
 398 |                   "url": "http://alice.example.com",
 399 |                   "photo": "http://alice.example.com/photo.jpg"
 400 |                 }
 401 |               }
 402 |             }
 403 |           
404 | 405 |
 406 |             {
 407 |               "type": "entry",
 408 |               "published": "2015-10-20T15:49:00-0700",
 409 |               "url": "http://example.com/like/r23eugi02c",
 410 |               "author": {
 411 |                 "type": "card",
 412 |                 "name": "Alice",
 413 |                 "url": "http://alice.example.com",
 414 |                 "photo": "http://alice.example.com/photo.jpg"
 415 |               },
 416 |               "like-of": "http://bob.example.com/post/100",
 417 |               "category": ["Likes", "Posts"],
 418 |               "references": {
 419 |                 "http://bob.example.com/post/100": {
 420 |                   "type": "entry",
 421 |                   "published": "2015-10-18T12:33:00-0700",
 422 |                   "url": "http://bob.example.com/post/100",
 423 |                   "author": "http://bob.example.com",
 424 |                   "name": "My First Post",
 425 |                   "content": "This is my first post on my new blog, I hope you like it"
 426 |                 },
 427 |                 "http://bob.example.com": {
 428 |                   "type": "card",
 429 |                   "name": "Bob",
 430 |                   "url": "http://bob.example.com",
 431 |                   "photo": "http://bob.example.com/mypicture.jpg"
 432 |                 }
 433 |               }
 434 |             }
 435 |           
436 |
437 | 438 |
439 |
440 |

Collections

441 | 442 |

443 | Posts can live inside of collections. A collection may be a home page feed, or a feed of other posts such as a list of contacts, a list of things someone has liked, etc. There is no requirement that all posts in a collection need to be of the same type. 444 |

445 | 446 |

447 | The collection may also have its own properties such as "name" or "author". 448 |

449 | 450 |
 451 |         {
 452 |           "type": "feed",
 453 |           "url": "http://alice.example.com/collectionurl",
 454 |           "name": "Alice's Home Page",
 455 |           "author": {
 456 |             "type": "card",
 457 |             "name": "Alice",
 458 |             "url": "http://alice.example.com",
 459 |             "photo": "http://alice.example.com/photo"
 460 |           },
 461 |           "children": [
 462 |             { ... },
 463 |             { ... }
 464 |           ]
 465 |         }
 466 |       
467 | 468 |
469 |

Multiple items on a page

470 |

471 | If an HTML page contains multiple top-level items, (most commonly found when a page contains a list of [[h-entry]] objects), the parser creates an implicit top-level collection with no properties. 472 |

473 | 474 |
 475 |           {
 476 |             "children": [
 477 |               { ... },
 478 |               { ... }
 479 |             ]
 480 |           }
 481 |         
482 | 483 |
484 |
485 | 486 |
487 |

Deriving the Syntax

488 |

489 | This syntax is derived from HTML with Microformats-2, converted to JSON [[microformats2-parsing]] converted to a simplified JSON. The examples below illustrate the process. 490 |

491 | 492 |
493 |

Deriving a Note

494 | 495 |

HTML + Microformats

496 |
 497 |           <article class="h-entry">
 498 |             <h1 class="p-name">Hello World</h1>
 499 |             <p>Published by <a class="p-author h-card" href="http://example.com/">A. Developer</a>
 500 |                on <a href="http://example.com/2015/10/21" class="u-url"><time class="dt-published" datetime="2015-10-21T12:00:00-0700">October 21<sup>st</sup>, 2015</time></a>
 501 |            
 502 |             <p class="p-summary">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus imperdiet ultrices pulvinar.</p>
 503 |            
 504 |             <div class="e-content"><p>Donec dapibus enim lacus, <i>a vehicula magna bibendum non</i>. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in.</p></div>
 505 |           </article>
 506 |         
507 | 508 |

Parsed Microformats JSON

509 | 510 |
 511 |         {
 512 |           "items": [
 513 |             {
 514 |               "type": [
 515 |                 "h-entry"
 516 |               ],
 517 |               "properties": {
 518 |                 "author": [
 519 |                   {
 520 |                     "type": [
 521 |                       "h-card"
 522 |                     ],
 523 |                     "properties": {
 524 |                       "name": [
 525 |                         "A. Developer"
 526 |                       ],
 527 |                       "url": [
 528 |                         "http:\/\/example.com\/"
 529 |                       ]
 530 |                     },
 531 |                     "value": "A. Developer"
 532 |                   }
 533 |                 ],
 534 |                 "name": [
 535 |                   "Hello World"
 536 |                 ],
 537 |                 "summary": [
 538 |                   "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus imperdiet ultrices pulvinar."
 539 |                 ],
 540 |                 "url": [
 541 |                   "http:\/\/example.com\/2015\/10\/21"
 542 |                 ],
 543 |                 "published": [
 544 |                   "2015-10-21T12:00:00-0700"
 545 |                 ],
 546 |                 "content": [
 547 |                   {
 548 |                     "html": "<p>Donec dapibus enim lacus, <i>a vehicula magna bibendum non</i>. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in.</p>",
 549 |                     "value": "Donec dapibus enim lacus, a vehicula magna bibendum non. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in."
 550 |                   }
 551 |                 ]
 552 |               }
 553 |             }
 554 |           ]
 555 |         }
 556 |         
557 | 558 |

Simplified JSON

559 | 560 |
 561 |         {
 562 |           "type": "entry",
 563 |           "author": {
 564 |             "type": "card",
 565 |             "url": "http://example.com",
 566 |             "name": "A. Developer"
 567 |           },
 568 |           "url": "http://example.com/2015/10/21",
 569 |           "published": "2015-10-21T12:00:00-0700",
 570 |           "name": "Hello World",
 571 |           "summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus imperdiet ultrices pulvinar.",
 572 |           "content": {
 573 |             "value": "<p>Donec dapibus enim lacus, <i>a vehicula magna bibendum non</i>. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in.</p>",
 574 |             "text": "Donec dapibus enim lacus, a vehicula magna bibendum non. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in."
 575 |           }
 576 |         }
 577 | 
 578 |         
579 | 580 |
581 |
582 |

JSON-LD Consideration

583 |

584 | JF2 documents all have an implicit @context field which is optional. This @context can be found at 585 | http://www.w3.org/ns/jf2 586 | and is provided only to make conversion to [[JSON-LD]] format possible. Most JF2 will process 587 | fine in JSON-LD systems, however, this support is not guaranteed and those wishing to 588 | use JF2 in JSON-LD may need to modify serialization slightly. 589 |

590 |
591 |
592 |
593 |

Profiles

594 |
595 |

Intro

596 |

597 | JF2 on its own is intentionally vocabulary independent, that is, any value may contain a single value, a 598 | set of values, or a structure and there are no requirements that fields be present. While this keeps 599 | the fidelity of the original [[microformats2]] encoded html, some consuming code can be simplified by 600 | adding vocabulary requirements. This creates the need for vocabulary aware profiles of JF2, where 601 | the profile adds additional constraints on the fields present. 602 |

603 |
604 |
605 |

JF2 Feed

606 | 607 |
608 |

Introduction

609 |

610 | XML formats have heavily dominated serializations of social activity feeds for blogs and news feeds for years. 611 | Several attempts have been created over the years to attempt to bring these to a JSON format, most recently 612 | [[jsonfeed-v1]], which JF2 Feed gets the majority of its requirements from. JF2 Feed is an attempt to bring 613 | some of the advantages of these vocabulary aware specifications under a standard vocabulary from 614 | [[microformats2]]. 615 |

616 | 617 |

618 | Fields described below have additional requirements to be considered a valid JF2 Feed, however they may contain 619 | any number of additional fields from [[h-feed]], [[h-entry]], and [[h-card]]. The entries below are codified as 620 | they are likely the most useful for feed readers and additional data is expected to be fetchable at the entry URL, 621 | preferrably though valid [[microformats2]] markup. 622 |

623 |
624 | 625 |
626 |

Required Fields and Required Formats

627 |

628 | The following fields have additional constraints to be a valid JF2 Feed that only apply 629 | to the top level feed object, its child entry object, and any properties of them. 630 |

631 | 632 |
    633 |
  • type MUST be defined with a value of "feed" on the top level entry. 634 | All other sub-object items MUST NOT have a type value of "feed". All direct children 635 | of the top level feed object MUST have a value of "entry". 636 |
  • 637 |
  • name MUST be defined on the top level "feed" object. This value MUST 638 | be a single string value. Any direct children of the top level item SHOULD have this 639 | property and if present MUST be a single string value. 640 |
  • 641 |
  • url SHOULD be defined on the top level "feed" object. This value MUST 642 | be a single string value and is expected to contain the URL of the data which this 643 | JF2 Feed describes. Additionally, if present in the author property object, or any direct 644 | child entry object, it MUST contain a single string value only and that value must be a [[URL]]. 645 |
  • 646 |
  • photo MUST be a single string value if present on the top level feed 647 | object, its author property object, or any direct child entry object and MUST be a valid [[URL]]. 648 |
  • 649 |
  • uid MUST be present on any entry object which is a direct child of the 650 | top level feed object. This property MUST be a single string value and MUST uniquely 651 | identify this entry object. This MAY be a duplicate of the entry's url property. 652 |
  • 653 |
  • published SHOULD be present on any entry object which is a direct 654 | child of the top level feed object. If present, this property MUST be a single string 655 | value and MUST be formatted as specified by [[ISO8601]]. 656 |
  • 657 |
  • updated MAY be present on any entry object which is a direct 658 | child of the top level feed object. If present, this property MUST be a single string 659 | value and MUST be formatted as specified by [[ISO8601]]. 660 |
  • 661 |
  • category MAY be present on any entry object which is a direct 662 | child of the top level feed object. If present, this property MUST be an array of 663 | string values. 664 |
  • 665 |
  • author MAY be present on the top level feed or second level entry 666 | objects. If present, it MUST be an object and it MUST contain at least a name, url, 667 | or photo property. 668 |
  • 669 |
  • content MAY be present on any entry object which is a direct 670 | child of the top level feed object. If present, this property MUST be an object as 671 | described in the html content regardless if only a text/plain 672 | version is available. 673 |
  • 674 |
  • summary MAY be present on any second level entry object. If present, 675 | it MUST be a single string. 676 |
  • 677 |
  • video or audio MAY be present on any second level entry object. If present, 678 | it MUST be an object as described in multiple urls section with at least 679 | a 'url' property which MUST be a single string and a valid [[URL]]. 680 |
  • 681 | 682 |
683 |

684 | The format of published and updated fields may change from [[ISO8601]] to [[RFC3339]] or use [[microformats2]]'s more liberal date field. Please discuss on github issues 685 |

686 | 687 |
688 | 689 |
690 |

Discovery

691 |

692 | The JF2 Feed for a page may be published as HTTP Link header [[RFC5988]], or as an HTML 693 | <link> or <a> tag element with the following attributes. 694 |

rel="alternate" type="application/jf2feed+json" href="http://www.example.com/jf2feed.json" 
695 |

696 |
697 |
698 |

JF2 Feed Example

699 | 700 |
 701 |             {
 702 |                 "type": "feed",
 703 |                 "url": "https://example.org/myfeed.html",
 704 |                 "name": "Brent Simmons’s Microblog",
 705 |                 "author": {
 706 |                     "type": "card",
 707 |                     "name": "Brent Simmons",
 708 |                     "url": "http://example.org/",
 709 |                     "photo": "https://example.org/avatar.png"
 710 |                 },
 711 |                 "children": [
 712 |                     {
 713 |                         "type": "entry",
 714 |                         "uid": "https://example.org/2347259",
 715 |                         "url": "https://example.org/2347259",
 716 |                         "content": {
 717 |                           "text": "Cats are neat. \n\nhttps://example.org/cats"
 718 |                         },
 719 |                         "published": "2016-02-09T14:22:00-07:00"
 720 |                     }
 721 |                 ]
 722 |             }
 723 | 
 724 |           
725 |
726 |
727 |

JSON Feed to JF2 conversion

728 | 729 |

730 | As it is not sane to do this for every feed serialization currently available, 731 | this section is likely to only be temporary in the spec and may be moved to a 732 | more permanant home elsewhere. 733 |

734 | 735 |

736 | JF2 Feed was based heavily on [[jsonfeed-v1]] and as such it is quite trivial to process a 737 | JSON Feed as a JF2 Feed with only minor changes. This section discusses the conversion that 738 | must be made for this process, most of which is simply conversion of properties to their 739 | [[microformats2]] equivalent. 740 |

741 | 742 |

743 | Conversion of the top level sturcture 744 |

745 |
    746 |
  • Drop or ignore the "version" property
  • 747 |
  • Create a property "type" with the value "feed"
  • 748 |
  • Rename the properties "title" to "name", "home_page_url" to "url", "icon" to "photo", and "description" to "summary"
  • 749 |
  • Modify the "author" property as described below
  • 750 |
  • Rename the "items" property to "children" and modify as described below
  • 751 |
752 | 753 |

754 | Conversion of the author property on the top level or on any lower level entry 755 |

756 |
    757 |
  • Rename the property 'avatar' to 'photo'
  • 758 |
759 | 760 |

761 | Conversion each entry in items 762 |

763 |
    764 |
  • Rename the 'id' property to 'uid'
  • 765 |
  • Convert content_html and content_text in to a single object under the property "content" with properties "html" and "text", leaving out either if not present, or the entire property if neither are present.
  • 766 |
  • Rename the properties 767 | "title" to "name", 768 | "image" to "photo", 769 | "date_published" to "published", 770 | "date_modified" to "updated", 771 | "tags" to "category", 772 | "banner_image" to "featured"
  • 773 |
  • Drop or ignore the 'external_url' property. If content is not set, it may be useful to set content['text'] to the value of 'external_url'
  • 774 |
  • Keep "summary" property the same
  • 775 |
  • 776 | For each entry in the "attachments" array, 777 |
      778 |
    1. Rename "mime-type" to "content-type".
    2. 779 |
    3. Keep the "url" property as is.
    4. 780 |
    5. If the "content-type" field begins with "video/", add the item to the "video" property array.
    6. 781 |
    7. If the "content-type" field begins with "photo/", add the item to the "photo" property array.
    8. 782 |
    783 | 784 |
  • 785 | 786 | 787 |
788 |

789 | It may be benefitial to drop any properties other than those mentioned to avoid conflicts with any future [[microformats2]] properties. 790 |

791 | 792 | 793 |
794 |
795 |
796 | 797 |
798 |

Extension

799 |

800 | JF2 MAY be extended by the [[microformats2]] extension mechanism. The 'x-*' properties created in this 801 | way MAY be present in any serialization of JF2. Parsers MUST NOT halt on any unknown properties they 802 | encounter. 803 |

804 |
805 | 806 |
807 |

Language And Internationalization

808 |

809 | In order to allow any language to be serialized in JF2, the 'lang' value can be set on any object to inform 810 | annotate the natural language of the text. Many often ask for control structures like directionality of text 811 | and multiple language serializations. Directionality of text can be accomplished with UTF-8 control characters 812 | which can be added to any of the values in the document. In addition, any content inside of 'html' properties 813 | can have any markup as defined by [[HTML5]]. 814 |

815 |

816 | Multiple serializations of the same text has not been seen to be needed in practice. 817 | The suggested path for accomplishing this with JF2 is to have these hosted at different locations. 818 | For example, it is common practice on websites to host an alternate language version of a site under a different 819 | directory structure for each language (such as /en/, /jp/, /fr/, etc). 820 |

821 |
822 | 823 |
824 |

Implementations

825 |

826 | The following is a list of currently known implementations of JF2-like formats and their current differences to this document 827 |

828 | 829 |
830 |

Webmention.io

831 |

832 | Webmention.io is a hosted service for handling webmentions. 833 | Webmentions are accessed from an API that that returns a set of objects in JSON format similar to JF2. 834 | The key difference is the inclusion of some additional data wrapping the objects 835 | (source, verified, verified_data, etc) and all wrapped in a "links" array. 836 | Code available at https://github.com/aaronpk/webmention.io 837 |

838 |
839 | 840 |
841 |

XRay

842 |

843 | X-Ray is a tool to return structured data from any URL. 844 | Code available at https://github.com/aaronpk/XRay 845 |

846 |
847 | 848 |
849 |

Unmung

850 |

851 | Unmung is a tool for converting various formats. 852 | Code available at https://github.com/kevinmarks/unmung 853 |

854 |
855 | 856 | 857 |
858 |

SocialStreams

859 |

860 | SocialStreams is a translation service for converting social formats. 861 | Code available at https://github.com/dissolve/socialstream 862 |

863 |
864 | 865 |
866 |

mf2util

867 |

868 | Mf2util is a utility library for interpreting microformats2. 869 | Code available at https://github.com/kylewm/mf2util 870 |

871 |
872 |
873 | 874 |
875 |

Privacy Considerations

876 | 877 |

878 | As with any serialization format, JF2 streams can potentially contain private and personally identifiable information. 879 | As such, producing and consuming implementations SHOULD take a default stance that all information in these documents are 880 | private and take steps to ensure the privacy of their users. This can include transmission only over secure connections, 881 | limiting access to streams, deletion of stored secure information, or any other steps that would apply to any private 882 | information. 883 |

884 |
885 | 886 |
887 |

Security Considerations

888 | 889 |

890 | JF2 is not intended to contain executable code however, as JF2 allows arbitrary text it may be possible for publishers 891 | to craft text in an executable fashion. As such, JF2 documents should not be passed through any executable mechanism 892 | such as javascripts eval() function to be parsed. Processing of field contents should not execute any encountered code 893 | and it is recommended that any HTML formatted content be filtered to remove any executable code such as script and style tags. 894 |

895 | 896 |

897 | JF2 documents may require privacy and integrity services depending on the document content and implementation 898 | concerns. If such services are required, they must be provided externally such as by the use of SSL/TLS connections 899 | to prevent forgery or exposure of data. 900 |

901 | 902 |

903 | Any externally linked documents must be processed by their own format's security model. JF2 documents do not make any claims 904 | about the security of any externally referenced documents. 905 |

906 | 907 |

908 | JF2 Streams are JSON Documents and are subject to the same security considerations described in [[!RFC7159]]. 909 |

910 | 911 |
912 | 913 | 914 | 915 |
916 | 917 |

IANA Considerations

918 | 919 |
920 |

The application/jf2feed+json Media Type

921 | 922 |

923 | This specification registers the application/jf2feed+json MIME Media Type specifically for identifying documents 924 | conforming to the JF2 format with the JF2 Feed profile. 925 |

926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 959 | 960 |
Type name: application
Subtype name: jf2feed+json
Required parameters: None
Optional parameters: None
Encoding considerations: 947 | Binary, as per [[RFC6839]], section 3.1; the charset parameter is not used and byte-order marks are not permitted, as per [[RFC7159]], sections 11 and 8.1 948 |
Security considerations: See [[RFC7159]] section 12 and the Security Considerations section of this specification.
Contact: 957 | Ben Roberts <ben@thatmustbe.me> 958 |
961 | 962 |
963 | 964 |
965 | 966 | 967 |
968 |

Change Log

969 |
970 |

Changes from 12 June 2017 WD to this version

971 | 972 |

This section lists changes from the 12 June 2017 FPWD to this version.

973 | 974 | 978 | 979 |
980 |
981 |

Changes from 28 July 2016 FPWD to 12 Jun 2017 WD

982 | 983 |

This section lists changes from the 28 July 2016 FPWD to 12 June 2017 WD

984 | 985 | 997 | 998 |
999 |
1000 | 1001 |
1002 |

Acknowledgements

1003 |

1004 | The authors wish to thank the Microformats, IndieWebCamp, Pump.io, and Activity Streams communities for their continued work 1005 | in building the social web and helping define standards such as this one. This includes, but is certainly not limited to, 1006 | Aaron Parecki, 1007 | Benjamin Goering, 1008 | Brent Simmons, 1009 | Christopher Webber, 1010 | Dave Wilkinson II, 1011 | James Snell, 1012 | Jonathan LaCour, 1013 | Kyle Mahan, 1014 | Manton Reece, 1015 | Pelle Wessman, 1016 | and 1017 | Tantek Çelik. 1018 |

1019 |
1020 | 1021 | 1022 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JF2 Post Serialization Format 5 | 6 | 8 | 82 | 83 | 84 | 85 | 86 |
87 |

88 | This document describes a JSON serialization format to describe simple 89 | streams of data as well as single objects of data for data transfer and 90 | processing. 91 |

92 |
93 | 94 |
95 |
96 | 97 |
98 |

Author's Note

99 |

100 | This document is an attempt to unify various simplified versions of the 101 | microformats2 representative JSON format. As such, much of this document 102 | is likely to change as various implementations contribute input. 103 | — Kevin Marks 104 | & Bebe Roberts 105 |

106 |
107 | 108 |
109 |

Introduction

110 |

111 | JF2 is a JSON based document format that describes single entries of 112 | information and lists of those entries. The primary use case for JF2 is to 113 | create a JSON format for social web post objects to be processed directly by 114 | client software and by other servers. 115 |

116 |

117 | JF2 is vocabulary independent except for its profiles which are fully 118 | vocabulary aware. Rather than defining a new vocabulary the profiles 119 | described in this document use the vocabulary defined by [[microformats2]] 120 | (MF2); however, any other suitable vocabulary could be used. The name JF2 121 | comes from its origins in being a direct parsed format of microformats2 data 122 | from HTML. 123 |

124 |
125 | 126 |
127 |

Use Cases

128 |

129 | JF2 has evolved as a result of a variety of use-cases for different implementations 130 | exploring ways to simplify their existing use of canonical parsed microformats2 JSON 131 | output. All of these use cases in particular are simply to have a single format for 132 | the storage and use of social web objects. 133 |

134 |

135 | Webmention.io is a service that provides Webmention 136 | processing and verification on behalf of other sites. It uses a simple JSON object to 137 | transfer a processed Webmention back to the client site's JavaScript for display. The 138 | attributes are intended to be processed only by the client site's JavaScript code, not 139 | by anything else. 140 |

141 |

142 | mf2util is a utility library that provides a layer on top 143 | of microformats processing. The library returns only a simple JSON object and strips 144 | off any unnecessary information leaving the library user only the most essential information. 145 |

146 |

147 | Various services (XRay, 148 | Unmung, 149 | SocialStreams) 150 | provide conversion from microformats pages into JF2 for quick inspection to 151 | validate proper semantics. These tools have been used in practice to allow for embedded social activity in a page. 152 |

153 |
154 | 155 |
156 | 157 |
158 |

Documents

159 | 160 |

161 | Conforming documents are those that comply with all the conformance 162 | criteria for JF2 documents. For readability, some of these conformance 163 | requirements are phrased as conformance requirements on publishers. 164 | Such requirements are implicitly requirements on documents: by 165 | definition, all documents are assumed to have a publisher. 166 |

167 | 168 |

169 | Conforming documents must not use features of JSON-LD or other 170 | serialization features disallowed in this specification. Conforming 171 | documents that include types or properties beyond those defined in 172 | [[microformats2]] must use [[microformats2]]'s prefixing methods to indicate 173 | non-standard properties. 174 |

175 | 176 |

177 | A non-exhaustive list of examples of documents includes: 178 | 179 |

180 | 201 | 202 | 203 |
204 | 205 |
206 | 207 |

Implementations

208 | 209 |

210 | Conforming implementations are software that publish, store, analyze, 211 | consume or otherwise process conforming documents. The two main kinds 212 | of implementations are publishers and consumers. 213 |

214 | 215 |
216 | 217 |

Publishers

218 | 219 |

220 | Conforming publishers are implementations that create and publish 221 | conforming documents. Conforming publishers must make conforming 222 | documents available according to the serialization requirements of 223 | this document. Conforming publishers must consider privacy as described 224 | in the Privacy section of this document. 225 | Conforming publishers must consider security as described in the 226 | Security Considerations section 227 | of this document. 228 |

229 | 230 |

231 | A non-exhaustive list of example publishers includes: 232 |

233 | 234 |
    235 |
  • 236 | A social network 237 |
  • 238 |
  • 239 | A personal web site 240 |
  • 241 |
  • 242 | A document publishing system 243 |
  • 244 |
  • 245 | A bridge from a non-conforming social network 246 |
  • 247 |
  • 248 | A document converter from similar document types such as RSS, Atom, 249 | or JSON Feed 250 |
  • 251 |
252 | 253 |
254 | 255 |
256 | 257 |

Consumers

258 | 259 |

260 | Conforming consumers are implementations that read and analyze 261 | conforming documents. Conforming consumers must not halt on any 262 | unrecognized properties or types. 263 |

264 | 265 |

266 | Conforming consumers may re-publish conforming documents in other 267 | other data formats. Conforming consumers may present conforming 268 | documents to a user on screen, in print, in audio format, or using 269 | other presentation mechanisms. Conforming consumers must faithfully 270 | translate the information represented in conforming documents into 271 | these other formats or media. Conforming consumers that re-publish 272 | conforming documents must consider privacy and security as described 273 | in the Privacy section and 274 | Security Considerations section 275 | of this document. 276 |

277 | 278 |

A non-exhaustive list of example consumers includes:

279 | 280 |
    281 |
  • 282 | A social network 283 |
  • 284 |
  • 285 | A search engine 286 |
  • 287 |
  • 288 | A feed reader 289 |
  • 290 |
  • 291 | A document validator 292 |
  • 293 |
  • 294 | A feed aggregator 295 |
  • 296 |
297 | 298 |
299 |
300 | 301 |
302 |

Validator and Reporting

303 | 304 |

305 | A validator for conforming documents is available at 306 | https://jf2.rocks/. 307 | This also offers example conforming and non-conforming 308 | documents for testing purposes. 309 |

310 | 311 |

312 | Please submit your implementation reports at 313 | https://jf2.rocks/. 314 | Instructions are provided at the URL. 315 |

316 | 317 |
318 | 319 |
320 | 321 | 322 | 323 |
324 |

Syntax

325 |

326 | JF2 consists of JSON objects which are defined by a type property that will specify 327 | the vocabulary of the object. Properties are attached to these objects which will contain 328 | either a single string, a single object, an array of strings, or an array of objects. 329 | Arrays that have only a single item SHOULD be condensed into only the single containing item. 330 | Any property of an object MAY be a single item or an array of items except for reserved properties 331 | defined below. 332 |

333 |
334 |

Reserved Properties

335 |

336 | The following properties are reserved and cannot be used as property names in vocabularies. 337 |

338 | 367 |
368 | 369 |
370 |

Posts

371 | 372 |
373 |

Post Objects

374 |

375 | A post is composed of a "type" property, and one or more additional properties that describe the post. 376 |

377 |

378 | The "type" property has a value that describes the vocabulary of the post. Common values include "entry", "card", etc. 379 | See microformats2 vocabularies 380 | for the full list when using a microformats based vocabulary. 381 |

382 |

383 | Any additional properties in the post object are considered part of the post's vocabulary. 384 |

385 | 386 |
387 |
388 |

Post Properties

389 |

390 | The list of valid post properties is defined by the vocabularies. This allows new vocabularies to be developed outside the development of the syntax. 391 |

392 |

393 | Most values will be strings. If a property (such as "author" for example) references another 394 | object, it may be serialized in two ways: as an inline JSON object or as the unique identifier 395 | or [[!URL]] where the object can be found. See Using References. 396 |

397 |

398 | Values MAY also be arrays if the vocabulary allows for multiple values of the property. 399 |

400 | 401 |
402 |
403 |

Example Post

404 | 405 |
 406 |             {
 407 |               "type": "entry",
 408 |               "published": "2015-10-20T15:49:00-0700",
 409 |               "url": "https://example.com/post/fsjeuu8372",
 410 |               "author": {
 411 |                 "type": "card",
 412 |                 "name": "Alice",
 413 |                 "url": "https://alice.example.com",
 414 |                 "photo": "https://alice.example.com/photo.jpg"
 415 |               },
 416 |               "name": "Hello World",
 417 |               "content": "This is a blog post",
 418 |               "category": "Posts"
 419 |             }
 420 |           
421 | 422 |
 423 |             {
 424 |               "type": "entry",
 425 |               "published": "2015-10-20T15:49:00-0700",
 426 |               "url": "https://example.com/like/r23eugi02c",
 427 |               "author": {
 428 |                 "type": "card",
 429 |                 "name": "Alice",
 430 |                 "url": "https://alice.example.com",
 431 |                 "photo": "https://alice.example.com/photo.jpg"
 432 |               },
 433 |               "like-of": "https://bob.example.com/post/100",
 434 |               "category": ["Likes", "Posts"]
 435 |             }
 436 |           
437 |
438 |
439 | 440 |
441 |

Author

442 |

443 | An author is represented by the [[!h-card]] vocabulary, and consists of a name, photo [[!URL]], [[!URL]] to the author profile, 444 | and others. This is represented by the following JSON. 445 |

446 | 447 |
 448 |           {
 449 |             "type": "card",
 450 |             "name": "Aaron Parecki",
 451 |             "photo": "https://aaronparecki.com/photo.jpg",
 452 |             "url": "https://aaronparecki.com/"
 453 |           }
 454 |         
455 |
456 | 457 |
458 |

HTML Content

459 |

460 | By default, any string value should be interpreted as literal plaintext. 461 | This means when displaying a string in an HTML page, it must be HTML escaped. 462 |

463 | 464 |

465 | If the value of a property is to be interpreted as HTML, it MUST be enclosed in 466 | an object and placed under the "html" property as follows. If a plaintext version 467 | is also available, that is placed under the "text" property. 468 |

469 | 470 |
 471 |           {
 472 |             "type": "entry",
 473 |             "content": {
 474 |               "html": "<p>Hello World</p>",
 475 |               "text": "Hello World"
 476 |             }
 477 |           }
 478 |         
479 | 480 |
481 |

Multiple URLs for video/audio/picture

482 |

483 | Since HTML video/audio/picture tags may have multiple URLs, we need a way to convey this information in the JSON representation. 484 | In such situations, vocabulary properties MAY be arrays. 485 |

486 | 487 |

488 | For example, this HTML (marked up with microformats2): 489 |

490 | 491 |
 492 |             <div class="h-entry">
 493 |               <video class="u-video" width="640" height="360" preload controls>
 494 |                 <source src="sample_h264.mov" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"' />
 495 |                 <source src="sample_ogg.ogv" type='video/ogg; codecs="theora, vorbis"' />
 496 |                 <source src="sample_webm.webm" type='video/webm; codecs="vp8, vorbis"' />
 497 |               </video>
 498 |             </div>
 499 |           
500 | 501 |

502 | could be represented with this JSON: 503 |

504 | 505 |
 506 |             {
 507 |               "type": "entry",
 508 |               "video": [
 509 |                 {
 510 |                   "content-type": "video/mp4",
 511 |                   "url": "sample_h264.mov"
 512 |                 },
 513 |                 {
 514 |                   "content-type": "video/ogg",
 515 |                   "url": "sample_ogg.ogg"
 516 |                 },
 517 |                 {
 518 |                   "content-type": "video/webm",
 519 |                   "url": "sample_webm.webm"
 520 |                 }
 521 |               ]
 522 |             }
 523 |           
524 | 525 |
526 |
527 |
528 | 529 |
530 |

Using References

531 |

532 | The purpose of the references property is to exclude any non-authoritative 533 | data from the defined object. To do this, non-authoritative data is moved so 534 | that implementations looking to process only authoritative data may simply ignore 535 | the references property and fetch any data that would be contained there from its 536 | authoritative source. 537 |

538 |

539 | If a property is a reference to an object that is defined authoritatively in 540 | some other location, the references property SHOULD be used. The property 541 | SHOULD contain only the unique identifier or [[!URL]] where the authoritative data 542 | may be found. In the references object, the URL or unique identifier MAY 543 | be used as the property key and a serialization of the referenced object MAY 544 | be provided as the property value. This serialization of the referenced object MAY 545 | be incomplete so as to provide only necessary data. 546 |

547 |

548 | Parsing implementations SHOULD fetch data from the authoritative source instead of using 549 | the references object. 550 |

551 |
552 |

Example of References

553 |
 554 |             {
 555 |               "type": "entry",
 556 |               "published": "2015-10-20T15:49:00-0700",
 557 |               "url": "https://example.com/post/fsjeuu8372",
 558 |               "author": "https://alice.example.com",
 559 |               "name": "Hello World",
 560 |               "content": "This is a blog post",
 561 |               "category": "Posts",
 562 |               "references": {
 563 |                 "https://alice.example.com": {
 564 |                   "type": "card",
 565 |                   "name": "Alice",
 566 |                   "url": "https://alice.example.com",
 567 |                   "photo": "https://alice.example.com/photo.jpg"
 568 |                 }
 569 |               }
 570 |             }
 571 |           
572 | 573 |
 574 |             {
 575 |               "type": "entry",
 576 |               "published": "2015-10-20T15:49:00-0700",
 577 |               "url": "https://example.com/like/r23eugi02c",
 578 |               "author": {
 579 |                 "type": "card",
 580 |                 "name": "Alice",
 581 |                 "url": "https://alice.example.com",
 582 |                 "photo": "https://alice.example.com/photo.jpg"
 583 |               },
 584 |               "like-of": "https://bob.example.com/post/100",
 585 |               "category": ["Likes", "Posts"],
 586 |               "references": {
 587 |                 "https://bob.example.com/post/100": {
 588 |                   "type": "entry",
 589 |                   "published": "2015-10-18T12:33:00-0700",
 590 |                   "url": "https://bob.example.com/post/100",
 591 |                   "author": "https://bob.example.com",
 592 |                   "name": "My First Post",
 593 |                   "content": "This is my first post on my new blog, I hope you like it"
 594 |                 },
 595 |                 "https://bob.example.com": {
 596 |                   "type": "card",
 597 |                   "name": "Bob",
 598 |                   "url": "https://bob.example.com",
 599 |                   "photo": "https://bob.example.com/mypicture.jpg"
 600 |                 }
 601 |               }
 602 |             }
 603 |           
604 |
605 | 606 |
607 |
608 |

Collections

609 | 610 |

611 | Posts can be contained inside of collections. A collection may be a home page feed, or a feed of other posts such as a list of contacts, a list of things someone has liked, etc. There is no requirement that all posts in a collection need to be of the same type. 612 |

613 | 614 |

615 | The collection may also have its own properties such as "name" or "author". 616 |

617 | 618 |
 619 |         {
 620 |           "type": "feed",
 621 |           "url": "https://alice.example.com/collectionurl",
 622 |           "name": "Alice's Home Page",
 623 |           "author": {
 624 |             "type": "card",
 625 |             "name": "Alice",
 626 |             "url": "https://alice.example.com",
 627 |             "photo": "https://alice.example.com/photo"
 628 |           },
 629 |           "children": [
 630 |             { 
 631 |               "type": "entry",
 632 |               "content": {
 633 |                 "html": "

Hello World

", 634 | "text": "Hello World" 635 | } 636 | }, 637 | { 638 | "type": "entry", 639 | "content": { 640 | "html": "

A Second Post

", 641 | "text": "A Second Post" 642 | } 643 | } 644 | ] 645 | } 646 |
647 | 648 |
649 |

Multiple items on a page

650 |

651 | If an HTML page contains multiple top-level items, (most commonly found when a page contains a list of [[h-entry]] objects), the parser creates an implicit top-level collection with no properties. 652 |

653 | 654 |
 655 |           {
 656 |             "children": [
 657 |               { 
 658 |                 "type": "entry",
 659 |                 "content": {
 660 |                   "html": "

Hello World

", 661 | "text": "Hello World" 662 | } 663 | }, 664 | { 665 | "type": "entry", 666 | "content": { 667 | "html": "

A Second Post

", 668 | "text": "A Second Post" 669 | } 670 | } 671 | ] 672 | } 673 |
674 | 675 |
676 |
677 | 678 |
679 |

Deriving the Syntax

680 |

681 | This syntax is derived from HTML with microformats2 converted to JSON with [[!microformats2-parsing]], then converted to a simplified JSON. The examples below illustrate the process. 682 |

683 | 684 |
685 |

Deriving a Note

686 | 687 |

HTML + Microformats

688 |
 689 |           <article class="h-entry">
 690 |             <h1 class="p-name">Hello World</h1>
 691 |             <p>Published by <a class="p-author h-card" href="https://example.com/">A. Developer</a>
 692 |                on <a href="https://example.com/2015/10/21" class="u-url"><time class="dt-published" datetime="2015-10-21T12:00:00-0700">October 21<sup>st</sup>, 2015</time></a>
 693 |            
 694 |             <p class="p-summary">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus imperdiet ultrices pulvinar.</p>
 695 |            
 696 |             <div class="e-content"><p>Donec dapibus enim lacus, <i>a vehicula magna bibendum non</i>. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in.</p></div>
 697 |           </article>
 698 |         
699 | 700 |

Parsed Microformats JSON

701 | 702 |
 703 |         {
 704 |           "items": [
 705 |             {
 706 |               "type": [
 707 |                 "h-entry"
 708 |               ],
 709 |               "properties": {
 710 |                 "author": [
 711 |                   {
 712 |                     "type": [
 713 |                       "h-card"
 714 |                     ],
 715 |                     "properties": {
 716 |                       "name": [
 717 |                         "A. Developer"
 718 |                       ],
 719 |                       "url": [
 720 |                         "https://example.com/"
 721 |                       ]
 722 |                     },
 723 |                     "value": "A. Developer"
 724 |                   }
 725 |                 ],
 726 |                 "name": [
 727 |                   "Hello World"
 728 |                 ],
 729 |                 "summary": [
 730 |                   "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus imperdiet ultrices pulvinar."
 731 |                 ],
 732 |                 "url": [
 733 |                   "https://example.com/2015/10/21"
 734 |                 ],
 735 |                 "published": [
 736 |                   "2015-10-21T12:00:00-0700"
 737 |                 ],
 738 |                 "content": [
 739 |                   {
 740 |                     "html": "<p>Donec dapibus enim lacus, <i>a vehicula magna bibendum non</i>. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in.</p>",
 741 |                     "value": "Donec dapibus enim lacus, a vehicula magna bibendum non. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in."
 742 |                   }
 743 |                 ]
 744 |               }
 745 |             }
 746 |           ]
 747 |         }
 748 |         
749 | 750 |

Simplified JSON

751 | 752 |
 753 |         {
 754 |           "type": "entry",
 755 |           "author": {
 756 |             "type": "card",
 757 |             "url": "https://example.com",
 758 |             "name": "A. Developer"
 759 |           },
 760 |           "url": "https://example.com/2015/10/21",
 761 |           "published": "2015-10-21T12:00:00-0700",
 762 |           "name": "Hello World",
 763 |           "summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus imperdiet ultrices pulvinar.",
 764 |           "content": {
 765 |             "html": "<p>Donec dapibus enim lacus, <i>a vehicula magna bibendum non</i>. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in.</p>",
 766 |             "text": "Donec dapibus enim lacus, a vehicula magna bibendum non. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in."
 767 |           }
 768 |         }
 769 | 
 770 |         
771 | 772 |
773 |
774 |

JSON-LD Consideration

775 |

776 | JF2 documents all have an implicit @context field which is optional. This @context can be found at 777 | https://www.w3.org/ns/jf2 778 | and is provided only to make conversion to [[JSON-LD]] format possible. Most JF2 will process 779 | fine in JSON-LD systems, however, this support is not guaranteed and those wishing to 780 | use JF2 in JSON-LD may need to modify serialization slightly. 781 |

782 |
783 |
784 |
785 |

Profiles

786 |
787 |

Intro

788 |

789 | JF2 on its own is intentionally vocabulary independent, that is, any value may contain a single value, a 790 | set of values, or a structure and there are no requirements that fields be present. While this keeps 791 | the fidelity of the original [[microformats2]] encoded HTML, some consuming code can be simplified by 792 | adding vocabulary requirements. This creates the need for vocabulary aware profiles of JF2, where 793 | the profile adds additional constraints on the fields present. 794 |

795 |
796 |
797 |

JF2 Feed

798 | 799 |
800 |

Introduction

801 |

802 | XML formats have heavily dominated serializations of social activity feeds for blogs and news feeds for years. 803 | Several attempts have been created over the years to attempt to bring these to a JSON format, most recently 804 | [[jsonfeed-v1]], which JF2 Feed gets the majority of its requirements from. JF2 Feed is an attempt to bring 805 | some of the advantages of these vocabulary aware specifications under a standard vocabulary from 806 | [[microformats2]]. 807 |

808 | 809 |

810 | Fields described below have additional requirements to be considered a valid JF2 Feed, however they may contain 811 | any number of additional fields from [[h-feed]], [[h-entry]], and [[h-card]]. The fields below are codified as 812 | they are likely the most useful for feed readers; additional data is expected to be fetchable at the entry URL, 813 | preferably though valid [[microformats2]] markup. 814 |

815 |
816 | 817 |
818 |

Required Fields and Required Formats

819 |

820 | The following fields have additional constraints to be a valid JF2 Feed that only apply 821 | to the top level feed object, its child entry object, and any properties of them. 822 |

823 | 824 |
    825 |
  • type MUST be defined with a value of "feed" on the top level entry. 826 | All other sub-object items MUST NOT have a type value of "feed". All direct children 827 | of the top level feed object MUST have a value of "entry". 828 |
  • 829 |
  • name MUST be defined on the top level "feed" object. This value MUST 830 | be a single string value. Any direct children of the top level item SHOULD have this 831 | property and if present MUST be a single string value. 832 |
  • 833 |
  • url SHOULD be defined on the top level "feed" object. This value MUST 834 | be a single string value and is expected to contain the [[!URL]] of the data which this 835 | JF2 Feed describes. Additionally, if present in the author property object, or any direct 836 | child entry object, it MUST contain a single string value only and that value must be a [[!URL]]. 837 |
  • 838 |
  • photo MUST be a single string value if present on the top level feed 839 | object, its author property object, or any direct child entry object and MUST be a valid [[!URL]]. 840 |
  • 841 |
  • uid MUST be present on any entry object which is a direct child of the 842 | top level feed object. This property MUST be a single string value and MUST uniquely 843 | identify this entry object. This MAY be a duplicate of the entry's url property. 844 |
  • 845 |
  • published SHOULD be present on any entry object which is a direct 846 | child of the top level feed object. If present, this property MUST be a single string 847 | value and MUST be formatted as specified by [[!ISO8601]]. 848 |
  • 849 |
  • updated MAY be present on any entry object which is a direct 850 | child of the top level feed object. If present, this property MUST be a single string 851 | value and MUST be formatted as specified by [[!ISO8601]]. 852 |
  • 853 |
  • category MAY be present on any entry object which is a direct 854 | child of the top level feed object. If present, this property MUST be an array of 855 | string values. 856 |
  • 857 |
  • author MAY be present on the top level feed or second level entry 858 | objects. If present, it MUST be an object and it MUST contain at least a name, url, 859 | or photo property. 860 |
  • 861 |
  • content MAY be present on any entry object which is a direct 862 | child of the top level feed object. If present, this property MUST be an object as 863 | described in the HTML content regardless if only a text/plain 864 | version is available. 865 |
  • 866 |
  • summary MAY be present on any second level entry object. If present, 867 | it MUST be a single string. 868 |
  • 869 |
  • video or audio MAY be present on any second level entry object. If present, 870 | it MUST be an object as described in the multiple URLs section with 871 | at least a 'url' property which MUST be a single string and a valid [[!URL]]. 872 |
  • 873 | 874 |
875 |

876 | The format of published and updated fields may change from [[ISO8601]] to [[RFC3339]] or use [[microformats2]]'s more liberal date field. Please discuss on github issues 877 |

878 | 879 |
880 | 881 |
882 |

Discovery

883 |

884 | The JF2 Feed for a page may be published as HTTP Link header [[!RFC5988]], or as an HTML 885 | <link> or <a> tag element with the following attributes. 886 |

rel="alternate" type="application/jf2feed+json" href="https://example.com/jf2feed.json" 
887 |

888 |
889 |
890 |

JF2 Feed Example

891 | 892 |
 893 |             {
 894 |                 "type": "feed",
 895 |                 "url": "https://example.org/myfeed.html",
 896 |                 "name": "Brent Simmons’s Microblog",
 897 |                 "author": {
 898 |                     "type": "card",
 899 |                     "name": "Brent Simmons",
 900 |                     "url": "https://example.org/",
 901 |                     "photo": "https://example.org/avatar.png"
 902 |                 },
 903 |                 "children": [
 904 |                     {
 905 |                         "type": "entry",
 906 |                         "uid": "https://example.org/2347259",
 907 |                         "url": "https://example.org/2347259",
 908 |                         "content": {
 909 |                           "text": "Cats are neat. \n\nhttps://example.org/cats"
 910 |                         },
 911 |                         "published": "2016-02-09T14:22:00-07:00"
 912 |                     }
 913 |                 ]
 914 |             }
 915 | 
 916 |           
917 |
918 |
919 |

JSON Feed to JF2 conversion

920 | 921 |

922 | As it is not sane to do this for every feed serialization currently available, 923 | this section is likely to only be temporary in the spec and may be moved to a 924 | more permanant home elsewhere. 925 |

926 | 927 |

928 | JF2 Feed was based heavily on [[jsonfeed-v1]] and as such it is quite trivial to process a 929 | JSON Feed as a JF2 Feed with only minor changes. This section discusses the conversion that 930 | must be made for this process, most of which is simply conversion of properties to their 931 | [[microformats2]] equivalent. 932 |

933 | 934 |

935 | Conversion of the top level structure 936 |

937 |
    938 |
  • Drop or ignore the "version" property
  • 939 |
  • Create a property "type" with the value "feed"
  • 940 |
  • Rename the properties "title" to "name", "home_page_url" to "url", "icon" to "photo", and "description" to "summary"
  • 941 |
  • Modify the "author" property as described below
  • 942 |
  • Rename the "items" property to "children" and modify as described below
  • 943 |
944 | 945 |

946 | Conversion of the author property on the top level or on any lower level entry 947 |

948 |
    949 |
  • Rename the property 'avatar' to 'photo'
  • 950 |
951 | 952 |

953 | Conversion of each entry in items 954 |

955 |
    956 |
  • Rename the 'id' property to 'uid'
  • 957 |
  • Convert content_html and content_text into a single object under the property "content" with properties "html" and "text", leaving out either if not present or the entire property if neither are present
  • 958 |
  • Rename the properties 959 | "title" to "name", 960 | "image" to "photo", 961 | "date_published" to "published", 962 | "date_modified" to "updated", 963 | "tags" to "category", 964 | "banner_image" to "featured"
  • 965 |
  • Drop or ignore the 'external_url' property. If content is not set, it may be useful to set content['text'] to the value of 'external_url'
  • 966 |
  • Keep "summary" property the same
  • 967 |
  • 968 | For each entry in the "attachments" array, 969 |
      970 |
    1. Rename "mime-type" to "content-type".
    2. 971 |
    3. Keep the "url" property as is.
    4. 972 |
    5. If the "content-type" field begins with "video/", add the item to the "video" property array.
    6. 973 |
    7. If the "content-type" field begins with "image/", add the item to the "photo" property array.
    8. 974 |
    975 | 976 |
  • 977 | 978 | 979 |
980 |

981 | It may be beneficial to drop any properties other than those mentioned to avoid conflicts with any future [[microformats2]] properties. 982 |

983 | 984 | 985 |
986 |
987 |
988 | 989 |
990 |

Extension

991 |

992 | JF2 MAY be extended by the [[!microformats2]] extension mechanism. The 'x-*' properties created in this 993 | way MAY be present in any serialization of JF2. Parsers MUST NOT halt on any unknown properties they 994 | encounter. 995 |

996 |
997 | 998 |
999 |

Language And Internationalization

1000 |

1001 | In order to allow any language to be serialized in JF2, the 'lang' value can be set on any object to annotate 1002 | the natural language of the text. Many often ask for control structures like directionality of text and 1003 | multiple language serializations. Directionality of text can be accomplished with UTF-8 control characters 1004 | which can be added to any of the values in the document. In addition, any content inside of 'html' properties 1005 | can have any markup as defined by [[!HTML5]]. 1006 |

1007 |

1008 | Multiple serializations of the same text has not been seen to be needed in practice. 1009 | The suggested path for accomplishing this with JF2 is to have these hosted at different locations. 1010 | For example, it is common practice on websites to host an alternate language version of a site under a different 1011 | directory structure for each language (such as /en/, /jp/, /fr/, etc). 1012 |

1013 |
1014 | 1015 |
1016 |

Privacy Considerations

1017 | 1018 |

1019 | As with any serialization format, JF2 streams can potentially contain private and personally identifiable information. 1020 | As such, producing and consuming implementations SHOULD take a default stance that all information in these documents are 1021 | private and take steps to ensure the privacy of their users. This can include transmission only over secure connections, 1022 | limiting access to streams, deletion of stored secure information, or any other steps that would apply to any private 1023 | information. 1024 |

1025 |
1026 | 1027 |
1028 |

Security Considerations

1029 | 1030 |

1031 | JF2 is not intended to contain executable code however, as JF2 allows arbitrary text it may be possible for publishers 1032 | to craft text in an executable fashion. As such, JF2 documents should not be passed through any executable mechanism 1033 | such as JavaScript's eval() function to be parsed. Processing of field contents should not execute any encountered code 1034 | and it is recommended that any HTML formatted content be filtered to remove any executable code such as script and style tags. 1035 |

1036 | 1037 |

1038 | JF2 documents may require privacy and integrity services depending on the document content and implementation 1039 | concerns. If such services are required, they must be provided externally such as by the use of SSL/TLS connections 1040 | to prevent forgery or exposure of data. 1041 |

1042 | 1043 |

1044 | Any externally linked documents must be processed by their own format's security model. JF2 documents do not make any claims 1045 | about the security of any externally referenced documents. 1046 |

1047 | 1048 |

1049 | JF2 Streams are JSON Documents and are subject to the same security considerations described in [[!RFC7159]]. 1050 |

1051 | 1052 |
1053 | 1054 | 1055 | 1056 |
1057 | 1058 |

IANA Considerations

1059 | 1060 |
1061 |

The application/jf2feed+json Media Type

1062 | 1063 |

1064 | This specification registers the application/jf2feed+json MIME Media Type specifically for identifying documents 1065 | conforming to the JF2 format with the JF2 Feed profile. 1066 |

1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1100 | 1101 |
Type name: application
Subtype name: jf2feed+json
Required parameters: None
Optional parameters: None
Encoding considerations: 1088 | Binary, as per [[!RFC6839]], section 3.1; the charset parameter is not used and byte-order marks are not permitted, as per [[!RFC7159]], sections 11 and 8.1 1089 |
Security considerations: See [[RFC7159]] section 12 and the Security Considerations section of this specification.
Contact: 1098 | Bebe Roberts <bebe@thatmustbe.me> 1099 |
1102 | 1103 |
1104 | 1105 |
1106 | 1107 | 1108 |
1109 |

Change Log

1110 |
1111 |

Changes from 26 Oct 2017 WD to this version

1112 | 1113 |

This section lists changes from the 26 Oct 2017 WD to this version.

1114 | 1115 | 1120 | 1121 |
1122 |
1123 |

Changes from 19 July 2017 WD to 26 Oct 2017

1124 | 1125 |

This section lists changes from the 19 July 2017 WD to 26 Oct 2017 WD.

1126 | 1127 | 1132 | 1133 |
1134 |
1135 |

Changes from 27 June 2017 WD to 19 July 2017

1136 | 1137 |

This section lists changes from the 27 June 2017 WD to 19 July 2017 WD.

1138 | 1139 | 1144 | 1145 |
1146 |
1147 |

Changes from 12 June 2017 WD to 27 June 2017

1148 | 1149 |

This section lists changes from the 12 June 2017 WD to 27 June 2017 WD.

1150 | 1151 | 1155 | 1156 |
1157 |
1158 |

Changes from 28 July 2016 FPWD to 12 Jun 2017 WD

1159 | 1160 |

This section lists changes from the 28 July 2016 FPWD to 12 June 2017 WD

1161 | 1162 | 1174 | 1175 |
1176 |
1177 | 1178 |
1179 |

Acknowledgements

1180 |

1181 | The authors wish to thank the Microformats, IndieWeb, Pump.io, and Activity Streams communities for their continued work 1182 | in building the social web and helping define standards such as this one. This includes, but is certainly not limited to, 1183 | Aaron Parecki, 1184 | AJ Jordan, 1185 | Benjamin Goering, 1186 | Brent Simmons, 1187 | Christine Webber, 1188 | Dave Wilkinson II, 1189 | James Snell, 1190 | Jonathan LaCour, 1191 | Kara Mahan, 1192 | Manton Reece, 1193 | Pelle Wessman, 1194 | and 1195 | Tantek Çelik. 1196 |

1197 |
1198 | 1199 | 1200 | --------------------------------------------------------------------------------