stam/
json.rs

1/*
2    STAM Library (Stand-off Text Annotation Model)
3        by Maarten van Gompel <proycon@anaproy.nl>
4        Digital Infrastucture, KNAW Humanities Cluster
5
6        Licensed under the GNU General Public License v3
7
8        https://github.com/annotation/stam-rust
9*/
10
11//! This module contains the [`ToJson`] and [`FromJson`] trait thare are used
12//! in serialisation to/from STAM JSON. Most of the actual deserialisation/serialisation methods
13//! are implemented alongside the low-level data structures themselves, not here.
14
15use crate::config::{Config, SerializeMode};
16use crate::error::StamError;
17use crate::file::*;
18use crate::types::*;
19
20pub trait ToJson
21where
22    Self: TypeInfo + serde::Serialize,
23{
24    /// Writes a serialisation (choose a dataformat) to any writer
25    /// Lower-level function
26    fn to_json_writer<W>(&self, writer: W, compact: bool) -> Result<(), StamError>
27    where
28        W: std::io::Write,
29    {
30        match compact {
31            false => serde_json::to_writer_pretty(writer, &self).map_err(|e| {
32                StamError::SerializationError(format!(
33                    "Writing {} to file: {}",
34                    Self::typeinfo(),
35                    e
36                ))
37            }),
38            true => serde_json::to_writer(writer, &self).map_err(|e| {
39                StamError::SerializationError(format!(
40                    "Writing {} to file: {}",
41                    Self::typeinfo(),
42                    e
43                ))
44            }),
45        }
46    }
47
48    /// Writes this structure to a file
49    /// The actual dataformat can be set via `config`, the default is STAM JSON.
50    fn to_json_file(&self, filename: &str, config: &Config) -> Result<(), StamError> {
51        debug(config, || {
52            format!(
53                "{}.to_json_file: filename={:?} workdir={:?}",
54                Self::typeinfo(),
55                filename,
56                config.workdir()
57            )
58        });
59        if let Type::TextResource | Type::AnnotationDataSet = Self::typeinfo() {
60            //introspection to detect whether type can do @include
61            config.set_serialize_mode(SerializeMode::NoInclude); //set standoff mode, what we're about the write is the standoff file
62        }
63        let compact = match config.dataformat {
64            DataFormat::Json { compact } => compact,
65            _ => {
66                if let Type::AnnotationStore = Self::typeinfo() {
67                    return Err(StamError::SerializationError(format!(
68                        "Unable to serialize to JSON for {} (filename {}) when config dataformat is set to {}",
69                        Self::typeinfo(),
70                        filename,
71                        config.dataformat
72                    )));
73                } else {
74                    false
75                }
76            }
77        };
78        let writer = open_file_writer(filename, &config)?;
79        let result = self.to_json_writer(writer, compact);
80        if let Type::TextResource | Type::AnnotationDataSet = Self::typeinfo() {
81            //introspection to detect whether type can do @include
82            config.set_serialize_mode(SerializeMode::AllowInclude); //set standoff mode, what we're about the write is the standoff file
83        }
84        result
85    }
86
87    /// Serializes this structure to one string.
88    /// The actual dataformat can be set via `config`, the default is STAM JSON.
89    /// If `config` not not specified, an attempt to fetch the AnnotationStore's initial config is made
90    fn to_json_string(&self, config: &Config) -> Result<String, StamError> {
91        if let Type::TextResource | Type::AnnotationDataSet = Self::typeinfo() {
92            //introspection to detect whether type can do @include
93            config.set_serialize_mode(SerializeMode::NoInclude); //set standoff mode, what we're about the write is the standoff file
94        }
95        let result = match config.dataformat {
96            DataFormat::Json { compact: false } => {
97                serde_json::to_string_pretty(&self).map_err(|e| {
98                    StamError::SerializationError(format!(
99                        "Writing {} to string: {}",
100                        Self::typeinfo(),
101                        e
102                    ))
103                })
104            }
105            DataFormat::Json { compact: true } => serde_json::to_string(&self).map_err(|e| {
106                StamError::SerializationError(format!(
107                    "Writing {} to string: {}",
108                    Self::typeinfo(),
109                    e
110                ))
111            }),
112            _ => Err(StamError::SerializationError(format!(
113                "Unable to serialize to JSON for {} when config dataformat is set to {}",
114                Self::typeinfo(),
115                config.dataformat
116            ))),
117        };
118        if let Type::TextResource | Type::AnnotationDataSet = Self::typeinfo() {
119            //introspection to detect whether type can do @include
120            config.set_serialize_mode(SerializeMode::AllowInclude); //set standoff mode, what we're about the write is the standoff file
121        }
122        result
123    }
124
125    /// Serializes this structure to a JSON value
126    /// If `config` not not specified, an attempt to fetch the AnnotationStore's initial config is made
127    fn to_json_value(&self) -> Result<serde_json::Value, StamError> {
128        serde_json::to_value(&self).map_err(|e| {
129            StamError::SerializationError(format!(
130                "Writing {} to JSON value: {}",
131                Self::typeinfo(),
132                e
133            ))
134        })
135    }
136}
137
138pub trait FromJson
139where
140    Self: TypeInfo + Sized,
141{
142    fn from_json_file(filename: &str, config: Config) -> Result<Self, StamError>;
143
144    fn from_json_str(string: &str, config: Config) -> Result<Self, StamError>;
145
146    fn merge_json_file(&mut self, _filename: &str) -> Result<(), StamError> {
147        unimplemented!("merge_json_file not implemented")
148    }
149
150    fn merge_json_str(&mut self, _string: &str) -> Result<(), StamError> {
151        unimplemented!("merge_json_str not implemented")
152    }
153}