OCaml codecs for the Citation File Format (CFF)
1(*---------------------------------------------------------------------------
2 Copyright (c) 2026 The ocaml-cff programmers. All rights reserved.
3 SPDX-License-Identifier: ISC
4 ---------------------------------------------------------------------------*)
5
6(** Reference type for CFF with logical sub-records. *)
7
8(** Core identity of a reference. *)
9module Core = struct
10 type t =
11 { type_ : Cff_enums.Reference_type.t
12 ; title : string
13 ; authors : Cff_author.t list
14 ; abstract : string option
15 ; abbreviation : string option
16 }
17
18 let make ~type_ ~title ~authors ?abstract ?abbreviation () =
19 { type_; title; authors; abstract; abbreviation }
20 ;;
21
22 let type_ t = t.type_
23 let title t = t.title
24 let authors t = t.authors
25 let abstract t = t.abstract
26 let abbreviation t = t.abbreviation
27 let pp ppf t = Format.fprintf ppf "%s (%a)" t.title Cff_enums.Reference_type.pp t.type_
28end
29
30(** Publication information (journal, volume, pages, etc.). *)
31module Publication = struct
32 type t =
33 { journal : string option
34 ; volume : string option
35 ; issue : string option
36 ; pages : string option
37 ; start : string option
38 ; end_ : string option
39 ; edition : string option
40 ; section : string option
41 ; status : Cff_enums.Status.t option
42 }
43
44 let empty =
45 { journal = None
46 ; volume = None
47 ; issue = None
48 ; pages = None
49 ; start = None
50 ; end_ = None
51 ; edition = None
52 ; section = None
53 ; status = None
54 }
55 ;;
56
57 let make ?journal ?volume ?issue ?pages ?start ?end_ ?edition ?section ?status () =
58 { journal; volume; issue; pages; start; end_; edition; section; status }
59 ;;
60
61 let journal t = t.journal
62 let volume t = t.volume
63 let issue t = t.issue
64 let pages t = t.pages
65 let start t = t.start
66 let end_ t = t.end_
67 let edition t = t.edition
68 let section t = t.section
69 let status t = t.status
70
71 let is_empty t =
72 t.journal = None
73 && t.volume = None
74 && t.issue = None
75 && t.pages = None
76 && t.start = None
77 && t.end_ = None
78 && t.edition = None
79 && t.section = None
80 && t.status = None
81 ;;
82end
83
84(** Collection information (proceedings, book series, etc.). *)
85module Collection = struct
86 type t =
87 { collection_title : string option
88 ; collection_type : string option
89 ; collection_doi : string option
90 ; volume_title : string option
91 ; number_volumes : string option
92 }
93
94 let empty =
95 { collection_title = None
96 ; collection_type = None
97 ; collection_doi = None
98 ; volume_title = None
99 ; number_volumes = None
100 }
101 ;;
102
103 let make
104 ?collection_title
105 ?collection_type
106 ?collection_doi
107 ?volume_title
108 ?number_volumes
109 ()
110 =
111 { collection_title; collection_type; collection_doi; volume_title; number_volumes }
112 ;;
113
114 let collection_title t = t.collection_title
115 let collection_type t = t.collection_type
116 let collection_doi t = t.collection_doi
117 let volume_title t = t.volume_title
118 let number_volumes t = t.number_volumes
119
120 let is_empty t =
121 t.collection_title = None
122 && t.collection_type = None
123 && t.collection_doi = None
124 && t.volume_title = None
125 && t.number_volumes = None
126 ;;
127end
128
129(** Date information. *)
130module Dates = struct
131 type t =
132 { date_accessed : Cff_date.t option
133 ; date_downloaded : Cff_date.t option
134 ; date_published : Cff_date.t option
135 ; date_released : Cff_date.t option
136 ; year : int option
137 ; year_original : int option
138 ; month : int option
139 ; issue_date : string option
140 }
141
142 let empty =
143 { date_accessed = None
144 ; date_downloaded = None
145 ; date_published = None
146 ; date_released = None
147 ; year = None
148 ; year_original = None
149 ; month = None
150 ; issue_date = None
151 }
152 ;;
153
154 let make
155 ?date_accessed
156 ?date_downloaded
157 ?date_published
158 ?date_released
159 ?year
160 ?year_original
161 ?month
162 ?issue_date
163 ()
164 =
165 { date_accessed
166 ; date_downloaded
167 ; date_published
168 ; date_released
169 ; year
170 ; year_original
171 ; month
172 ; issue_date
173 }
174 ;;
175
176 let date_accessed t = t.date_accessed
177 let date_downloaded t = t.date_downloaded
178 let date_published t = t.date_published
179 let date_released t = t.date_released
180 let year t = t.year
181 let year_original t = t.year_original
182 let month t = t.month
183 let issue_date t = t.issue_date
184
185 let is_empty t =
186 t.date_accessed = None
187 && t.date_downloaded = None
188 && t.date_published = None
189 && t.date_released = None
190 && t.year = None
191 && t.year_original = None
192 && t.month = None
193 && t.issue_date = None
194 ;;
195end
196
197(** Identifiers and links. *)
198module Identifiers = struct
199 type t =
200 { doi : string option
201 ; url : string option
202 ; repository : string option
203 ; repository_code : string option
204 ; repository_artifact : string option
205 ; isbn : string option
206 ; issn : string option
207 ; pmcid : string option
208 ; nihmsid : string option
209 ; identifiers : Cff_identifier.t list option
210 }
211
212 let empty =
213 { doi = None
214 ; url = None
215 ; repository = None
216 ; repository_code = None
217 ; repository_artifact = None
218 ; isbn = None
219 ; issn = None
220 ; pmcid = None
221 ; nihmsid = None
222 ; identifiers = None
223 }
224 ;;
225
226 let make
227 ?doi
228 ?url
229 ?repository
230 ?repository_code
231 ?repository_artifact
232 ?isbn
233 ?issn
234 ?pmcid
235 ?nihmsid
236 ?identifiers
237 ()
238 =
239 { doi
240 ; url
241 ; repository
242 ; repository_code
243 ; repository_artifact
244 ; isbn
245 ; issn
246 ; pmcid
247 ; nihmsid
248 ; identifiers
249 }
250 ;;
251
252 let doi t = t.doi
253 let url t = t.url
254 let repository t = t.repository
255 let repository_code t = t.repository_code
256 let repository_artifact t = t.repository_artifact
257 let isbn t = t.isbn
258 let issn t = t.issn
259 let pmcid t = t.pmcid
260 let nihmsid t = t.nihmsid
261 let identifiers t = t.identifiers
262
263 let is_empty t =
264 t.doi = None
265 && t.url = None
266 && t.repository = None
267 && t.repository_code = None
268 && t.repository_artifact = None
269 && t.isbn = None
270 && t.issn = None
271 && t.pmcid = None
272 && t.nihmsid = None
273 && t.identifiers = None
274 ;;
275end
276
277(** Related entities (editors, publisher, etc.). *)
278module Entities = struct
279 type t =
280 { editors : Cff_author.t list option
281 ; editors_series : Cff_author.t list option
282 ; translators : Cff_author.t list option
283 ; recipients : Cff_author.t list option
284 ; senders : Cff_author.t list option
285 ; contact : Cff_author.t list option
286 ; publisher : Cff_author.Entity.t option
287 ; institution : Cff_author.Entity.t option
288 ; conference : Cff_author.Entity.t option
289 ; database_provider : Cff_author.Entity.t option
290 ; location : Cff_author.Entity.t option
291 }
292
293 let empty =
294 { editors = None
295 ; editors_series = None
296 ; translators = None
297 ; recipients = None
298 ; senders = None
299 ; contact = None
300 ; publisher = None
301 ; institution = None
302 ; conference = None
303 ; database_provider = None
304 ; location = None
305 }
306 ;;
307
308 let make
309 ?editors
310 ?editors_series
311 ?translators
312 ?recipients
313 ?senders
314 ?contact
315 ?publisher
316 ?institution
317 ?conference
318 ?database_provider
319 ?location
320 ()
321 =
322 { editors
323 ; editors_series
324 ; translators
325 ; recipients
326 ; senders
327 ; contact
328 ; publisher
329 ; institution
330 ; conference
331 ; database_provider
332 ; location
333 }
334 ;;
335
336 let editors t = t.editors
337 let editors_series t = t.editors_series
338 let translators t = t.translators
339 let recipients t = t.recipients
340 let senders t = t.senders
341 let contact t = t.contact
342 let publisher t = t.publisher
343 let institution t = t.institution
344 let conference t = t.conference
345 let database_provider t = t.database_provider
346 let location t = t.location
347
348 let is_empty t =
349 t.editors = None
350 && t.editors_series = None
351 && t.translators = None
352 && t.recipients = None
353 && t.senders = None
354 && t.contact = None
355 && t.publisher = None
356 && t.institution = None
357 && t.conference = None
358 && t.database_provider = None
359 && t.location = None
360 ;;
361end
362
363(** Metadata and description. *)
364module Metadata = struct
365 type t =
366 { keywords : string list option
367 ; languages : string list option
368 ; license : Cff_license.t option
369 ; copyright : string option
370 ; scope : string option
371 ; notes : string option
372 }
373
374 let empty =
375 { keywords = None
376 ; languages = None
377 ; license = None
378 ; copyright = None
379 ; scope = None
380 ; notes = None
381 }
382 ;;
383
384 let make ?keywords ?languages ?license ?copyright ?scope ?notes () =
385 { keywords; languages; license; copyright; scope; notes }
386 ;;
387
388 let keywords t = t.keywords
389 let languages t = t.languages
390 let license t = t.license
391 let copyright t = t.copyright
392 let scope t = t.scope
393 let notes t = t.notes
394
395 let is_empty t =
396 t.keywords = None
397 && t.languages = None
398 && t.license = None
399 && t.copyright = None
400 && t.scope = None
401 && t.notes = None
402 ;;
403end
404
405(** Technical and domain-specific fields. *)
406module Technical = struct
407 type t =
408 { commit : string option
409 ; version : string option
410 ; filename : string option
411 ; format : string option
412 ; medium : string option
413 ; data_type : string option
414 ; database : string option
415 ; number : string option
416 ; patent_states : string list option
417 ; thesis_type : string option
418 ; term : string option
419 ; entry : string option
420 ; department : string option
421 ; loc_start : string option
422 ; loc_end : string option
423 }
424
425 let empty =
426 { commit = None
427 ; version = None
428 ; filename = None
429 ; format = None
430 ; medium = None
431 ; data_type = None
432 ; database = None
433 ; number = None
434 ; patent_states = None
435 ; thesis_type = None
436 ; term = None
437 ; entry = None
438 ; department = None
439 ; loc_start = None
440 ; loc_end = None
441 }
442 ;;
443
444 let make
445 ?commit
446 ?version
447 ?filename
448 ?format
449 ?medium
450 ?data_type
451 ?database
452 ?number
453 ?patent_states
454 ?thesis_type
455 ?term
456 ?entry
457 ?department
458 ?loc_start
459 ?loc_end
460 ()
461 =
462 { commit
463 ; version
464 ; filename
465 ; format
466 ; medium
467 ; data_type
468 ; database
469 ; number
470 ; patent_states
471 ; thesis_type
472 ; term
473 ; entry
474 ; department
475 ; loc_start
476 ; loc_end
477 }
478 ;;
479
480 let commit t = t.commit
481 let version t = t.version
482 let filename t = t.filename
483 let format t = t.format
484 let medium t = t.medium
485 let data_type t = t.data_type
486 let database t = t.database
487 let number t = t.number
488 let patent_states t = t.patent_states
489 let thesis_type t = t.thesis_type
490 let term t = t.term
491 let entry t = t.entry
492 let department t = t.department
493 let loc_start t = t.loc_start
494 let loc_end t = t.loc_end
495
496 let is_empty t =
497 t.commit = None
498 && t.version = None
499 && t.filename = None
500 && t.format = None
501 && t.medium = None
502 && t.data_type = None
503 && t.database = None
504 && t.number = None
505 && t.patent_states = None
506 && t.thesis_type = None
507 && t.term = None
508 && t.entry = None
509 && t.department = None
510 && t.loc_start = None
511 && t.loc_end = None
512 ;;
513end
514
515(** Complete reference type. *)
516type t =
517 { core : Core.t
518 ; publication : Publication.t
519 ; collection : Collection.t
520 ; dates : Dates.t
521 ; identifiers : Identifiers.t
522 ; entities : Entities.t
523 ; metadata : Metadata.t
524 ; technical : Technical.t
525 }
526
527let make
528 ~core
529 ?(publication = Publication.empty)
530 ?(collection = Collection.empty)
531 ?(dates = Dates.empty)
532 ?(identifiers = Identifiers.empty)
533 ?(entities = Entities.empty)
534 ?(metadata = Metadata.empty)
535 ?(technical = Technical.empty)
536 ()
537 =
538 { core; publication; collection; dates; identifiers; entities; metadata; technical }
539;;
540
541let make_simple ~type_ ~title ~authors ?doi ?year ?journal () =
542 let core = Core.make ~type_ ~title ~authors () in
543 let publication = Publication.make ?journal () in
544 let dates = Dates.make ?year () in
545 let identifiers = Identifiers.make ?doi () in
546 make ~core ~publication ~dates ~identifiers ()
547;;
548
549(* Accessors for sub-records *)
550let core t = t.core
551let publication t = t.publication
552let collection t = t.collection
553let dates t = t.dates
554let identifiers t = t.identifiers
555let entities t = t.entities
556let metadata t = t.metadata
557let technical t = t.technical
558
559(* Direct accessors for common fields *)
560let type_ t = Core.type_ t.core
561let title t = Core.title t.core
562let authors t = Core.authors t.core
563let doi t = Identifiers.doi t.identifiers
564let year t = Dates.year t.dates
565let pp ppf t = Core.pp ppf t.core
566
567(* Helper for string that can also be int (for pages, etc.) *)
568let string_or_int_jsont =
569 Jsont.any
570 ~dec_number:
571 (Jsont.number
572 |> Jsont.map ~dec:(fun f -> string_of_int (int_of_float f)) ~enc:float_of_string)
573 ~dec_string:Jsont.string
574 ~enc:(fun s ->
575 match float_of_string_opt s with
576 | Some _ ->
577 Jsont.number |> Jsont.map ~dec:(fun _ -> assert false) ~enc:float_of_string
578 | None -> Jsont.string)
579 ()
580;;
581
582(* Helper to convert array jsont to list jsont *)
583let list_jsont elt =
584 Jsont.(array elt |> map ~dec:Stdlib.Array.to_list ~enc:Stdlib.Array.of_list)
585;;
586
587(* Jsont codec for the full reference type *)
588let jsont =
589 let authors_list_jsont = list_jsont Cff_author.jsont in
590 let identifiers_list_jsont = list_jsont Cff_identifier.jsont in
591 let string_list_jsont = list_jsont Jsont.string in
592 (* We need to decode all 60+ fields and then group into sub-records *)
593 Jsont.Object.map
594 ~kind:"Reference"
595 (fun
596 type_
597 title
598 authors
599 abstract
600 abbreviation
601 (* Publication *)
602 journal
603 volume
604 issue
605 pages
606 start
607 end_
608 edition
609 section
610 status
611 (* Collection *)
612 collection_title
613 collection_type
614 collection_doi
615 volume_title
616 number_volumes
617 (* Dates *)
618 date_accessed
619 date_downloaded
620 date_published
621 date_released
622 year
623 year_original
624 month
625 issue_date
626 (* Identifiers *)
627 doi
628 url
629 repository
630 repository_code
631 repository_artifact
632 isbn
633 issn
634 pmcid
635 nihmsid
636 identifiers_list
637 (* Entities *)
638 editors
639 editors_series
640 translators
641 recipients
642 senders
643 contact
644 publisher
645 institution
646 conference
647 database_provider
648 location_entity
649 (* Metadata *)
650 keywords
651 languages
652 license
653 license_url
654 copyright
655 scope
656 notes
657 (* Technical *)
658 commit
659 version
660 filename
661 format
662 medium
663 data_type
664 database
665 number
666 patent_states
667 thesis_type
668 term
669 entry
670 department
671 loc_start
672 loc_end
673 ->
674 let core = { Core.type_; title; authors; abstract; abbreviation } in
675 let publication =
676 { Publication.journal
677 ; volume
678 ; issue
679 ; pages
680 ; start
681 ; end_
682 ; edition
683 ; section
684 ; status
685 }
686 in
687 let collection =
688 { Collection.collection_title
689 ; collection_type
690 ; collection_doi
691 ; volume_title
692 ; number_volumes
693 }
694 in
695 let dates =
696 { Dates.date_accessed
697 ; date_downloaded
698 ; date_published
699 ; date_released
700 ; year
701 ; year_original
702 ; month
703 ; issue_date
704 }
705 in
706 let identifiers =
707 { Identifiers.doi
708 ; url
709 ; repository
710 ; repository_code
711 ; repository_artifact
712 ; isbn
713 ; issn
714 ; pmcid
715 ; nihmsid
716 ; identifiers = identifiers_list
717 }
718 in
719 let entities =
720 { Entities.editors
721 ; editors_series
722 ; translators
723 ; recipients
724 ; senders
725 ; contact
726 ; publisher
727 ; institution
728 ; conference
729 ; database_provider
730 ; location = location_entity
731 }
732 in
733 let license = Option.map (Cff_license.with_url_opt license_url) license in
734 let metadata =
735 { Metadata.keywords; languages; license; copyright; scope; notes }
736 in
737 let technical =
738 { Technical.commit
739 ; version
740 ; filename
741 ; format
742 ; medium
743 ; data_type
744 ; database
745 ; number
746 ; patent_states
747 ; thesis_type
748 ; term
749 ; entry
750 ; department
751 ; loc_start
752 ; loc_end
753 }
754 in
755 { core
756 ; publication
757 ; collection
758 ; dates
759 ; identifiers
760 ; entities
761 ; metadata
762 ; technical
763 })
764 (* Core fields *)
765 |> Jsont.Object.mem "type" Cff_enums.Reference_type.jsont ~enc:(fun r -> r.core.type_)
766 |> Jsont.Object.mem "title" Jsont.string ~enc:(fun r -> r.core.title)
767 |> Jsont.Object.mem "authors" authors_list_jsont ~enc:(fun r -> r.core.authors)
768 |> Jsont.Object.opt_mem "abstract" Jsont.string ~enc:(fun r -> r.core.abstract)
769 |> Jsont.Object.opt_mem "abbreviation" Jsont.string ~enc:(fun r -> r.core.abbreviation)
770 (* Publication fields *)
771 |> Jsont.Object.opt_mem "journal" Jsont.string ~enc:(fun r -> r.publication.journal)
772 |> Jsont.Object.opt_mem "volume" string_or_int_jsont ~enc:(fun r ->
773 r.publication.volume)
774 |> Jsont.Object.opt_mem "issue" string_or_int_jsont ~enc:(fun r -> r.publication.issue)
775 |> Jsont.Object.opt_mem "pages" string_or_int_jsont ~enc:(fun r -> r.publication.pages)
776 |> Jsont.Object.opt_mem "start" string_or_int_jsont ~enc:(fun r -> r.publication.start)
777 |> Jsont.Object.opt_mem "end" string_or_int_jsont ~enc:(fun r -> r.publication.end_)
778 |> Jsont.Object.opt_mem "edition" Jsont.string ~enc:(fun r -> r.publication.edition)
779 |> Jsont.Object.opt_mem "section" string_or_int_jsont ~enc:(fun r ->
780 r.publication.section)
781 |> Jsont.Object.opt_mem "status" Cff_enums.Status.jsont ~enc:(fun r ->
782 r.publication.status)
783 (* Collection fields *)
784 |> Jsont.Object.opt_mem "collection-title" Jsont.string ~enc:(fun r ->
785 r.collection.collection_title)
786 |> Jsont.Object.opt_mem "collection-type" Jsont.string ~enc:(fun r ->
787 r.collection.collection_type)
788 |> Jsont.Object.opt_mem "collection-doi" Jsont.string ~enc:(fun r ->
789 r.collection.collection_doi)
790 |> Jsont.Object.opt_mem "volume-title" Jsont.string ~enc:(fun r ->
791 r.collection.volume_title)
792 |> Jsont.Object.opt_mem "number-volumes" string_or_int_jsont ~enc:(fun r ->
793 r.collection.number_volumes)
794 (* Date fields *)
795 |> Jsont.Object.opt_mem "date-accessed" Cff_date.jsont ~enc:(fun r ->
796 r.dates.date_accessed)
797 |> Jsont.Object.opt_mem "date-downloaded" Cff_date.jsont ~enc:(fun r ->
798 r.dates.date_downloaded)
799 |> Jsont.Object.opt_mem "date-published" Cff_date.jsont ~enc:(fun r ->
800 r.dates.date_published)
801 |> Jsont.Object.opt_mem "date-released" Cff_date.jsont ~enc:(fun r ->
802 r.dates.date_released)
803 |> Jsont.Object.opt_mem "year" Jsont.int ~enc:(fun r -> r.dates.year)
804 |> Jsont.Object.opt_mem "year-original" Jsont.int ~enc:(fun r -> r.dates.year_original)
805 |> Jsont.Object.opt_mem "month" Jsont.int ~enc:(fun r -> r.dates.month)
806 |> Jsont.Object.opt_mem "issue-date" Jsont.string ~enc:(fun r -> r.dates.issue_date)
807 (* Identifier fields *)
808 |> Jsont.Object.opt_mem "doi" Jsont.string ~enc:(fun r -> r.identifiers.doi)
809 |> Jsont.Object.opt_mem "url" Jsont.string ~enc:(fun r -> r.identifiers.url)
810 |> Jsont.Object.opt_mem "repository" Jsont.string ~enc:(fun r ->
811 r.identifiers.repository)
812 |> Jsont.Object.opt_mem "repository-code" Jsont.string ~enc:(fun r ->
813 r.identifiers.repository_code)
814 |> Jsont.Object.opt_mem "repository-artifact" Jsont.string ~enc:(fun r ->
815 r.identifiers.repository_artifact)
816 |> Jsont.Object.opt_mem "isbn" Jsont.string ~enc:(fun r -> r.identifiers.isbn)
817 |> Jsont.Object.opt_mem "issn" string_or_int_jsont ~enc:(fun r -> r.identifiers.issn)
818 |> Jsont.Object.opt_mem "pmcid" Jsont.string ~enc:(fun r -> r.identifiers.pmcid)
819 |> Jsont.Object.opt_mem "nihmsid" Jsont.string ~enc:(fun r -> r.identifiers.nihmsid)
820 |> Jsont.Object.opt_mem "identifiers" identifiers_list_jsont ~enc:(fun r ->
821 r.identifiers.identifiers)
822 (* Entity fields *)
823 |> Jsont.Object.opt_mem "editors" authors_list_jsont ~enc:(fun r -> r.entities.editors)
824 |> Jsont.Object.opt_mem "editors-series" authors_list_jsont ~enc:(fun r ->
825 r.entities.editors_series)
826 |> Jsont.Object.opt_mem "translators" authors_list_jsont ~enc:(fun r ->
827 r.entities.translators)
828 |> Jsont.Object.opt_mem "recipients" authors_list_jsont ~enc:(fun r ->
829 r.entities.recipients)
830 |> Jsont.Object.opt_mem "senders" authors_list_jsont ~enc:(fun r -> r.entities.senders)
831 |> Jsont.Object.opt_mem "contact" authors_list_jsont ~enc:(fun r -> r.entities.contact)
832 |> Jsont.Object.opt_mem "publisher" Cff_author.Entity.jsont ~enc:(fun r ->
833 r.entities.publisher)
834 |> Jsont.Object.opt_mem "institution" Cff_author.Entity.jsont ~enc:(fun r ->
835 r.entities.institution)
836 |> Jsont.Object.opt_mem "conference" Cff_author.Entity.jsont ~enc:(fun r ->
837 r.entities.conference)
838 |> Jsont.Object.opt_mem "database-provider" Cff_author.Entity.jsont ~enc:(fun r ->
839 r.entities.database_provider)
840 |> Jsont.Object.opt_mem "location" Cff_author.Entity.jsont ~enc:(fun r ->
841 r.entities.location)
842 (* Metadata fields *)
843 |> Jsont.Object.opt_mem "keywords" string_list_jsont ~enc:(fun r -> r.metadata.keywords)
844 |> Jsont.Object.opt_mem "languages" string_list_jsont ~enc:(fun r ->
845 r.metadata.languages)
846 |> Jsont.Object.opt_mem "license" Cff_license.jsont ~enc:(fun r -> r.metadata.license)
847 |> Jsont.Object.opt_mem "license-url" Jsont.string ~enc:(fun r ->
848 Option.bind r.metadata.license Cff_license.url)
849 |> Jsont.Object.opt_mem "copyright" Jsont.string ~enc:(fun r -> r.metadata.copyright)
850 |> Jsont.Object.opt_mem "scope" Jsont.string ~enc:(fun r -> r.metadata.scope)
851 |> Jsont.Object.opt_mem "notes" Jsont.string ~enc:(fun r -> r.metadata.notes)
852 (* Technical fields *)
853 |> Jsont.Object.opt_mem "commit" Jsont.string ~enc:(fun r -> r.technical.commit)
854 |> Jsont.Object.opt_mem "version" string_or_int_jsont ~enc:(fun r ->
855 r.technical.version)
856 |> Jsont.Object.opt_mem "filename" Jsont.string ~enc:(fun r -> r.technical.filename)
857 |> Jsont.Object.opt_mem "format" Jsont.string ~enc:(fun r -> r.technical.format)
858 |> Jsont.Object.opt_mem "medium" Jsont.string ~enc:(fun r -> r.technical.medium)
859 |> Jsont.Object.opt_mem "data-type" Jsont.string ~enc:(fun r -> r.technical.data_type)
860 |> Jsont.Object.opt_mem "database" Jsont.string ~enc:(fun r -> r.technical.database)
861 |> Jsont.Object.opt_mem "number" string_or_int_jsont ~enc:(fun r -> r.technical.number)
862 |> Jsont.Object.opt_mem "patent-states" string_list_jsont ~enc:(fun r ->
863 r.technical.patent_states)
864 |> Jsont.Object.opt_mem "thesis-type" Jsont.string ~enc:(fun r ->
865 r.technical.thesis_type)
866 |> Jsont.Object.opt_mem "term" Jsont.string ~enc:(fun r -> r.technical.term)
867 |> Jsont.Object.opt_mem "entry" Jsont.string ~enc:(fun r -> r.technical.entry)
868 |> Jsont.Object.opt_mem "department" Jsont.string ~enc:(fun r -> r.technical.department)
869 |> Jsont.Object.opt_mem "loc-start" string_or_int_jsont ~enc:(fun r ->
870 r.technical.loc_start)
871 |> Jsont.Object.opt_mem "loc-end" string_or_int_jsont ~enc:(fun r ->
872 r.technical.loc_end)
873 |> Jsont.Object.skip_unknown
874 |> Jsont.Object.finish
875;;