···11+(*
22+ * ISC License
33+ *
44+ * Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>
55+ *
66+ * Permission to use, copy, modify, and distribute this software for any
77+ * purpose with or without fee is hereby granted, provided that the above
88+ * copyright notice and this permission notice appear in all copies.
99+ *
1010+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1111+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1212+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1313+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1414+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1515+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1616+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1717+ *
1818+ *)
+160
README.md
···11+# OCaml-JSONFeed
22+33+An OCaml library for parsing and generating [JSON Feed](https://www.jsonfeed.org/) documents.
44+55+JSON Feed is a format similar to RSS and Atom but uses JSON instead of XML.
66+It is designed to be easier for developers to work with while providing all the
77+functionality needed for feed syndication.
88+99+## Installation
1010+1111+Add to your `dune-project`:
1212+1313+```lisp
1414+(package
1515+ (name your-package)
1616+ (depends
1717+ jsonfeed
1818+ ...))
1919+```
2020+2121+## Quick Start
2222+2323+### Creating a Feed
2424+2525+```ocaml
2626+open Jsonfeed
2727+2828+(* Create an author *)
2929+let author = Author.create
3030+ ~name:"Jane Doe"
3131+ ~url:"https://example.com/jane"
3232+ ()
3333+3434+(* Create an item with HTML content *)
3535+let item = Item.create
3636+ ~id:"https://example.com/posts/1"
3737+ ~url:"https://example.com/posts/1"
3838+ ~title:"Hello, JSON Feed!"
3939+ ~content:(`Html "<p>My first post using JSON Feed.</p>")
4040+ ~authors:[author]
4141+ ~tags:["introduction"; "jsonfeed"]
4242+ ()
4343+4444+(* Create the feed *)
4545+let feed = Jsonfeed.create
4646+ ~title:"My Blog"
4747+ ~home_page_url:"https://example.com"
4848+ ~feed_url:"https://example.com/feed.json"
4949+ ~items:[item]
5050+ ()
5151+5252+(* Serialize to JSON *)
5353+let json = Jsonfeed.to_string feed
5454+```
5555+5656+### Parsing a Feed
5757+5858+```ocaml
5959+open Jsonfeed
6060+6161+(* Parse from string *)
6262+match Jsonfeed.of_string json_string with
6363+| Ok feed ->
6464+ Printf.printf "Feed: %s\n" (Jsonfeed.title feed);
6565+ List.iter (fun item ->
6666+ match Item.title item with
6767+ | Some title -> Printf.printf "- %s\n" title
6868+ | None -> ()
6969+ ) (Jsonfeed.items feed)
7070+| Error (`Msg err) ->
7171+ Printf.eprintf "Parse error: %s\n" err
7272+7373+(* Parse from file *)
7474+let content = In_channel.with_open_text "feed.json" In_channel.input_all in
7575+match Jsonfeed.of_string content with
7676+| Ok feed -> (* ... *)
7777+| Error _ -> (* ... *)
7878+```
7979+8080+### Content Types
8181+8282+Items can have HTML content, plain text content, or both:
8383+8484+```ocaml
8585+(* HTML only *)
8686+let item1 = Item.create
8787+ ~id:"1"
8888+ ~content:(`Html "<p>Rich <strong>HTML</strong> content</p>")
8989+ ()
9090+9191+(* Plain text only *)
9292+let item2 = Item.create
9393+ ~id:"2"
9494+ ~content:(`Text "Plain text content")
9595+ ()
9696+9797+(* Both HTML and text *)
9898+let item3 = Item.create
9999+ ~id:"3"
100100+ ~content:(`Both ("<p>HTML version</p>", "Text version"))
101101+ ()
102102+103103+(* Access content *)
104104+match Item.content_html item1 with
105105+| Some html -> Printf.printf "HTML: %s\n" html
106106+| None -> ()
107107+```
108108+109109+### Podcast Feed with Attachments
110110+111111+```ocaml
112112+(* Create an audio attachment *)
113113+let episode_audio = Attachment.create
114114+ ~url:"https://podcast.example.com/ep1.mp3"
115115+ ~mime_type:"audio/mpeg"
116116+ ~size_in_bytes:15_728_640L
117117+ ~duration_in_seconds:1800
118118+ ()
119119+120120+(* Create a podcast episode *)
121121+let episode = Item.create
122122+ ~id:"https://podcast.example.com/episodes/1"
123123+ ~title:"Episode 1: Introduction"
124124+ ~content:(`Html "<p>Welcome to the show!</p>")
125125+ ~attachments:[episode_audio]
126126+ ()
127127+```
128128+129129+## Examples
130130+131131+The `example/` directory contains several complete examples:
132132+133133+- **feed_example.ml** - Creating and serializing feeds (blog and podcast)
134134+- **feed_parser.ml** - Parsing and analyzing feeds from files
135135+- **feed_validator.ml** - Validating feeds and demonstrating various feed types
136136+137137+Run examples:
138138+139139+```bash
140140+opam exec -- dune exec -- ./example/feed_parser.exe
141141+opam exec -- dune exec -- ./example/feed_example.exe
142142+```
143143+144144+## API Documentation
145145+146146+Build the API documentation:
147147+148148+```bash
149149+opam exec -- dune build @doc
150150+```
151151+152152+Then open `_build/default/_doc/_html/index.html` in your browser.
153153+154154+## Specification
155155+156156+This library implements [JSON Feed Version 1.1](https://www.jsonfeed.org/version/1.1/).
157157+158158+## License
159159+160160+See LICENSE.md for details.