@@ -95,6 +95,28 @@ fn into_iri<'a>(s: &'a str, mut prefix: &str) -> Cow<'a, str> {
9595fn value_to_json ( value : & DataValue ) -> String {
9696 match value {
9797 DataValue :: String ( s) => format ! ( "\" {}\" " , s. replace( "\n " , "\\ n" ) . replace( "\" " , "\\ \" " ) ) ,
98+ DataValue :: List ( l) => {
99+ let mut json_out = "[" . to_string ( ) ;
100+ for ( i, value) in l. iter ( ) . enumerate ( ) {
101+ if i > 0 {
102+ json_out. push ( ',' ) ;
103+ }
104+ json_out. push_str ( & value_to_json ( value) ) ;
105+ }
106+ json_out. push ( ']' ) ;
107+ json_out
108+ }
109+ DataValue :: Map ( m) => {
110+ let mut json_out = "{" . to_string ( ) ;
111+ for ( i, ( key, value) ) in m. iter ( ) . enumerate ( ) {
112+ if i > 0 {
113+ json_out. push ( ',' ) ;
114+ }
115+ json_out. push_str ( & format ! ( "\" {}\" : {}" , key, value_to_json( value) ) ) ;
116+ }
117+ json_out. push ( '}' ) ;
118+ json_out
119+ }
98120 x => x. to_string ( ) ,
99121 }
100122}
@@ -113,7 +135,9 @@ pub struct WebAnnoConfig {
113135 /// IRI prefix for Text Resources. Will be prepended if the resource public ID is not an IRI yet.
114136 pub default_resource_iri : String ,
115137
116- /// Extra JSON-LD context to export, these must be URLs to JSONLD files.
138+ /// Extra JSON-LD context to export, these must be URLs to JSONLD files. Keys in these sets that are not full IRIs will
139+ /// then be copied as-is to the output (as alias rather than joined with the set ID to form a
140+ /// full IRI ), leaving interpretation it up to the JSON-LD context.
117141 pub extra_context : Vec < String > ,
118142
119143 /// Automatically add a 'generated' triple for each annotation, with the timestamp of serialisation
@@ -163,6 +187,22 @@ impl WebAnnoConfig {
163187 Cow :: Borrowed ( s)
164188 }
165189
190+ /// Automatically add any datasets with IDs ending in `.jsonld` or `.jsonld` to the extra context list.
191+ pub fn auto_extra_context ( mut self , store : & AnnotationStore ) -> Self {
192+ for dataset in store. datasets ( ) {
193+ if let Some ( dataset_id) = dataset. id ( ) {
194+ if ( dataset_id. ends_with ( ".jsonld" ) || dataset_id. ends_with ( ".json" ) )
195+ && is_iri ( dataset_id)
196+ {
197+ if !self . extra_context . iter ( ) . any ( |x| x == dataset_id) {
198+ self . extra_context . push ( dataset_id. to_string ( ) ) ;
199+ }
200+ }
201+ }
202+ }
203+ self
204+ }
205+
166206 /// Generates a JSON-LD string to use for @context
167207 pub fn serialize_context ( & self ) -> String {
168208 let mut out = String :: new ( ) ;
@@ -208,7 +248,7 @@ impl WebAnnoConfig {
208248}
209249
210250impl < ' store > ResultItem < ' store , Annotation > {
211- /// Outputs the annotation as a W3C Web Annotation, the JSON output will be on a single line without pretty formatting.
251+ /// Outputs the annotation as a W3C Web Annotation, the output will be JSON-LD on a single line without pretty formatting.
212252 pub fn to_webannotation ( & self , config : & WebAnnoConfig ) -> String {
213253 if let Selector :: AnnotationDataSelector ( ..) | Selector :: DataKeySelector ( ..) =
214254 self . as_ref ( ) . target ( )
@@ -236,6 +276,7 @@ impl<'store> ResultItem<'store, Annotation> {
236276 let mut suppress_body_id = false ;
237277 let mut suppress_auto_generated = false ;
238278 let mut suppress_auto_generator = false ;
279+ let mut target_extra_out = String :: new ( ) ;
239280
240281 let mut outputted_to_main = false ;
241282 //gather annotation properties (outside of body)
@@ -267,6 +308,12 @@ impl<'store> ResultItem<'store, Annotation> {
267308 outputted_to_main = true ;
268309 ann_out += & output_predicate_datavalue ( key_id, data. value ( ) , config) ;
269310 }
311+ "target" => {
312+ if !target_extra_out. is_empty ( ) {
313+ target_extra_out. push ( ',' ) ;
314+ }
315+ target_extra_out += & value_to_json ( data. value ( ) ) ;
316+ }
270317 key_id => {
271318 //other predicates -> go into body
272319 if key_id == "type" {
@@ -280,9 +327,15 @@ impl<'store> ResultItem<'store, Annotation> {
280327 body_out += & output_predicate_datavalue ( key_id, data. value ( ) , config) ;
281328 }
282329 } ,
283- Some ( _set_id ) => {
330+ Some ( set_id ) => {
284331 //different set, go into body
285- let predicate = key. iri ( & config. default_set_iri ) . expect ( "set must have ID" ) ;
332+ let predicate = if config. extra_context . iter ( ) . any ( |s| s == set_id) {
333+ //the set doubles as JSON-LD context: return the key as is (either a full IRI already or an alias)
334+ Cow :: Borrowed ( key. id ( ) . expect ( "key must have ID" ) )
335+ } else {
336+ //turn it into an IRI per standard identifier rules
337+ key. iri ( & config. default_set_iri ) . expect ( "set must have ID" )
338+ } ;
286339 if !body_out. is_empty ( ) {
287340 body_out. push ( ',' ) ;
288341 }
@@ -338,15 +391,29 @@ impl<'store> ResultItem<'store, Annotation> {
338391 & mut need_second_pass,
339392 true , //second pass
340393 ) ;
394+ if !target_extra_out. is_empty ( ) {
395+ //with extra target from second pass and extra target(s) from annotation data
396+ ann_out += & format ! (
397+ " \" target\" : [ {}, {}, {} ]" ,
398+ output_selector_out, & second_pass_out, & target_extra_out
399+ ) ;
400+ } else {
401+ //with extra target from second pass
402+ ann_out += & format ! (
403+ " \" target\" : [ {}, {} ]" ,
404+ output_selector_out, & second_pass_out
405+ ) ;
406+ }
407+ } else if !target_extra_out. is_empty ( ) {
408+ //with extra target(s) from annotation data
341409 ann_out += & format ! (
342410 " \" target\" : [ {}, {} ]" ,
343- output_selector_out, & second_pass_out
411+ & output_selector_out, & target_extra_out
344412 ) ;
345413 } else {
346- //normal situation
414+ //normal situation, no extra targets
347415 ann_out += & format ! ( " \" target\" : {}" , & output_selector_out) ;
348416 }
349-
350417 ann_out += "}" ;
351418 ann_out
352419 }
0 commit comments