oxjsonld/from_rdf.rs
1use crate::context::has_keyword_form;
2#[cfg(feature = "async-tokio")]
3use json_event_parser::TokioAsyncWriterJsonSerializer;
4use json_event_parser::{JsonEvent, WriterJsonSerializer};
5use oxiri::{Iri, IriParseError};
6#[cfg(feature = "rdf-12")]
7use oxrdf::BaseDirection;
8use oxrdf::vocab::xsd;
9use oxrdf::{
10 GraphName, GraphNameRef, NamedNode, NamedOrBlankNode, NamedOrBlankNodeRef, QuadRef, TermRef,
11};
12use std::borrow::Cow;
13use std::collections::{BTreeMap, BTreeSet};
14use std::io;
15use std::io::Write;
16#[cfg(feature = "async-tokio")]
17use tokio::io::AsyncWrite;
18
19/// A [JSON-LD](https://www.w3.org/TR/json-ld/) serializer.
20///
21/// Returns [Streaming JSON-LD](https://www.w3.org/TR/json-ld11-streaming/).
22///
23/// It does not implement exactly the [RDF as JSON-LD Algorithm](https://www.w3.org/TR/json-ld-api/#serialize-rdf-as-json-ld-algorithm)
24/// to be a streaming serializer but aims at being close to it.
25/// Features like `@json` and `@list` generation are not implemented.
26///
27/// ```
28/// use oxrdf::{GraphNameRef, LiteralRef, NamedNodeRef, QuadRef};
29/// use oxrdf::vocab::rdf;
30/// use oxjsonld::JsonLdSerializer;
31///
32/// let mut serializer = JsonLdSerializer::new().with_prefix("schema", "http://schema.org/")?.for_writer(Vec::new());
33/// serializer.serialize_quad(QuadRef::new(
34/// NamedNodeRef::new("http://example.com#me")?,
35/// rdf::TYPE,
36/// NamedNodeRef::new("http://schema.org/Person")?,
37/// GraphNameRef::DefaultGraph
38/// ))?;
39/// serializer.serialize_quad(QuadRef::new(
40/// NamedNodeRef::new("http://example.com#me")?,
41/// NamedNodeRef::new("http://schema.org/name")?,
42/// LiteralRef::new_language_tagged_literal_unchecked("Foo Bar", "en"),
43/// GraphNameRef::DefaultGraph
44/// ))?;
45/// assert_eq!(
46/// b"{\"@context\":{\"schema\":\"http://schema.org/\"},\"@graph\":[{\"@id\":\"http://example.com#me\",\"http://www.w3.org/1999/02/22-rdf-syntax-ns#type\":[{\"@id\":\"http://schema.org/Person\"}],\"http://schema.org/name\":[{\"@language\":\"en\",\"@value\":\"Foo Bar\"}]}]}",
47/// serializer.finish()?.as_slice()
48/// );
49/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
50/// ```
51#[derive(Default, Clone)]
52#[must_use]
53pub struct JsonLdSerializer {
54 prefixes: BTreeMap<String, String>,
55 base_iri: Option<Iri<String>>,
56}
57
58impl JsonLdSerializer {
59 /// Builds a new [`JsonLdSerializer`].
60 #[inline]
61 pub fn new() -> Self {
62 Self {
63 prefixes: BTreeMap::new(),
64 base_iri: None,
65 }
66 }
67
68 #[inline]
69 pub fn with_prefix(
70 mut self,
71 prefix_name: impl Into<String>,
72 prefix_iri: impl Into<String>,
73 ) -> Result<Self, IriParseError> {
74 self.prefixes.insert(
75 prefix_name.into(),
76 Iri::parse(prefix_iri.into())?.into_inner(),
77 );
78 Ok(self)
79 }
80
81 /// Allows to set the base IRI for serialization.
82 ///
83 /// Corresponds to the [`base` option from the algorithm specification](https://www.w3.org/TR/json-ld-api/#dom-jsonldoptions-base).
84 /// ```
85 /// use oxrdf::{GraphNameRef, NamedNodeRef, QuadRef};
86 /// use oxjsonld::JsonLdSerializer;
87 ///
88 /// let mut serializer = JsonLdSerializer::new()
89 /// .with_base_iri("http://example.com")?
90 /// .with_prefix("ex", "http://example.com/ns#")?
91 /// .for_writer(Vec::new());
92 /// serializer.serialize_quad(QuadRef::new(
93 /// NamedNodeRef::new("http://example.com#me")?,
94 /// NamedNodeRef::new("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")?,
95 /// NamedNodeRef::new("http://example.com/ns#Person")?,
96 /// GraphNameRef::DefaultGraph
97 /// ))?;
98 /// serializer.serialize_quad(QuadRef::new(
99 /// NamedNodeRef::new("http://example.com#me")?,
100 /// NamedNodeRef::new("http://example.com/ns#parent")?,
101 /// NamedNodeRef::new("http://example.com#other")?,
102 /// GraphNameRef::DefaultGraph
103 /// ))?;
104 /// assert_eq!(
105 /// b"{\"@context\":{\"@base\":\"http://example.com\",\"ex\":\"http://example.com/ns#\"},\"@graph\":[{\"@id\":\"#me\",\"http://www.w3.org/1999/02/22-rdf-syntax-ns#type\":[{\"@id\":\"/ns#Person\"}],\"http://example.com/ns#parent\":[{\"@id\":\"#other\"}]}]}",
106 /// serializer.finish()?.as_slice()
107 /// );
108 /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
109 /// ```
110 #[inline]
111 pub fn with_base_iri(mut self, base_iri: impl Into<String>) -> Result<Self, IriParseError> {
112 self.base_iri = Some(Iri::parse(base_iri.into())?);
113 Ok(self)
114 }
115
116 /// Serializes a JSON-LD file to a [`Write`] implementation.
117 ///
118 /// This writer does unbuffered writes.
119 ///
120 /// ```
121 /// use oxrdf::{GraphNameRef, LiteralRef, NamedNodeRef, QuadRef};
122 /// use oxrdf::vocab::rdf;
123 /// use oxjsonld::JsonLdSerializer;
124 ///
125 /// let mut serializer = JsonLdSerializer::new().with_prefix("schema", "http://schema.org/")?.for_writer(Vec::new());
126 /// serializer.serialize_quad(QuadRef::new(
127 /// NamedNodeRef::new("http://example.com#me")?,
128 /// rdf::TYPE,
129 /// NamedNodeRef::new("http://schema.org/Person")?,
130 /// GraphNameRef::DefaultGraph
131 /// ))?;
132 /// serializer.serialize_quad(QuadRef::new(
133 /// NamedNodeRef::new("http://example.com#me")?,
134 /// NamedNodeRef::new("http://schema.org/name")?,
135 /// LiteralRef::new_language_tagged_literal_unchecked("Foo Bar", "en"),
136 /// GraphNameRef::DefaultGraph
137 /// ))?;
138 /// assert_eq!(
139 /// b"{\"@context\":{\"schema\":\"http://schema.org/\"},\"@graph\":[{\"@id\":\"http://example.com#me\",\"http://www.w3.org/1999/02/22-rdf-syntax-ns#type\":[{\"@id\":\"http://schema.org/Person\"}],\"http://schema.org/name\":[{\"@language\":\"en\",\"@value\":\"Foo Bar\"}]}]}",
140 /// serializer.finish()?.as_slice()
141 /// );
142 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
143 /// ```
144 pub fn for_writer<W: Write>(self, writer: W) -> WriterJsonLdSerializer<W> {
145 WriterJsonLdSerializer {
146 writer: WriterJsonSerializer::new(writer),
147 inner: self.inner_writer(),
148 }
149 }
150
151 /// Serializes a JSON-LD file to a [`AsyncWrite`] implementation.
152 ///
153 /// This writer does unbuffered writes.
154 ///
155 /// ```
156 /// # #[tokio::main(flavor = "current_thread")]
157 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
158 /// use oxrdf::{NamedNodeRef, QuadRef, LiteralRef, GraphNameRef};
159 /// use oxrdf::vocab::rdf;
160 /// use oxjsonld::JsonLdSerializer;
161 ///
162 /// let mut serializer = JsonLdSerializer::new().with_prefix("schema", "http://schema.org/")?.for_tokio_async_writer(Vec::new());
163 /// serializer.serialize_quad(QuadRef::new(
164 /// NamedNodeRef::new("http://example.com#me")?,
165 /// rdf::TYPE,
166 /// NamedNodeRef::new("http://schema.org/Person")?,
167 /// GraphNameRef::DefaultGraph
168 /// )).await?;
169 /// serializer.serialize_quad(QuadRef::new(
170 /// NamedNodeRef::new("http://example.com#me")?,
171 /// NamedNodeRef::new("http://schema.org/name")?,
172 /// LiteralRef::new_language_tagged_literal_unchecked("Foo Bar", "en"),
173 /// GraphNameRef::DefaultGraph
174 /// )).await?;
175 /// assert_eq!(
176 /// b"{\"@context\":{\"schema\":\"http://schema.org/\"},\"@graph\":[{\"@id\":\"http://example.com#me\",\"http://www.w3.org/1999/02/22-rdf-syntax-ns#type\":[{\"@id\":\"http://schema.org/Person\"}],\"http://schema.org/name\":[{\"@language\":\"en\",\"@value\":\"Foo Bar\"}]}]}",
177 /// serializer.finish().await?.as_slice()
178 /// );
179 /// # Ok(())
180 /// # }
181 /// ```
182 #[cfg(feature = "async-tokio")]
183 pub fn for_tokio_async_writer<W: AsyncWrite + Unpin>(
184 self,
185 writer: W,
186 ) -> TokioAsyncWriterJsonLdSerializer<W> {
187 TokioAsyncWriterJsonLdSerializer {
188 writer: TokioAsyncWriterJsonSerializer::new(writer),
189 inner: self.inner_writer(),
190 }
191 }
192
193 fn inner_writer(self) -> InnerJsonLdWriter {
194 InnerJsonLdWriter {
195 started: false,
196 current_graph_name: None,
197 current_subject: None,
198 current_predicate: None,
199 emitted_predicates: BTreeSet::new(),
200 prefixes: self.prefixes,
201 base_iri: self.base_iri,
202 }
203 }
204}
205
206/// Serializes a JSON-LD file to a [`Write`] implementation.
207///
208/// Can be built using [`JsonLdSerializer::for_writer`].
209///
210/// ```
211/// use oxrdf::{GraphNameRef, LiteralRef, NamedNodeRef, QuadRef};
212/// use oxrdf::vocab::rdf;
213/// use oxjsonld::JsonLdSerializer;
214///
215/// let mut serializer = JsonLdSerializer::new().with_prefix("schema", "http://schema.org/")?.for_writer(Vec::new());
216/// serializer.serialize_quad(QuadRef::new(
217/// NamedNodeRef::new("http://example.com#me")?,
218/// rdf::TYPE,
219/// NamedNodeRef::new("http://schema.org/Person")?,
220/// GraphNameRef::DefaultGraph
221/// ))?;
222/// serializer.serialize_quad(QuadRef::new(
223/// NamedNodeRef::new("http://example.com#me")?,
224/// NamedNodeRef::new("http://schema.org/name")?,
225/// LiteralRef::new_language_tagged_literal_unchecked("Foo Bar", "en"),
226/// GraphNameRef::DefaultGraph
227/// ))?;
228/// assert_eq!(
229/// b"{\"@context\":{\"schema\":\"http://schema.org/\"},\"@graph\":[{\"@id\":\"http://example.com#me\",\"http://www.w3.org/1999/02/22-rdf-syntax-ns#type\":[{\"@id\":\"http://schema.org/Person\"}],\"http://schema.org/name\":[{\"@language\":\"en\",\"@value\":\"Foo Bar\"}]}]}",
230/// serializer.finish()?.as_slice()
231/// );
232/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
233/// ```
234#[must_use]
235pub struct WriterJsonLdSerializer<W: Write> {
236 writer: WriterJsonSerializer<W>,
237 inner: InnerJsonLdWriter,
238}
239
240impl<W: Write> WriterJsonLdSerializer<W> {
241 /// Serializes an extra quad.
242 pub fn serialize_quad<'a>(&mut self, t: impl Into<QuadRef<'a>>) -> io::Result<()> {
243 let mut buffer = Vec::new();
244 self.inner.serialize_quad(t, &mut buffer)?;
245 self.flush_buffer(&mut buffer)
246 }
247
248 /// Ends the write process and returns the underlying [`Write`].
249 pub fn finish(mut self) -> io::Result<W> {
250 let mut buffer = Vec::new();
251 self.inner.finish(&mut buffer);
252 self.flush_buffer(&mut buffer)?;
253 self.writer.finish()
254 }
255
256 fn flush_buffer(&mut self, buffer: &mut Vec<JsonEvent<'_>>) -> io::Result<()> {
257 for event in buffer.drain(0..) {
258 self.writer.serialize_event(event)?;
259 }
260 Ok(())
261 }
262}
263
264/// Serializes a JSON-LD file to a [`AsyncWrite`] implementation.
265///
266/// Can be built using [`JsonLdSerializer::for_tokio_async_writer`].
267///
268/// ```
269/// # #[tokio::main(flavor = "current_thread")]
270/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
271/// use oxrdf::{NamedNodeRef, QuadRef, LiteralRef, GraphNameRef};
272/// use oxrdf::vocab::rdf;
273/// use oxjsonld::JsonLdSerializer;
274///
275/// let mut serializer = JsonLdSerializer::new().with_prefix("schema", "http://schema.org/")?.for_tokio_async_writer(Vec::new());
276/// serializer.serialize_quad(QuadRef::new(
277/// NamedNodeRef::new("http://example.com#me")?,
278/// rdf::TYPE,
279/// NamedNodeRef::new("http://schema.org/Person")?,
280/// GraphNameRef::DefaultGraph
281/// )).await?;
282/// serializer.serialize_quad(QuadRef::new(
283/// NamedNodeRef::new("http://example.com#me")?,
284/// NamedNodeRef::new("http://schema.org/name")?,
285/// LiteralRef::new_language_tagged_literal_unchecked("Foo Bar", "en"),
286/// GraphNameRef::DefaultGraph
287/// )).await?;
288/// assert_eq!(
289/// b"{\"@context\":{\"schema\":\"http://schema.org/\"},\"@graph\":[{\"@id\":\"http://example.com#me\",\"http://www.w3.org/1999/02/22-rdf-syntax-ns#type\":[{\"@id\":\"http://schema.org/Person\"}],\"http://schema.org/name\":[{\"@language\":\"en\",\"@value\":\"Foo Bar\"}]}]}",
290/// serializer.finish().await?.as_slice()
291/// );
292/// # Ok(())
293/// # }
294/// ```
295#[cfg(feature = "async-tokio")]
296#[must_use]
297pub struct TokioAsyncWriterJsonLdSerializer<W: AsyncWrite + Unpin> {
298 writer: TokioAsyncWriterJsonSerializer<W>,
299 inner: InnerJsonLdWriter,
300}
301
302#[cfg(feature = "async-tokio")]
303impl<W: AsyncWrite + Unpin> TokioAsyncWriterJsonLdSerializer<W> {
304 /// Serializes an extra quad.
305 pub async fn serialize_quad<'a>(&mut self, t: impl Into<QuadRef<'a>>) -> io::Result<()> {
306 let mut buffer = Vec::new();
307 self.inner.serialize_quad(t, &mut buffer)?;
308 self.flush_buffer(&mut buffer).await
309 }
310
311 /// Ends the write process and returns the underlying [`Write`].
312 pub async fn finish(mut self) -> io::Result<W> {
313 let mut buffer = Vec::new();
314 self.inner.finish(&mut buffer);
315 self.flush_buffer(&mut buffer).await?;
316 self.writer.finish()
317 }
318
319 async fn flush_buffer(&mut self, buffer: &mut Vec<JsonEvent<'_>>) -> io::Result<()> {
320 for event in buffer.drain(0..) {
321 self.writer.serialize_event(event).await?;
322 }
323 Ok(())
324 }
325}
326
327pub struct InnerJsonLdWriter {
328 started: bool,
329 current_graph_name: Option<GraphName>,
330 current_subject: Option<NamedOrBlankNode>,
331 current_predicate: Option<NamedNode>,
332 emitted_predicates: BTreeSet<String>,
333 prefixes: BTreeMap<String, String>,
334 base_iri: Option<Iri<String>>,
335}
336
337impl InnerJsonLdWriter {
338 fn serialize_quad<'a>(
339 &mut self,
340 quad: impl Into<QuadRef<'a>>,
341 output: &mut Vec<JsonEvent<'a>>,
342 ) -> io::Result<()> {
343 if !self.started {
344 self.serialize_start(output);
345 self.started = true;
346 }
347
348 let quad = quad.into();
349 if self
350 .current_graph_name
351 .as_ref()
352 .is_some_and(|graph_name| graph_name.as_ref() != quad.graph_name)
353 {
354 output.push(JsonEvent::EndArray);
355 output.push(JsonEvent::EndObject);
356 if self
357 .current_graph_name
358 .as_ref()
359 .is_some_and(|g| !g.is_default_graph())
360 {
361 output.push(JsonEvent::EndArray);
362 output.push(JsonEvent::EndObject);
363 }
364 self.current_graph_name = None;
365 self.current_subject = None;
366 self.current_predicate = None;
367 self.emitted_predicates.clear();
368 } else if self
369 .current_subject
370 .as_ref()
371 .is_some_and(|subject| subject.as_ref() != quad.subject)
372 || self
373 .current_predicate
374 .as_ref()
375 .is_some_and(|predicate| predicate.as_ref() != quad.predicate)
376 && self.emitted_predicates.contains(quad.predicate.as_str())
377 {
378 output.push(JsonEvent::EndArray);
379 output.push(JsonEvent::EndObject);
380 self.current_subject = None;
381 self.emitted_predicates.clear();
382 self.current_predicate = None;
383 } else if self
384 .current_predicate
385 .as_ref()
386 .is_some_and(|predicate| predicate.as_ref() != quad.predicate)
387 {
388 output.push(JsonEvent::EndArray);
389 if let Some(current_predicate) = self.current_predicate.take() {
390 self.emitted_predicates
391 .insert(current_predicate.into_string());
392 }
393 }
394
395 if self.current_graph_name.is_none() {
396 if !quad.graph_name.is_default_graph() {
397 // We open a new graph name
398 output.push(JsonEvent::StartObject);
399 output.push(JsonEvent::ObjectKey("@id".into()));
400 output.push(JsonEvent::String(self.id_value(match quad.graph_name {
401 GraphNameRef::NamedNode(iri) => iri.into(),
402 GraphNameRef::BlankNode(bnode) => bnode.into(),
403 GraphNameRef::DefaultGraph => unreachable!(),
404 })));
405 output.push(JsonEvent::ObjectKey("@graph".into()));
406 output.push(JsonEvent::StartArray);
407 }
408 self.current_graph_name = Some(quad.graph_name.into_owned());
409 }
410
411 // We open a new subject block if useful (ie. new subject or already used predicate)
412 if self.current_subject.is_none() {
413 output.push(JsonEvent::StartObject);
414 output.push(JsonEvent::ObjectKey("@id".into()));
415 #[allow(
416 unreachable_patterns,
417 clippy::match_wildcard_for_single_variants,
418 clippy::allow_attributes
419 )]
420 output.push(JsonEvent::String(self.id_value(match quad.subject {
421 NamedOrBlankNodeRef::NamedNode(iri) => iri.into(),
422 NamedOrBlankNodeRef::BlankNode(bnode) => bnode.into(),
423 _ => {
424 return Err(io::Error::new(
425 io::ErrorKind::InvalidInput,
426 "JSON-LD does not support RDF 1.2 yet",
427 ));
428 }
429 })));
430 self.current_subject = Some(quad.subject.into_owned());
431 }
432
433 // We open a predicate key
434 if self.current_predicate.is_none() {
435 output.push(JsonEvent::ObjectKey(
436 // TODO: use @type
437 quad.predicate.as_str().into(), // TODO: prefixes including @vocab
438 ));
439 output.push(JsonEvent::StartArray);
440 self.current_predicate = Some(quad.predicate.into_owned());
441 }
442
443 self.serialize_term(quad.object, output)
444 }
445
446 fn serialize_start(&self, output: &mut Vec<JsonEvent<'_>>) {
447 if self.base_iri.is_some() || !self.prefixes.is_empty() {
448 output.push(JsonEvent::StartObject);
449 output.push(JsonEvent::ObjectKey("@context".into()));
450 output.push(JsonEvent::StartObject);
451 if let Some(base_iri) = &self.base_iri {
452 output.push(JsonEvent::ObjectKey("@base".into()));
453 output.push(JsonEvent::String(base_iri.to_string().into()));
454 }
455 for (prefix_name, prefix_iri) in &self.prefixes {
456 output.push(JsonEvent::ObjectKey(if prefix_name.is_empty() {
457 "@vocab".into()
458 } else {
459 prefix_name.clone().into()
460 }));
461 output.push(JsonEvent::String(prefix_iri.clone().into()));
462 }
463 output.push(JsonEvent::EndObject);
464 output.push(JsonEvent::ObjectKey("@graph".into()));
465 }
466 output.push(JsonEvent::StartArray);
467 }
468
469 fn serialize_term<'a>(
470 &self,
471 term: TermRef<'a>,
472 output: &mut Vec<JsonEvent<'a>>,
473 ) -> io::Result<()> {
474 output.push(JsonEvent::StartObject);
475 #[allow(
476 unreachable_patterns,
477 clippy::match_wildcard_for_single_variants,
478 clippy::allow_attributes
479 )]
480 match term {
481 TermRef::NamedNode(iri) => {
482 output.push(JsonEvent::ObjectKey("@id".into()));
483 output.push(JsonEvent::String(self.id_value(iri.into())));
484 }
485 TermRef::BlankNode(bnode) => {
486 output.push(JsonEvent::ObjectKey("@id".into()));
487 output.push(JsonEvent::String(self.id_value(bnode.into())));
488 }
489 TermRef::Literal(literal) => {
490 if let Some(language) = literal.language() {
491 output.push(JsonEvent::ObjectKey("@language".into()));
492 output.push(JsonEvent::String(language.into()));
493 #[cfg(feature = "rdf-12")]
494 if let Some(direction) = literal.direction() {
495 output.push(JsonEvent::ObjectKey("@direction".into()));
496 output.push(JsonEvent::String(
497 match direction {
498 BaseDirection::Ltr => "ltr",
499 BaseDirection::Rtl => "rtl",
500 }
501 .into(),
502 ));
503 }
504 } else if literal.datatype() != xsd::STRING {
505 output.push(JsonEvent::ObjectKey("@type".into()));
506 output.push(JsonEvent::String(Self::type_value(
507 literal.datatype().into(),
508 )));
509 }
510 output.push(JsonEvent::ObjectKey("@value".into()));
511 output.push(JsonEvent::String(literal.value().into()));
512 }
513 _ => {
514 return Err(io::Error::new(
515 io::ErrorKind::InvalidInput,
516 "JSON-LD does not support RDF 1.2 yet",
517 ));
518 }
519 }
520 output.push(JsonEvent::EndObject);
521 Ok(())
522 }
523
524 fn id_value<'a>(&self, id: NamedOrBlankNodeRef<'a>) -> Cow<'a, str> {
525 match id {
526 NamedOrBlankNodeRef::NamedNode(iri) => {
527 if let Some(base_iri) = &self.base_iri {
528 if let Ok(relative) = base_iri.relativize(&Iri::parse_unchecked(iri.as_str())) {
529 let relative = relative.into_inner();
530 // We check the relative IRI is not considered as absolute or a keyword by IRI expansion
531 if !relative.split_once(':').is_some_and(|(prefix, suffix)| {
532 prefix == "_" || suffix.starts_with("//")
533 }) && !has_keyword_form(&relative)
534 {
535 return relative.into();
536 }
537 }
538 }
539 iri.as_str().into()
540 }
541 NamedOrBlankNodeRef::BlankNode(bnode) => bnode.to_string().into(),
542 }
543 }
544
545 fn type_value(id: NamedOrBlankNodeRef<'_>) -> Cow<'_, str> {
546 match id {
547 NamedOrBlankNodeRef::NamedNode(iri) => iri.as_str().into(),
548 NamedOrBlankNodeRef::BlankNode(bnode) => bnode.to_string().into(),
549 }
550 }
551
552 fn finish(&mut self, output: &mut Vec<JsonEvent<'static>>) {
553 if !self.started {
554 self.serialize_start(output);
555 }
556 if self.current_predicate.is_some() {
557 output.push(JsonEvent::EndArray)
558 }
559 if self.current_subject.is_some() {
560 output.push(JsonEvent::EndObject)
561 }
562 if self
563 .current_graph_name
564 .as_ref()
565 .is_some_and(|g| !g.is_default_graph())
566 {
567 output.push(JsonEvent::EndArray);
568 output.push(JsonEvent::EndObject)
569 }
570 output.push(JsonEvent::EndArray);
571 if self.base_iri.is_some() || !self.prefixes.is_empty() {
572 output.push(JsonEvent::EndObject);
573 }
574 }
575}