Pure OCaml xxhash implementation
at 9bd6ff45e7ea97520392b0d279bed61e591af6ce 309 lines 25 kB view raw
1<!DOCTYPE html> 2<html xmlns="http://www.w3.org/1999/xhtml"><head><title>tutorial (tutorial)</title><meta charset="utf-8"/><link rel="stylesheet" href="odoc.css"/><meta name="generator" content="odoc 3.1.0"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="highlight.pack.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body class="odoc"><nav class="odoc-nav"><a href="index.html">Up</a><a href="index.html">Index</a> &#x00BB; tutorial</nav><header class="odoc-preamble"><h1 id="yaml-tutorial"><a href="#yaml-tutorial" class="anchor"></a>YAML Tutorial</h1><p>This tutorial introduces YAML (YAML Ain't Markup Language) and demonstrates the <code>yamlrw</code> OCaml library through interactive examples. We'll start with the basics and work up to advanced features like anchors, aliases, and streaming.</p></header><div class="odoc-tocs"><nav class="odoc-toc odoc-local-toc"><ul><li><a href="#what-is-yaml?">What is YAML?</a><ul><li><a href="#yaml-vs-json">YAML vs JSON</a></li></ul></li><li><a href="#setup">Setup</a></li><li><a href="#basic-parsing">Basic Parsing</a><ul><li><a href="#boolean-values">Boolean Values</a></li><li><a href="#strings">Strings</a></li></ul></li><li><a href="#mappings-(objects)">Mappings (Objects)</a><ul><li><a href="#nested-mappings">Nested Mappings</a></li><li><a href="#accessing-values">Accessing Values</a></li></ul></li><li><a href="#sequences-(arrays)">Sequences (Arrays)</a><ul><li><a href="#sequences-of-mappings">Sequences of Mappings</a></li><li><a href="#accessing-sequence-elements">Accessing Sequence Elements</a></li></ul></li><li><a href="#serialization">Serialization</a><ul><li><a href="#constructing-values">Constructing Values</a></li><li><a href="#controlling-output-style">Controlling Output Style</a></li></ul></li><li><a href="#full-yaml-representation">Full YAML Representation</a><ul><li><a href="#scalars-with-metadata">Scalars with Metadata</a></li></ul></li><li><a href="#anchors-and-aliases">Anchors and Aliases</a><ul><li><a href="#parsing-with-aliases">Parsing with Aliases</a></li><li><a href="#preserving-aliases">Preserving Aliases</a></li></ul></li><li><a href="#multi-line-strings">Multi-line Strings</a><ul><li><a href="#literal-block-scalar">Literal Block Scalar</a></li><li><a href="#folded-block-scalar">Folded Block Scalar</a></li></ul></li><li><a href="#multiple-documents">Multiple Documents</a><ul><li><a href="#working-with-documents">Working with Documents</a></li><li><a href="#serializing-multiple-documents">Serializing Multiple Documents</a></li></ul></li><li><a href="#streaming-api">Streaming API</a><ul><li><a href="#building-yaml-with-events">Building YAML with Events</a></li></ul></li><li><a href="#error-handling">Error Handling</a><ul><li><a href="#type-errors">Type Errors</a></li></ul></li><li><a href="#common-patterns">Common Patterns</a><ul><li><a href="#configuration-files">Configuration Files</a></li><li><a href="#working-with-lists">Working with Lists</a></li><li><a href="#transforming-data">Transforming Data</a></li></ul></li><li><a href="#summary">Summary</a></li></ul></nav></div><div class="odoc-content"><h2 id="what-is-yaml?"><a href="#what-is-yaml?" class="anchor"></a>What is YAML?</h2><p>YAML is a human-readable data serialization format. It's commonly used for configuration files, data exchange, and anywhere you need structured data that humans will read and edit.</p><p>YAML is designed to be more readable than JSON or XML:</p><ul><li>No curly braces or brackets required for simple structures</li><li>Indentation defines structure (like Python)</li><li>Comments are supported</li><li>Multiple data types are recognized automatically</li></ul><h3 id="yaml-vs-json"><a href="#yaml-vs-json" class="anchor"></a>YAML vs JSON</h3><p>YAML is a superset of JSON - any valid JSON is also valid YAML. However, YAML offers additional features:</p><pre>JSON: YAML: 3{ name: Alice 4 &quot;name&quot;: &quot;Alice&quot;, age: 30 5 &quot;age&quot;: 30, active: true 6 &quot;active&quot;: true 7}</pre><p>The YAML version is cleaner for humans to read and write.</p><h2 id="setup"><a href="#setup" class="anchor"></a>Setup</h2><p>First, let's set up our environment. The library is loaded with:</p><pre class="language-ocaml"><code># open Yamlrw;;</code></pre><h2 id="basic-parsing"><a href="#basic-parsing" class="anchor"></a>Basic Parsing</h2><p>The simplest way to parse YAML is with <code>Yamlrw.of_string</code>:</p><pre class="language-ocaml"><code># let simple = of_string &quot;hello&quot;;; 8val simple : value = `String &quot;hello&quot;</code></pre><p>YAML automatically recognizes different data types:</p><pre class="language-ocaml"><code># of_string &quot;42&quot;;; 9- : value = `Float 42. 10# of_string &quot;3.14&quot;;; 11- : value = `Float 3.14 12# of_string &quot;true&quot;;; 13- : value = `Bool true 14# of_string &quot;null&quot;;; 15- : value = `Null</code></pre><p>Note that integers are stored as floats in the JSON-compatible <code>Yamlrw.value</code> type, matching the behavior of JSON parsers.</p><h3 id="boolean-values"><a href="#boolean-values" class="anchor"></a>Boolean Values</h3><p>YAML recognizes many forms of boolean values:</p><pre class="language-ocaml"><code># of_string &quot;yes&quot;;; 16- : value = `Bool true 17# of_string &quot;no&quot;;; 18- : value = `Bool false 19# of_string &quot;on&quot;;; 20- : value = `Bool true 21# of_string &quot;off&quot;;; 22- : value = `Bool false</code></pre><h3 id="strings"><a href="#strings" class="anchor"></a>Strings</h3><p>Strings can be plain, single-quoted, or double-quoted:</p><pre class="language-ocaml"><code># of_string &quot;plain text&quot;;; 23- : value = `String &quot;plain text&quot; 24# of_string &quot;'single quoted'&quot;;; 25- : value = `String &quot;single quoted&quot; 26# of_string {|&quot;double quoted&quot;|};; 27- : value = `String &quot;double quoted&quot;</code></pre><p>Quoting is useful when your string looks like another type:</p><pre class="language-ocaml"><code># of_string &quot;'123'&quot;;; 28- : value = `String &quot;123&quot; 29# of_string &quot;'true'&quot;;; 30- : value = `String &quot;true&quot;</code></pre><h2 id="mappings-(objects)"><a href="#mappings-(objects)" class="anchor"></a>Mappings (Objects)</h2><p>YAML mappings associate keys with values. In the JSON-compatible representation, these become association lists:</p><pre class="language-ocaml"><code># of_string &quot;name: Alice\nage: 30&quot;;; 31- : value = `O [(&quot;name&quot;, `String &quot;Alice&quot;); (&quot;age&quot;, `Float 30.)]</code></pre><p>Keys and values are separated by a colon and space. Each key-value pair goes on its own line.</p><h3 id="nested-mappings"><a href="#nested-mappings" class="anchor"></a>Nested Mappings</h3><p>Indentation creates nested structures:</p><pre class="language-ocaml"><code># let nested = of_string {| 32database: 33 host: localhost 34 port: 5432 35 credentials: 36 user: admin 37 pass: secret 38|};; 39val nested : value = 40 `O 41 [(&quot;database&quot;, 42 `O 43 [(&quot;host&quot;, `String &quot;localhost&quot;); (&quot;port&quot;, `Float 5432.); 44 (&quot;credentials&quot;, 45 `O [(&quot;user&quot;, `String &quot;admin&quot;); (&quot;pass&quot;, `String &quot;secret&quot;)])])]</code></pre><h3 id="accessing-values"><a href="#accessing-values" class="anchor"></a>Accessing Values</h3><p>Use the <code>Yamlrw.Util</code> module to navigate and extract values:</p><pre class="language-ocaml"><code># let db = Util.get &quot;database&quot; nested;; 46val db : Util.t = 47 `O 48 [(&quot;host&quot;, `String &quot;localhost&quot;); (&quot;port&quot;, `Float 5432.); 49 (&quot;credentials&quot;, 50 `O [(&quot;user&quot;, `String &quot;admin&quot;); (&quot;pass&quot;, `String &quot;secret&quot;)])] 51# Util.get_string (Util.get &quot;host&quot; db);; 52- : string = &quot;localhost&quot; 53# Util.get_int (Util.get &quot;port&quot; db);; 54- : int = 5432</code></pre><p>For nested access, use <code>Yamlrw.Util.get_path</code>:</p><pre class="language-ocaml"><code># Util.get_path [&quot;database&quot;; &quot;credentials&quot;; &quot;user&quot;] nested;; 55- : Util.t option = Some (`String &quot;admin&quot;) 56# Util.get_path_exn [&quot;database&quot;; &quot;port&quot;] nested;; 57- : Util.t = `Float 5432.</code></pre><h2 id="sequences-(arrays)"><a href="#sequences-(arrays)" class="anchor"></a>Sequences (Arrays)</h2><p>YAML sequences are written as bulleted lists:</p><pre class="language-ocaml"><code># of_string {| 58- apple 59- banana 60- cherry 61|};; 62- : value = `A [`String &quot;apple&quot;; `String &quot;banana&quot;; `String &quot;cherry&quot;]</code></pre><p>Or using flow style (like JSON arrays):</p><pre class="language-ocaml"><code># of_string &quot;[1, 2, 3]&quot;;; 63- : value = `A [`Float 1.; `Float 2.; `Float 3.]</code></pre><h3 id="sequences-of-mappings"><a href="#sequences-of-mappings" class="anchor"></a>Sequences of Mappings</h3><p>A common pattern is a list of objects:</p><pre class="language-ocaml"><code># let users = of_string {| 64- name: Alice 65 role: admin 66- name: Bob 67 role: user 68|};; 69val users : value = 70 `A 71 [`O [(&quot;name&quot;, `String &quot;Alice&quot;); (&quot;role&quot;, `String &quot;admin&quot;)]; 72 `O [(&quot;name&quot;, `String &quot;Bob&quot;); (&quot;role&quot;, `String &quot;user&quot;)]]</code></pre><h3 id="accessing-sequence-elements"><a href="#accessing-sequence-elements" class="anchor"></a>Accessing Sequence Elements</h3><pre class="language-ocaml"><code># Util.nth 0 users;; 73- : Util.t option = 74Some (`O [(&quot;name&quot;, `String &quot;Alice&quot;); (&quot;role&quot;, `String &quot;admin&quot;)]) 75# match Util.nth 0 users with 76 | Some user -&gt; Util.get_string (Util.get &quot;name&quot; user) 77 | None -&gt; &quot;not found&quot;;; 78- : string = &quot;Alice&quot;</code></pre><h2 id="serialization"><a href="#serialization" class="anchor"></a>Serialization</h2><p>Convert OCaml values back to YAML strings with <code>Yamlrw.to_string</code>:</p><pre class="language-ocaml"><code># let data = `O [ 79 (&quot;name&quot;, `String &quot;Bob&quot;); 80 (&quot;active&quot;, `Bool true); 81 (&quot;score&quot;, `Float 95.5) 82 ];; 83val data : 84 [&gt; `O of 85 (string * [&gt; `Bool of bool | `Float of float | `String of string ]) 86 list ] = 87 `O 88 [(&quot;name&quot;, `String &quot;Bob&quot;); (&quot;active&quot;, `Bool true); (&quot;score&quot;, `Float 95.5)] 89# print_string (to_string data);; 90name: Bob 91active: true 92score: 95.5 93- : unit = ()</code></pre><h3 id="constructing-values"><a href="#constructing-values" class="anchor"></a>Constructing Values</h3><p>Use <code>Yamlrw.Util</code> constructors for cleaner code:</p><pre class="language-ocaml"><code># let config = Util.obj [ 94 &quot;server&quot;, Util.obj [ 95 &quot;host&quot;, Util.string &quot;0.0.0.0&quot;; 96 &quot;port&quot;, Util.int 8080 97 ]; 98 &quot;debug&quot;, Util.bool true; 99 &quot;tags&quot;, Util.strings [&quot;api&quot;; &quot;v2&quot;] 100 ];; 101val config : Value.t = 102 `O 103 [(&quot;server&quot;, `O [(&quot;host&quot;, `String &quot;0.0.0.0&quot;); (&quot;port&quot;, `Float 8080.)]); 104 (&quot;debug&quot;, `Bool true); (&quot;tags&quot;, `A [`String &quot;api&quot;; `String &quot;v2&quot;])] 105# print_string (to_string config);; 106server: 107 host: 0.0.0.0 108 port: 8080 109debug: true 110tags: 111 - api 112 - v2 113- : unit = ()</code></pre><h3 id="controlling-output-style"><a href="#controlling-output-style" class="anchor"></a>Controlling Output Style</h3><p>You can control the output format with style options:</p><pre class="language-ocaml"><code># print_string (to_string ~layout_style:`Flow config);; 114{server: {host: 0.0.0.0, port: 8080}, debug: true, tags: [api, v2</code></pre><ul><li>: unit = ()</li></ul><p>Scalar styles control how strings are written:</p><pre class="language-ocaml"><code># print_string (to_string ~scalar_style:`Double_quoted (Util.string &quot;hello&quot;));; 115hello 116- : unit = () 117# print_string (to_string ~scalar_style:`Single_quoted (Util.string &quot;hello&quot;));; 118hello 119- : unit = ()</code></pre><h2 id="full-yaml-representation"><a href="#full-yaml-representation" class="anchor"></a>Full YAML Representation</h2><p>The <code>Yamlrw.value</code> type is convenient but loses some YAML-specific information. For full fidelity, use the <code>Yamlrw.yaml</code> type:</p><pre class="language-ocaml"><code># let full = yaml_of_string ~resolve_aliases:false &quot;hello&quot;;; 120val full : yaml = `Scalar &lt;abstr&gt;</code></pre><p>The <code>Yamlrw.yaml</code> type preserves:</p><ul><li>Scalar styles (plain, quoted, literal, folded)</li><li>Anchors and aliases</li><li>Type tags</li><li>Collection styles (block vs flow)</li></ul><h3 id="scalars-with-metadata"><a href="#scalars-with-metadata" class="anchor"></a>Scalars with Metadata</h3><pre class="language-ocaml"><code># let s = yaml_of_string ~resolve_aliases:false &quot;'quoted string'&quot;;; 121val s : yaml = `Scalar &lt;abstr&gt; 122# match s with 123 | `Scalar sc -&gt; Scalar.value sc, Scalar.style sc 124 | _ -&gt; &quot;&quot;, `Any;; 125- : string * Scalar_style.t = (&quot;quoted string&quot;, `Single_quoted)</code></pre><h2 id="anchors-and-aliases"><a href="#anchors-and-aliases" class="anchor"></a>Anchors and Aliases</h2><p>YAML supports node reuse through anchors (<code>&amp;name</code>) and aliases (<code>*name</code>). This is powerful for avoiding repetition:</p><pre>defaults: &amp;defaults 126 timeout: 30 127 retries: 3 128 129production: 130 &lt;&lt;: *defaults 131 host: prod.example.com 132 133staging: 134 &lt;&lt;: *defaults 135 host: stage.example.com</pre><h3 id="parsing-with-aliases"><a href="#parsing-with-aliases" class="anchor"></a>Parsing with Aliases</h3><p>By default, <code>Yamlrw.of_string</code> resolves aliases:</p><pre class="language-ocaml"><code># let yaml_with_alias = {| 136base: &amp;base 137 x: 1 138 y: 2 139derived: 140 &lt;&lt;: *base 141 z: 3 142|};; 143val yaml_with_alias : string = 144 &quot;\nbase: &amp;base\n x: 1\n y: 2\nderived:\n &lt;&lt;: *base\n z: 3\n&quot; 145# of_string yaml_with_alias;; 146- : value = 147`O 148 [(&quot;base&quot;, `O [(&quot;x&quot;, `Float 1.); (&quot;y&quot;, `Float 2.)]); 149 (&quot;derived&quot;, `O [(&quot;x&quot;, `Float 1.); (&quot;y&quot;, `Float 2.); (&quot;z&quot;, `Float 3.)])]</code></pre><h3 id="preserving-aliases"><a href="#preserving-aliases" class="anchor"></a>Preserving Aliases</h3><p>To preserve the alias structure, use <code>Yamlrw.yaml_of_string</code> with <code>~resolve_aliases:false</code>:</p><pre class="language-ocaml"><code># let y = yaml_of_string ~resolve_aliases:false {| 150item: &amp;ref 151 name: shared 152copy: *ref 153|};; 154val y : yaml = 155 `O 156 &lt;abstr&gt;</code></pre><h2 id="multi-line-strings"><a href="#multi-line-strings" class="anchor"></a>Multi-line Strings</h2><p>YAML has special syntax for multi-line strings:</p><h3 id="literal-block-scalar"><a href="#literal-block-scalar" class="anchor"></a>Literal Block Scalar</h3><p>The <code>|</code> indicator preserves newlines exactly:</p><pre class="language-ocaml"><code># of_string {| 157description: | 158 This is a 159 multi-line 160 string. 161|};; 162- : value = `O [(&quot;description&quot;, `String &quot;This is a\nmulti-line\nstring.\n&quot;)]</code></pre><h3 id="folded-block-scalar"><a href="#folded-block-scalar" class="anchor"></a>Folded Block Scalar</h3><p>The <code>&gt;</code> indicator folds newlines into spaces:</p><pre class="language-ocaml"><code># of_string {| 163description: &gt; 164 This is a 165 single line 166 when folded. 167|};; 168- : value = `O [(&quot;description&quot;, `String &quot;This is a single line when folded.\n&quot;)]</code></pre><h2 id="multiple-documents"><a href="#multiple-documents" class="anchor"></a>Multiple Documents</h2><p>A YAML stream can contain multiple documents separated by <code>---</code>:</p><pre class="language-ocaml"><code># let docs = documents_of_string {| 169--- 170name: first 171--- 172name: second 173... 174|};; 175val docs : document list = [&lt;abstr&gt;; &lt;abstr&gt;] 176# List.length docs;; 177- : int = 2</code></pre><p>The <code>---</code> marker starts a document, and <code>...</code> optionally ends it.</p><h3 id="working-with-documents"><a href="#working-with-documents" class="anchor"></a>Working with Documents</h3><p>Each document has metadata and a root value:</p><pre class="language-ocaml"><code># List.map (fun d -&gt; Document.root d) docs;; 178- : Yaml.t option list = 179[Some (`O &lt;abstr&gt;); Some (`O &lt;abstr&gt;)]</code></pre><h3 id="serializing-multiple-documents"><a href="#serializing-multiple-documents" class="anchor"></a>Serializing Multiple Documents</h3><pre class="language-ocaml"><code># let doc1 = Document.make (Some (of_json (Util.obj [&quot;x&quot;, Util.int 1])));; 180val doc1 : Document.t = 181 {Document.version = None; tags = []; root = Some (`O &lt;abstr&gt;); 182 implicit_start = true; implicit_end = true} 183# let doc2 = Document.make (Some (of_json (Util.obj [&quot;x&quot;, Util.int 2])));; 184val doc2 : Document.t = 185 {Document.version = None; tags = []; root = Some (`O &lt;abstr&gt;); 186 implicit_start = true; implicit_end = true} 187# print_string (documents_to_string [doc1; doc2]);; 188x: 1 189--- 190x: 2 191- : unit = ()</code></pre><h2 id="streaming-api"><a href="#streaming-api" class="anchor"></a>Streaming API</h2><p>For large files or fine-grained control, use the streaming API:</p><pre class="language-ocaml"><code># let parser = Stream.parser &quot;key: value&quot;;; 192val parser : Stream.parser = &lt;abstr&gt;</code></pre><p>Iterate over events:</p><pre class="language-ocaml"><code># Stream.iter (fun event _ _ -&gt; 193 Format.printf &quot;%a@.&quot; Event.pp event 194 ) parser;; 195stream-start(UTF-8) 196document-start(version=none, implicit=true) 197mapping-start(anchor=none, tag=none, implicit=true, style=block) 198scalar(anchor=none, tag=none, style=plain, value=&quot;key&quot;) 199scalar(anchor=none, tag=none, style=plain, value=&quot;value&quot;) 200mapping-end 201document-end(implicit=true) 202stream-end 203- : unit = ()</code></pre><h3 id="building-yaml-with-events"><a href="#building-yaml-with-events" class="anchor"></a>Building YAML with Events</h3><p>You can also emit YAML by sending events:</p><pre class="language-ocaml"><code># let emitter = Stream.emitter ();; 204val emitter : Stream.emitter = &lt;abstr&gt; 205# Stream.stream_start emitter `Utf8;; 206- : unit = () 207# Stream.document_start emitter ();; 208- : unit = () 209# Stream.mapping_start emitter ();; 210- : unit = () 211# Stream.scalar emitter &quot;greeting&quot;;; 212- : unit = () 213# Stream.scalar emitter &quot;Hello, World!&quot;;; 214- : unit = () 215# Stream.mapping_end emitter;; 216- : unit = () 217# Stream.document_end emitter ();; 218- : unit = () 219# Stream.stream_end emitter;; 220- : unit = () 221# print_string (Stream.contents emitter);; 222greeting: Hello, World! 223- : unit = ()</code></pre><h2 id="error-handling"><a href="#error-handling" class="anchor"></a>Error Handling</h2><p>Parse errors raise <code>Yamlrw.Yamlrw_error</code>:</p><pre class="language-ocaml"><code># try 224 ignore (of_string &quot;key: [unclosed&quot;); 225 &quot;ok&quot; 226 with Yamlrw_error e -&gt; 227 Error.to_string e;; 228- : string = &quot;expected sequence end ']' at line 1, columns 15-15&quot;</code></pre><h3 id="type-errors"><a href="#type-errors" class="anchor"></a>Type Errors</h3><p>The <code>Yamlrw.Util</code> module raises <code>Yamlrw.Util.Type_error</code> for type mismatches:</p><pre class="language-ocaml"><code># try 229 ignore (Util.get_string (`Float 42.)); 230 &quot;ok&quot; 231 with Util.Type_error (expected, actual) -&gt; 232 Printf.sprintf &quot;expected %s, got %s&quot; expected (Value.type_name actual);; 233- : string = &quot;expected string, got float&quot;</code></pre><h2 id="common-patterns"><a href="#common-patterns" class="anchor"></a>Common Patterns</h2><h3 id="configuration-files"><a href="#configuration-files" class="anchor"></a>Configuration Files</h3><p>A typical configuration file pattern:</p><pre class="language-ocaml"><code># let config_yaml = {| 234app: 235 name: myapp 236 version: 1.0.0 237 238server: 239 host: 0.0.0.0 240 port: 8080 241 ssl: true 242 243database: 244 url: postgres://localhost/mydb 245 pool_size: 10 246|};; 247val config_yaml : string = 248 &quot;app:\n name: myapp\n version: 1.0.0\n\nserver:\n host: 0.0.0.0\n port: 8080\n ssl: true\n\ndatabase:\n url: postgres://localhost/mydb\n pool_size: 10\n&quot; 249# let config = of_string config_yaml;; 250val config : value = 251 `O 252 [(&quot;app&quot;, `O [(&quot;name&quot;, `String &quot;myapp&quot;); (&quot;version&quot;, `Float 1.)]); 253 (&quot;server&quot;, 254 `O 255 [(&quot;host&quot;, `String &quot;0.0.0.0&quot;); (&quot;port&quot;, `Float 8080.); 256 (&quot;ssl&quot;, `Bool true)]); 257 (&quot;database&quot;, 258 `O 259 [(&quot;url&quot;, `String &quot;postgres://localhost/mydb&quot;); 260 (&quot;pool_size&quot;, `Float 10.)])] 261# let server = Util.get &quot;server&quot; config;; 262val server : Util.t = 263 `O 264 [(&quot;host&quot;, `String &quot;0.0.0.0&quot;); (&quot;port&quot;, `Float 8080.); (&quot;ssl&quot;, `Bool true)] 265# let host = Util.to_string ~default:&quot;localhost&quot; (Util.get &quot;host&quot; server);; 266val host : string = &quot;0.0.0.0&quot; 267# let port = Util.to_int ~default:80 (Util.get &quot;port&quot; server);; 268val port : int = 8080</code></pre><h3 id="working-with-lists"><a href="#working-with-lists" class="anchor"></a>Working with Lists</h3><p>Processing lists of items:</p><pre class="language-ocaml"><code># let items_yaml = {| 269items: 270 - id: 1 271 name: Widget 272 price: 9.99 273 - id: 2 274 name: Gadget 275 price: 19.99 276 - id: 3 277 name: Gizmo 278 price: 29.99 279|};; 280val items_yaml : string = 281 &quot;items:\n - id: 1\n name: Widget\n price: 9.99\n - id: 2\n name: Gadget\n price: 19.99\n - id: 3\n name: Gizmo\n price: 29.99\n&quot; 282# let items = Util.get_list (Util.get &quot;items&quot; (of_string items_yaml));; 283val items : Util.t list = 284 [`O [(&quot;id&quot;, `Float 1.); (&quot;name&quot;, `String &quot;Widget&quot;); (&quot;price&quot;, `Float 9.99)]; 285 `O [(&quot;id&quot;, `Float 2.); (&quot;name&quot;, `String &quot;Gadget&quot;); (&quot;price&quot;, `Float 19.99)]; 286 `O [(&quot;id&quot;, `Float 3.); (&quot;name&quot;, `String &quot;Gizmo&quot;); (&quot;price&quot;, `Float 29.99)]] 287# let names = List.map (fun item -&gt; 288 Util.get_string (Util.get &quot;name&quot; item) 289 ) items;; 290val names : string list = [&quot;Widget&quot;; &quot;Gadget&quot;; &quot;Gizmo&quot;] 291# let total = List.fold_left (fun acc item -&gt; 292 acc +. Util.get_float (Util.get &quot;price&quot; item) 293 ) 0. items;; 294val total : float = 59.97</code></pre><h3 id="transforming-data"><a href="#transforming-data" class="anchor"></a>Transforming Data</h3><p>Modifying YAML structures:</p><pre class="language-ocaml"><code># let original = of_string &quot;name: Alice\nstatus: active&quot;;; 295val original : value = 296 `O [(&quot;name&quot;, `String &quot;Alice&quot;); (&quot;status&quot;, `String &quot;active&quot;)] 297# let updated = Util.update &quot;status&quot; (Util.string &quot;inactive&quot;) original;; 298val updated : Value.t = 299 `O [(&quot;name&quot;, `String &quot;Alice&quot;); (&quot;status&quot;, `String &quot;inactive&quot;)] 300# let with_timestamp = Util.update &quot;updated_at&quot; (Util.string &quot;2024-01-01&quot;) updated;; 301val with_timestamp : Value.t = 302 `O 303 [(&quot;name&quot;, `String &quot;Alice&quot;); (&quot;status&quot;, `String &quot;inactive&quot;); 304 (&quot;updated_at&quot;, `String &quot;2024-01-01&quot;)] 305# print_string (to_string with_timestamp);; 306name: Alice 307status: inactive 308updated_at: 2024-01-01 309- : unit = ()</code></pre><h2 id="summary"><a href="#summary" class="anchor"></a>Summary</h2><p>The <code>yamlrw</code> library provides:</p><ol><li><b>Simple parsing</b>: <code>Yamlrw.of_string</code> for JSON-compatible values</li><li><b>Full fidelity</b>: <code>Yamlrw.yaml_of_string</code> preserves all YAML metadata</li><li><b>Easy serialization</b>: <code>Yamlrw.to_string</code> with style options</li><li><b>Navigation</b>: <code>Yamlrw.Util</code> module for accessing and modifying values</li><li><b>Multi-document</b>: <code>Yamlrw.documents_of_string</code> for YAML streams</li><li><b>Streaming</b>: <code>Yamlrw.Stream</code> module for event-based processing</li></ol><p>Key types:</p><ul><li><code>Yamlrw.value</code> - JSON-compatible representation (<code>`Null</code>, <code>`Bool</code>, <code>`Float</code>, <code>`String</code>, <code>`A</code>, <code>`O</code>)</li><li><code>Yamlrw.yaml</code> - Full YAML with scalars, anchors, aliases, and metadata</li><li><code>Yamlrw.document</code> - A complete document with directives</li></ul><p>For more details, see the <span class="xref-unresolved" title="Yamlrw">API reference</span>.</p></div></body></html>