Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
JsonLdDocument.cs
1using System;
2using System.Collections.Generic;
3using System.Net.Http;
4using System.Threading.Tasks;
8
10{
17 {
18 private static readonly Cache<string, KeyValuePair<DateTimeOffset, Dictionary<string, object>>> contextObjects =
19 new Cache<string, KeyValuePair<DateTimeOffset, Dictionary<string, object>>>(int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromHours(1));
20
21 private readonly object parsedJson;
22 private readonly BlankNodeIdMode blankNodeIdMode;
23 private readonly string text;
24 private readonly string blankNodeIdPrefix;
25 private DateTimeOffset? date = null;
26 private int blankNodeIndex = 0;
27
35 private JsonLdDocument(object Doc, string Text, string BlankNodeIdPrefix, BlankNodeIdMode BlankNodeIdMode)
36 {
37 this.parsedJson = Doc;
38 this.text = Text;
39 this.blankNodeIdMode = BlankNodeIdMode;
40 this.blankNodeIdPrefix = BlankNodeIdPrefix;
41 }
42
47 public static Task<JsonLdDocument> CreateAsync(string Text)
48 {
49 return CreateAsync(JSON.Parse(Text), Text, null);
50 }
51
57 public static Task<JsonLdDocument> CreateAsync(string Text, Uri BaseUri)
58 {
59 return CreateAsync(JSON.Parse(Text), Text, BaseUri, "n");
60 }
61
68 public static Task<JsonLdDocument> CreateAsync(string Text, Uri BaseUri, string BlankNodeIdPrefix)
69 {
70 return CreateAsync(JSON.Parse(Text), Text, BaseUri, BlankNodeIdPrefix, BlankNodeIdMode.Sequential);
71 }
72
80 public static Task<JsonLdDocument> CreateAsync(string Text, Uri BaseUri, string BlankNodeIdPrefix, BlankNodeIdMode BlankNodeIdMode)
81 {
82 return CreateAsync(JSON.Parse(Text), Text, BaseUri, BlankNodeIdPrefix, BlankNodeIdMode);
83 }
84
90 public static Task<JsonLdDocument> CreateAsync(object Doc, string Text)
91 {
92 return CreateAsync(Doc, Text, null);
93 }
94
101 public static Task<JsonLdDocument> CreateAsync(object Doc, string Text, Uri BaseUri)
102 {
103 return CreateAsync(Doc, Text, BaseUri, "n");
104 }
105
113 public static Task<JsonLdDocument> CreateAsync(object Doc, string Text, Uri BaseUri, string BlankNodeIdPrefix)
114 {
115 return CreateAsync(Doc, Text, BaseUri, BlankNodeIdPrefix, BlankNodeIdMode.Sequential);
116 }
117
126 public static async Task<JsonLdDocument> CreateAsync(object Doc, string Text, Uri BaseUri, string BlankNodeIdPrefix, BlankNodeIdMode BlankNodeIdMode)
127 {
128 JsonLdDocument Result = new JsonLdDocument(Doc, Text, BlankNodeIdPrefix, BlankNodeIdMode);
129 await Result.Parse(Doc, null, BaseUri, Result.CreateBlankNode());
130 return Result;
131 }
132
133 private BlankNode CreateBlankNode()
134 {
135 if (this.blankNodeIdMode == BlankNodeIdMode.Guid)
136 return new BlankNode(this.blankNodeIdPrefix + Guid.NewGuid().ToString());
137 else
138 return new BlankNode(this.blankNodeIdPrefix + (++this.blankNodeIndex).ToString());
139 }
140
141 private UriNode CreateUriNode(string Reference, Uri BaseUri, JsonLdContext Context)
142 {
143 return new UriNode(this.CreateUri(Reference, BaseUri, Context), Reference);
144 }
145
146 private UriNode CreateUriNode(string Reference, Uri BaseUri, string ShortName, JsonLdContext Context)
147 {
148 return new UriNode(this.CreateUri(Reference, BaseUri, Context), ShortName);
149 }
150
151 private Uri CreateUri(string Reference, Uri BaseUri, JsonLdContext Context)
152 {
153 ExpandPrefixes(ref Reference, Context);
154 BaseUri = Context?.Base ?? BaseUri;
155
156 if (BaseUri is null)
157 {
158 if (Uri.TryCreate(Reference, UriKind.Absolute, out Uri URI))
159 return URI;
160 else
161 throw this.ParsingException("Invalid URI: " + Reference);
162 }
163 else
164 {
165 if (string.IsNullOrEmpty(Reference))
166 return BaseUri;
167 else if (Uri.TryCreate(BaseUri, Reference, out Uri URI))
168 return URI;
169 else
170 throw this.ParsingException("Invalid URI: " + Reference + " (base: " + BaseUri.ToString() + ")");
171 }
172 }
173
174 private Exception ParsingException(string Message)
175 {
176 return new Exception(Message);
177 }
178
179 private async Task<ISemanticElement> Parse(object Doc, JsonLdContext Context, Uri BaseUri, ISemanticElement Subject)
180 {
181 ISemanticElement ReturnValue = null;
182
183 if (Doc is Dictionary<string, object> DocObj)
184 {
185 string Name;
186 object Value;
187 ISemanticElement ParsedValue;
188 Uri ParsedUri;
189 string s;
190
191 foreach (KeyValuePair<string, object> P in DocObj)
192 {
193 Name = P.Key;
194 Value = P.Value;
195
196 if (!TryGetContextName(Name, Context, out object ContextValue))
197 ContextValue = null;
198 else if (ContextValue is string Alias)
199 Name = Alias;
200
201 if (Name.StartsWith("@"))
202 {
203 switch (Name)
204 {
205 case "@context":
206 Context = await this.GetContext(Value, BaseUri, Context);
207 break;
208
209 case "@id":
210 s = Value as string
211 ?? throw this.ParsingException("Unsupported @id value: " + Value.ToString());
212
213 Subject = this.CreateUriNode(s, BaseUri, Context);
214 break;
215
216 case "@type":
217 Context = await this.AddType(Subject, Value, BaseUri, Context);
218 break;
219
220 case "@graph":
221 await this.Parse(P.Value, Context, BaseUri, Subject);
222 break;
223
224 case "@version":
225 break;
226
227 case "@base":
228 s = Value as string
229 ?? throw this.ParsingException("Unsupported @base value: " + Value.ToString());
230
231 BaseUri = this.CreateUri(s, BaseUri, Context);
232 break;
233
234 case "@value":
235 ReturnValue = await this.ParseValue(BaseUri, Value, Context);
236 break;
237
238 default:
239 throw this.ParsingException("Unrecognized keyword: " + Name);
240 }
241 }
242 else
243 {
244 ParsedUri = null;
245 ParsedValue = null;
246
247 if (ContextValue is null)
248 {
249 if (Uri.TryCreate(Name, UriKind.Absolute, out Uri UriValue)) // Not relative to base URI.
250 {
251 ContextValue = Name;
252 ParsedUri = UriValue;
253 }
254 else
255 continue;
256 }
257
258 s = ContextValue as string;
259 if (!(s is null))
260 {
261 Name = s;
262
263 if (!TryGetContextName(Name, Context, out ContextValue))
264 ContextValue = Name;
265 }
266
267 if (ContextValue is Dictionary<string, object> ContextValueObj)
268 {
269 foreach (KeyValuePair<string, object> P2 in ContextValueObj)
270 {
271 switch (P2.Key)
272 {
273 case "@id":
274 s = P2.Value as string
275 ?? throw this.ParsingException("Unsupported @id value: " + P2.Value.ToString());
276
277 Name = s;
278 ExpandPrefixes(ref Name, Context);
279 break;
280
281 case "@type":
282 s = P2.Value as string
283 ?? throw this.ParsingException("Unsupported @type value: " + P2.Value.ToString());
284
285 switch (s)
286 {
287 case "@id":
288 Value = this.CreateUri(Value?.ToString(), BaseUri, Context);
289 break;
290
291 default:
292 Uri TypeUri = this.CreateUri(s, BaseUri, Context);
293 ParsedValue = SemanticElements.Parse(Value?.ToString(), TypeUri.AbsoluteUri, string.Empty);
294 break;
295 }
296 break;
297
298 case "@vocab":
299 s = P2.Value as string
300 ?? throw this.ParsingException("Unsupported @vocab value: " + P2.Value.ToString());
301
302 ParsedUri = this.CreateUri(s, BaseUri, Context);
303 break;
304
305 case "@base":
306 s = P2.Value as string
307 ?? throw this.ParsingException("Unsupported @base value: " + P2.Value.ToString());
308
309 BaseUri = this.CreateUri(s, BaseUri, Context);
310 break;
311
312 case "@prefix":
313 break;
314
315 case "@context":
316 JsonLdContext ScopedContext = await this.GetContext(P2.Value, BaseUri, Context);
317 if (!(Context is null))
318 ScopedContext.Append(Context);
319
320 Context = ScopedContext;
321 break;
322
323 default:
324 throw this.ParsingException("Unhandled context keyword: " + P2.Key);
325 }
326 }
327
328 if (ParsedUri is null && !(Context.Vocabulary is null))
329 ParsedUri = this.CreateUri(Name, Context.Vocabulary, Context);
330 }
331
332 if (!(ParsedUri is null) || Uri.TryCreate(BaseUri, Name, out ParsedUri))
333 {
334 UriNode Predicate = new UriNode(ParsedUri, Name);
335
336 if (ParsedValue is null)
337 {
338 if (Value is Array ValueArray)
339 {
340 foreach (object ValueItem in ValueArray)
341 {
342 ParsedValue = await this.ParseValue(BaseUri, ValueItem, Context);
343 this.Add(new SemanticTriple(Subject, Predicate, ParsedValue));
344 }
345 }
346 else
347 {
348 ParsedValue = await this.ParseValue(BaseUri, Value, Context);
349 this.Add(new SemanticTriple(Subject, Predicate, ParsedValue));
350 }
351 }
352 else
353 this.Add(new SemanticTriple(Subject, Predicate, ParsedValue));
354 }
355 }
356 }
357 }
358 else if (Doc is Array DocArray)
359 {
360 foreach (object DocItem in DocArray)
361 await this.Parse(DocItem, Context, BaseUri, this.CreateBlankNode());
362 }
363
364 return ReturnValue;
365 }
366
367 private async Task<JsonLdContext> GetContext(object Value, Uri BaseUri, JsonLdContext Context)
368 {
369 if (Value is Dictionary<string, object> ContextObj)
370 return new JsonLdContext(ContextObj, BaseUri);
371 else if (Value is Array ContextArray)
372 {
373 JsonLdContext Result = new JsonLdContext();
374
375 foreach (object ContextItem in ContextArray)
376 {
377 JsonLdContext Result2 = await this.GetContext(ContextItem, BaseUri, Context);
378 Result.Append(Result2, BaseUri);
379 Context = Result;
380 }
381
382 return Result;
383 }
384 else
385 {
386 Uri ContextUri;
387
388 if (Value is string Url)
389 ContextUri = this.CreateUri(Url, BaseUri, Context);
390 else if (Value is Uri Uri2)
391 ContextUri = Uri2;
392 else
393 throw this.ParsingException("Expected context URI.");
394
395 if (contextObjects.TryGetValue(ContextUri.AbsolutePath, out KeyValuePair<DateTimeOffset, Dictionary<string, object>> P2))
396 {
397 try
398 {
399 object Obj = await InternetContent.GetAsync(ContextUri,
400 new KeyValuePair<string, string>("Accept", JsonLdCodec.DefaultContentType),
401 new KeyValuePair<string, string>("If-Modified-Since", CommonTypes.EncodeRfc822(P2.Key)));
402
403 if (Obj is JsonLdDocument ContextDoc &&
404 ContextDoc.parsedJson is Dictionary<string, object> ContextObj2 &&
405 ContextObj2.TryGetValue("@context", out Obj) &&
406 Obj is Dictionary<string, object> ContextObj3)
407 {
408 Context = new JsonLdContext(ContextObj3, BaseUri);
409 contextObjects[ContextUri.AbsolutePath] = new KeyValuePair<DateTimeOffset, Dictionary<string, object>>(
410 ContextDoc.Date ?? DateTimeOffset.Now, ContextObj3);
411 }
412 else
413 Context = new JsonLdContext(P2.Value, BaseUri);
414 }
415 catch (Exception)
416 {
417 Context = new JsonLdContext(P2.Value, BaseUri);
418 }
419 }
420 else
421 {
422 object Obj = await InternetContent.GetAsync(ContextUri,
423 new KeyValuePair<string, string>("Accept", JsonLdCodec.DefaultContentType));
424
425 if (Obj is JsonLdDocument ContextDoc &&
426 ContextDoc.parsedJson is Dictionary<string, object> ContextObj2 &&
427 ContextObj2.TryGetValue("@context", out Obj) &&
428 Obj is Dictionary<string, object> ContextObj3)
429 {
430 Context = new JsonLdContext(ContextObj3, BaseUri);
431 contextObjects[ContextUri.AbsolutePath] = new KeyValuePair<DateTimeOffset, Dictionary<string, object>>(
432 ContextDoc.Date ?? DateTimeOffset.Now, ContextObj3);
433 }
434 else
435 throw this.ParsingException("Context reference not a JSON-LD document. Expected Content-Type: " + JsonLdCodec.DefaultContentType);
436 }
437
438 return Context;
439 }
440 }
441
442 private async Task<JsonLdContext> AddType(ISemanticElement Subject, object Value, Uri BaseUri, JsonLdContext Context)
443 {
444 if (Value is string s)
445 {
446 if (TryGetContextName(s, Context, out object ContextValue))
447 {
448 Context = await this.AddType(Subject, ContextValue, BaseUri, Context);
449 }
450 else
451 this.Add(new SemanticTriple(Subject, RdfDocument.RdfType, this.CreateUriNode(s, BaseUri, Context)));
452 }
453 else if (Value is Dictionary<string, object> TypeObj)
454 {
455 foreach (KeyValuePair<string, object> P in TypeObj)
456 {
457 switch (P.Key)
458 {
459 case "@id":
460 Context = await this.AddType(Subject, P.Value, BaseUri, Context);
461 break;
462
463 case "@context":
464 JsonLdContext ScopedContext = await this.GetContext(P.Value, BaseUri, Context);
465 if (!(Context is null))
466 ScopedContext.Append(Context);
467
468 Context = ScopedContext;
469 break;
470 }
471 }
472 }
473 else if (Value is Array A)
474 {
475 foreach (object Item in A)
476 await this.AddType(Subject, Item, BaseUri, Context);
477 }
478 else
479 throw this.ParsingException("Unsupported @type: " + Value?.ToString());
480
481 return Context;
482 }
483
484 private static bool ExpandPrefixes(ref string Name, JsonLdContext Context)
485 {
486 if (Context is null)
487 return false;
488
489 int i = Name.IndexOf(':');
490 if (i < 0)
491 return false;
492
493 string Prefix = Name.Substring(0, i);
494
495 if (!Context.TryGetObjectValue(Prefix, out object Value))
496 return false;
497
498 if (Value is string PrefixUrl)
499 {
500 Name = PrefixUrl + Name.Substring(i + 1);
501 return true;
502 }
503
504 if (Value is Dictionary<string, object> Obj)
505 {
506 foreach (KeyValuePair<string, object> P in Obj)
507 {
508 switch (P.Key)
509 {
510 case "@id":
511 PrefixUrl = P.Value as string;
512 if (!(PrefixUrl is null))
513 {
514 Name = PrefixUrl + Name.Substring(i + 1);
515 return true;
516 }
517 break;
518 }
519 }
520 }
521
522 return false;
523 }
524
525 private static bool TryGetContextName(string Name, JsonLdContext Context, out object Value)
526 {
527 if (Name.StartsWith("@"))
528 {
529 Value = null;
530 return false;
531 }
532
533 if (Context is null)
534 {
535 Value = null;
536 return false;
537 }
538
539 if (ExpandPrefixes(ref Name, Context))
540 {
541 Value = Name;
542 return true;
543 }
544
545 if (Context.TryGetObjectValue(Name, out Value))
546 return true;
547
548 if (!(Context.Vocabulary is null) && Name.IndexOf(':') < 0)
549 {
550 Value = Context.Vocabulary.AbsoluteUri + Name;
551 return true;
552 }
553
554 Value = null;
555 return false;
556 }
557
558 private async Task<ISemanticElement> ParseValue(Uri BaseUri, object Value, JsonLdContext Context)
559 {
560 if (Value is Dictionary<string, object> Obj)
561 {
562 ISemanticElement Object = null;
563
564 foreach (KeyValuePair<string, object> P in Obj)
565 {
566 switch (P.Key)
567 {
568 case "@id":
569 if (P.Value is string s)
570 Object = this.CreateUriNode(s, BaseUri, Context);
571 else
572 throw this.ParsingException("Invalid @id: " + P.Value.ToString());
573 break;
574
575 case "@base":
576 s = P.Value as string;
577 if (!(s is null))
578 BaseUri = this.CreateUri(s, BaseUri, Context);
579 else
580 throw this.ParsingException("Invalid @base: " + P.Value.ToString());
581 break;
582
583 }
584 }
585
586 if (Object is null)
587 Object = this.CreateBlankNode();
588
589 return await this.Parse(Obj, Context, BaseUri, Object) ?? Object;
590 }
591 else if (Value is Uri Uri)
592 return new UriNode(Uri, Uri.ToString());
593 else
595 }
596
600 public object ParsedJson => this.parsedJson;
601
605 public string Text => this.text;
606
610 public DateTimeOffset? Date => this.date;
611
616 public Task DecodeMetaInformation(HttpResponseMessage HttpResponse)
617 {
618 this.date = HttpResponse.Headers.Date;
619 return Task.CompletedTask;
620 }
621
622 }
623}
Helps with parsing of commong data types.
Definition: CommonTypes.cs:13
static string EncodeRfc822(DateTime Timestamp)
Encodes a date and time, according to RFC 822 §5.
Definition: CommonTypes.cs:667
Static class managing encoding and decoding of internet content.
static Task< object > GetAsync(Uri Uri, params KeyValuePair< string, string >[] Headers)
Gets a resource, given its URI.
Helps with common JSON-related tasks.
Definition: JSON.cs:14
static object Parse(string Json)
Parses a JSON string.
Definition: JSON.cs:43
override void Add(ISemanticTriple Triple)
Adds a triple to the cube.
Encoder and Decoder of semantic information in JSON-LD Documents.
Definition: JsonLdCodec.cs:13
const string DefaultContentType
application/ld+json
Definition: JsonLdCodec.cs:44
Contains semantic information stored in a JSON-LD document.
DateTimeOffset? Date
Server timestamp of document.
static Task< JsonLdDocument > CreateAsync(string Text, Uri BaseUri)
Contains semantic information stored in a turtle document.
static Task< JsonLdDocument > CreateAsync(object Doc, string Text, Uri BaseUri)
Contains semantic information stored in a turtle document.
string Text
Text representation.
static Task< JsonLdDocument > CreateAsync(string Text, Uri BaseUri, string BlankNodeIdPrefix)
Contains semantic information stored in a turtle document.
static Task< JsonLdDocument > CreateAsync(string Text)
Contains semantic information stored in a turtle document.
static Task< JsonLdDocument > CreateAsync(string Text, Uri BaseUri, string BlankNodeIdPrefix, BlankNodeIdMode BlankNodeIdMode)
Contains semantic information stored in a turtle document.
static Task< JsonLdDocument > CreateAsync(object Doc, string Text)
Contains semantic information stored in a turtle document.
static Task< JsonLdDocument > CreateAsync(object Doc, string Text, Uri BaseUri, string BlankNodeIdPrefix)
Contains semantic information stored in a turtle document.
static async Task< JsonLdDocument > CreateAsync(object Doc, string Text, Uri BaseUri, string BlankNodeIdPrefix, BlankNodeIdMode BlankNodeIdMode)
Contains semantic information stored in a turtle document.
Task DecodeMetaInformation(HttpResponseMessage HttpResponse)
Decodes meta-information available in the HTTP Response.
object ParsedJson
Original content, as parsed JSON.
Represents a blank node
Definition: BlankNode.cs:7
static ISemanticLiteral EncapsulateLiteral(object Value)
Encapsulates an object as a semantic literal.
static ISemanticElement Parse(string Value, string DataType, string Language)
Parses a string literal value.
Contains semantic information stored in an RDF document.
Definition: RdfDocument.cs:21
static UriNode RdfType
rdf:type predicate
Definition: RdfDocument.cs:25
Implements an in-memory cache.
Definition: Cache.cs:15
Interface for content classes, that process information available in HTTP headers in the response.
Interface for semantic nodes.
BlankNodeIdMode
How blank node IDs are generated
delegate string ToString(IElement Element)
Delegate for callback methods that convert an element value to a string.
Prefix
SI prefixes. http://physics.nist.gov/cuu/Units/prefixes.html
Definition: Prefixes.cs:11