├── GitVersionConfig.yaml ├── codecov.yml ├── assets ├── icon.png └── icon.xcf ├── llite ├── LLite.exe ├── FSharp.Core.dll └── LLite.exe.config ├── .paket ├── paket.bootstrapper.exe └── paket.targets ├── src ├── JsonLD.Entities.Tests │ ├── FodyWeavers.xml │ ├── Entities │ │ ├── ParentWithConverter.cs │ │ ├── Book.cs │ │ ├── ClassWithSomeUris.cs │ │ ├── PropertiesMappedToAbsoluteUrls.cs │ │ ├── PersonWithPrefixedClass.cs │ │ ├── Person.cs │ │ ├── HasInterestsList.cs │ │ ├── HasInterestsSet.cs │ │ ├── HasInterestsCollection.cs │ │ ├── HasInterestsArray.cs │ │ ├── HasInterestsEnumerable.cs │ │ ├── HasInterestsGenerator.cs │ │ └── WithConverter.cs │ ├── GlobalSuppressions.cs │ ├── ContextTestEntities │ │ ├── DerivedClass.cs │ │ └── BaseClass.cs │ ├── paket.references │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── app.config │ ├── ContextTests │ │ ├── VocabContextTests.cs │ │ └── ContextExtensionsTests.cs │ ├── IriRefConverterTests.cs │ ├── Bindings │ │ ├── SerializerTestContext.cs │ │ ├── SerializingSteps.cs │ │ ├── EntityVerificationSteps.cs │ │ └── DeserializingSteps.cs │ ├── ContextResolverTests.cs │ ├── Serializing.feature │ ├── JsonLD.Entities.Tests.csproj │ ├── IriRefTests.cs │ ├── DeserializingRDF.feature │ ├── Helpers │ │ └── TypeExtensionTests.cs │ └── EntitySerializerTests.cs ├── Documentation │ ├── GlobalSuppressions.cs │ ├── paket.references │ ├── Deserializing │ │ └── LiteralValues │ │ │ ├── IPAddressConverter.mkd │ │ │ ├── Readme.cs │ │ │ └── Readme.mkd │ ├── GlobalSuppressions.mkd │ ├── Documentation.csproj │ ├── Serializing │ │ ├── EntityTypes │ │ │ ├── Readme.cs │ │ │ └── Readme.mkd │ │ ├── EnsuringCompactedResource │ │ │ ├── Readme.cs │ │ │ └── Readme.mkd │ │ └── WorkingWithURIs │ │ │ ├── Readme.mkd │ │ │ └── Readme.cs │ ├── CreatingContext │ │ ├── FluentContext │ │ │ ├── Readme.cs │ │ │ └── Readme.mkd │ │ └── AutoContext │ │ │ └── Readme.mkd │ ├── ResolvingContext │ │ ├── Readme.cs │ │ └── Readme.mkd │ ├── Readme.cs │ └── Readme.mkd └── JsonLD.Entities │ ├── paket.references │ ├── FodyWeavers.xml │ ├── Settings.StyleCop │ ├── SerializeCompactedAttribute.cs │ ├── SerializationOptions.cs │ ├── IContextProvider.cs │ ├── NullFrameProvider.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── JsonLdSerializer.cs │ ├── NullContextProvider.cs │ ├── IFrameProvider.cs │ ├── Context │ ├── Base.cs │ ├── Vocab.cs │ ├── StringExtensions.cs │ ├── VocabContext.cs │ ├── CoercionBuilder.cs │ ├── PropertyBuilder.cs │ ├── ContainerBuilder.cs │ ├── ContextExtensions.cs │ ├── AutoContext.cs │ ├── PropertyExtensions.cs │ └── AutoContextBase.cs │ ├── JsonLdNamingStrategy.cs │ ├── Converters │ ├── JsonLdSetConverter.cs │ ├── JsonLdArrayConverter.cs │ ├── StringUriConverter.cs │ ├── JsonLdListConverter.cs │ ├── JsonLdCollectionConverter.cs │ ├── IriRefConverter.cs │ └── JsonLdLiteralConverter.cs │ ├── ContextNotFoundException.cs │ ├── paket.template │ ├── IEntitySerializer.cs │ ├── StaticContextProvider.cs │ ├── StaticFrameProvider.cs │ ├── JsonLD.Entities.csproj │ ├── TypeExtension.cs │ ├── IriRef.cs │ ├── JsonLdKeywords.cs │ ├── JsonLdContractResolver.cs │ ├── EntitySerializer.cs │ └── ContextResolver.cs ├── CHANGELOG.md ├── appveyor.yml ├── .gitattributes ├── LICENSE ├── paket.dependencies ├── JsonLd.Entities.sln.DotSettings ├── JsonLd.Entities.sln ├── readme.md └── .gitignore /GitVersionConfig.yaml: -------------------------------------------------------------------------------- 1 | continuous-delivery-fallback-tag: pre -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | token: a3f61881-ee57-4ada-a674-402319c6d2af 3 | -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibus/JsonLD.Entities/HEAD/assets/icon.png -------------------------------------------------------------------------------- /assets/icon.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibus/JsonLD.Entities/HEAD/assets/icon.xcf -------------------------------------------------------------------------------- /llite/LLite.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibus/JsonLD.Entities/HEAD/llite/LLite.exe -------------------------------------------------------------------------------- /llite/FSharp.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibus/JsonLD.Entities/HEAD/llite/FSharp.Core.dll -------------------------------------------------------------------------------- /.paket/paket.bootstrapper.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibus/JsonLD.Entities/HEAD/.paket/paket.bootstrapper.exe -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/Documentation/GlobalSuppressions.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibus/JsonLD.Entities/HEAD/src/Documentation/GlobalSuppressions.cs -------------------------------------------------------------------------------- /src/JsonLD.Entities/paket.references: -------------------------------------------------------------------------------- 1 | json-ld.net 2 | NullGuard.Fody 3 | InfoOf.Fody 4 | Dynamitey 5 | Costura.Fody 6 | tpluscode.Library.Ruleset 7 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/Documentation/paket.references: -------------------------------------------------------------------------------- 1 | Newtonsoft.Json 2 | xUnit 3 | xunit.runner.visualstudio 4 | xunit.extensibility.execution 5 | dotnet-xunit 6 | json-ld.net 7 | Microsoft.NET.Test.Sdk 8 | -------------------------------------------------------------------------------- /src/Documentation/Deserializing/LiteralValues/IPAddressConverter.mkd: -------------------------------------------------------------------------------- 1 | ``` c# 2 | using System; 3 | using System.Net; 4 | using JsonLD.Entities.Converters; 5 | using Newtonsoft.Json; 6 | ``` 7 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Entities/ParentWithConverter.cs: -------------------------------------------------------------------------------- 1 | namespace JsonLD.Entities.Tests.Entities 2 | { 3 | public class ParentWithConverter 4 | { 5 | public WithConverter Child { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Entities/Book.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace JsonLD.Entities.Tests.Entities 4 | { 5 | public class Book 6 | { 7 | public Uri Id { get; set; } 8 | 9 | public Person Author { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Entities/ClassWithSomeUris.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace JsonLD.Entities.Tests.Entities 4 | { 5 | public class ClassWithSomeUris 6 | { 7 | public IriRef Property { get; set; } 8 | 9 | public Uri UriProperty { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1405:ComVisibleTypeBaseTypesShouldBeComVisible", Scope = "type", Target = "JsonLD.Entities.Tests.Entities.WithConverter+DummyConverter", Justification = "tests only")] 2 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/ContextTestEntities/DerivedClass.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace JsonLD.Entities.Tests.ContextTestEntities 4 | { 5 | public class DerivedClass : BaseClass 6 | { 7 | [JsonProperty("foaf:name")] 8 | public string Name { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/paket.references: -------------------------------------------------------------------------------- 1 | SpecFlow 2 | SpecFlow.xunit 3 | xUnit 4 | xunit.runner.visualstudio 5 | xunit.extensibility.execution 6 | dotnet-xunit 7 | InfoOf.Fody 8 | dotNetRDF 9 | Dynamitey 10 | FakeItEasy 11 | json-ld.net 12 | Rdf.Vocabularies 13 | tpluscode.UnitTests.Ruleset 14 | Microsoft.NET.Test.Sdk 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. See [versionize](https://github.com/saintedlama/versionize) for commit guidelines. 4 | 5 | 6 | ## 0.3.6 (2019-11-6) 7 | 8 | ### Bug Fixes 9 | 10 | * don't throw when literal converter returns null 11 | 12 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Entities/PropertiesMappedToAbsoluteUrls.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace JsonLD.Entities.Tests.Entities 4 | { 5 | public class PropertiesMappedToAbsoluteUrls 6 | { 7 | [JsonProperty("http://xmlns.com/foaf/0.1/givenName")] 8 | public string Name { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("JsonLD.Entities.Tests")] 5 | [assembly: AssemblyProduct("JsonLD.Entities.Tests")] 6 | [assembly: AssemblyCopyright("Copyright © 2014")] 7 | [assembly: Guid("a5445151-2f20-4c82-a1b5-95c2279692a2")] 8 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Entities/PersonWithPrefixedClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace JsonLD.Entities.Tests.Entities 4 | { 5 | public class PersonWithPrefixedClass 6 | { 7 | public Uri Id { get; set; } 8 | 9 | public string Type 10 | { 11 | get { return "ex:Person"; } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Settings.StyleCop: -------------------------------------------------------------------------------- 1 | 2 | 3 | VersionAssemblyInfo.cs 4 | NQuadsParser.grammar.cs 5 | 6 | 7 | False 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Entities/Person.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace JsonLD.Entities.Tests.Entities 4 | { 5 | public class Person 6 | { 7 | public Uri Id { get; set; } 8 | 9 | public string Name { get; set; } 10 | 11 | public string Surname { get; set; } 12 | 13 | public int Age { get; set; } 14 | 15 | public DateTime BirthDate { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/ContextTestEntities/BaseClass.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | 3 | namespace JsonLD.Entities.Tests.ContextTestEntities 4 | { 5 | public class BaseClass 6 | { 7 | protected static JObject GetContext(BaseClass obj) 8 | { 9 | return new JObject 10 | { 11 | { "foaf", "http://xmlns.com/foaf/0.1/" } 12 | }; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/SerializeCompactedAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace JsonLD.Entities 4 | { 5 | /// 6 | /// Marker attribute used to inform the that 7 | /// the annotated class should be compacted after being serialized 8 | /// 9 | [AttributeUsage(AttributeTargets.Class)] 10 | public class SerializeCompactedAttribute : Attribute 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Entities/HasInterestsList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace JsonLD.Entities.Tests.Entities 4 | { 5 | public class HasInterestsList 6 | { 7 | public HasInterestsList() 8 | { 9 | this.Interests = new List(); 10 | } 11 | 12 | public IList Interests { get; set; } 13 | 14 | public void AddInterest(string interst) 15 | { 16 | this.Interests.Add(interst); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Entities/HasInterestsSet.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace JsonLD.Entities.Tests.Entities 4 | { 5 | public class HasInterestsSet 6 | { 7 | public HasInterestsSet() 8 | { 9 | this.Interests = new HashSet(); 10 | } 11 | 12 | public ISet Interests { get; set; } 13 | 14 | public void AddInterest(string interst) 15 | { 16 | this.Interests.Add(interst); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Entities/HasInterestsCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace JsonLD.Entities.Tests.Entities 4 | { 5 | public class HasInterestsCollection 6 | { 7 | public HasInterestsCollection() 8 | { 9 | this.Interests = new List(); 10 | } 11 | 12 | public ICollection Interests { get; set; } 13 | 14 | public void AddInterest(string interst) 15 | { 16 | this.Interests.Add(interst); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/SerializationOptions.cs: -------------------------------------------------------------------------------- 1 | namespace JsonLD.Entities 2 | { 3 | /// 4 | /// Represents various options, which modify the behavior of 5 | /// 6 | /// 7 | public class SerializationOptions 8 | { 9 | /// 10 | /// Gets or sets a value indicating whether the entity to be 11 | /// compacted with the current @context after being serialized 12 | /// 13 | public bool SerializeCompacted { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/IContextProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace JsonLD.Entities 5 | { 6 | /// 7 | /// Contract for classes, which provide JSON-LD @context for given types 8 | /// 9 | public interface IContextProvider 10 | { 11 | /// 12 | /// Gets the expanded context for a give serialized type.. 13 | /// 14 | /// Type of the model. 15 | JToken GetContext(Type modelType); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | before_build: 2 | - ps: gitversion /l console /output buildserver /updateassemblyinfo 3 | 4 | build_script: 5 | - ps: .\build.ps1 -Target CI -verbosity Verbose -configuration Release 6 | 7 | artifacts: 8 | - path: 'nugets\*.nupkg' 9 | 10 | cache: 11 | - packages -> paket.lock 12 | 13 | deploy: 14 | - provider: Environment 15 | name: NuGet 16 | on: 17 | branch: /v\d\.\d(\.\d)?/ 18 | appveyor_repo_tag: true 19 | - provider: Environment 20 | name: NuGet 21 | on: 22 | branch: master 23 | appveyor_repo_tag: true -------------------------------------------------------------------------------- /src/JsonLD.Entities/NullFrameProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json.Linq; 3 | using NullGuard; 4 | 5 | namespace JsonLD.Entities 6 | { 7 | /// 8 | /// Null-pattern for 9 | /// 10 | internal class NullFrameProvider : IFrameProvider 11 | { 12 | /// 13 | /// Always return null 14 | /// 15 | [return: AllowNull] 16 | public JObject GetFrame(Type modelType) 17 | { 18 | return null; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("JsonLD.Entities")] 5 | [assembly: AssemblyDescription("Simple POCO object seriallization for JSON-LD")] 6 | [assembly: AssemblyProduct("JsonLD.Entities")] 7 | [assembly: AssemblyCopyright("Copyright © 2014-2016 Tomasz Pluskiewicz")] 8 | [assembly: AssemblyCompany("Tomasz Pluskiewicz")] 9 | 10 | [assembly: ComVisible(false)] 11 | 12 | // The following GUID is for the ID of the typelib if this project is exposed to COM 13 | [assembly: Guid("ee2b4e4a-32c1-4636-9dc0-09fa135eed99")] 14 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/ContextTests/VocabContextTests.cs: -------------------------------------------------------------------------------- 1 | using JsonLD.Entities.Context; 2 | using Xunit; 3 | 4 | namespace JsonLD.Entities.Tests.ContextTests 5 | { 6 | public class VocabContextTests 7 | { 8 | [Fact] 9 | public void When_created_should_map_property_using_namespace_prefix() 10 | { 11 | // given 12 | var context = new VocabContext("http://example.api/o#"); 13 | 14 | // then 15 | Assert.Equal("http://example.api/o#title", context["title"].ToString()); 16 | } 17 | 18 | public class Issue 19 | { 20 | public string Title { get; set; } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /llite/LLite.exe.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Entities/HasInterestsArray.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace JsonLD.Entities.Tests.Entities 5 | { 6 | public class HasInterestsArray 7 | { 8 | private IList interests; 9 | 10 | public HasInterestsArray() 11 | { 12 | this.interests = new List(); 13 | } 14 | 15 | public string[] Interests 16 | { 17 | get { return this.interests.ToArray(); } 18 | set { this.interests = value; } 19 | } 20 | 21 | public void AddInterest(string interst) 22 | { 23 | this.interests.Add(interst); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/JsonLdSerializer.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace JsonLD.Entities 4 | { 5 | /// 6 | /// A serializer set-up to deserialize valid JSON-LD 7 | /// 8 | public sealed class JsonLdSerializer : JsonSerializer 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | public JsonLdSerializer() 14 | { 15 | this.DateFormatHandling = DateFormatHandling.IsoDateFormat; 16 | this.ContractResolver = new JsonLdContractResolver(); 17 | this.NullValueHandling = NullValueHandling.Ignore; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/NullContextProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json.Linq; 3 | using NullGuard; 4 | 5 | namespace JsonLD.Entities 6 | { 7 | /// 8 | /// Null pattern for 9 | /// 10 | public class NullContextProvider : IContextProvider 11 | { 12 | /// 13 | /// Gets the expanded context for a give serialized type.. 14 | /// 15 | /// Type of the model. 16 | /// null context 17 | [return: AllowNull] 18 | public JToken GetContext(Type modelType) 19 | { 20 | return null; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Entities/HasInterestsEnumerable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace JsonLD.Entities.Tests.Entities 5 | { 6 | public class HasInterestsEnumerable 7 | { 8 | private IList interests; 9 | 10 | public HasInterestsEnumerable() 11 | { 12 | this.Interests = new List(); 13 | } 14 | 15 | public IEnumerable Interests 16 | { 17 | get { return this.interests; } 18 | set { this.interests = value.ToList(); } 19 | } 20 | 21 | public void AddInterest(string interst) 22 | { 23 | this.interests.Add(interst); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/IFrameProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace JsonLD.Entities 5 | { 6 | /// 7 | /// Contract for classes, which provide a JSON-LD frame 8 | /// to be used for deserializing complex objects 9 | /// 10 | public interface IFrameProvider 11 | { 12 | /// 13 | /// Gets the frame. 14 | /// 15 | /// Type of the model. 16 | /// 17 | /// If the frame contains a @context, it will be replaced by 18 | /// a context provided by 19 | /// 20 | JObject GetFrame(Type modelType); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Documentation/GlobalSuppressions.mkd: -------------------------------------------------------------------------------- 1 | ``` c# 2 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1405:ComVisibleTypeBaseTypesShouldBeComVisible", Scope = "type", Target = "DeserializationOfLiterals+IPAddressConverter")] 3 | // This file is used by Code Analysis to maintain SuppressMessage 4 | // attributes that are applied to this project. 5 | // Project-level suppressions either have no target or are given 6 | // a specific target and scoped to a namespace, type, member, etc. 7 | // 8 | // To add a suppression to this file, right-click the message in the 9 | // Code Analysis results, point to "Suppress Message", and click 10 | // "In Suppression File". 11 | // You do not need to add suppressions to this file manually. 12 | ``` 13 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/IriRefConverterTests.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using JsonLD.Entities.Converters; 3 | using Newtonsoft.Json; 4 | using Xunit; 5 | 6 | namespace JsonLD.Entities.Tests 7 | { 8 | public class IriRefConverterTests 9 | { 10 | private readonly IriRefConverter converter = new IriRefConverter(); 11 | 12 | [Fact] 13 | public void Should_serialize_empty_iriref_as_null() 14 | { 15 | // given 16 | var stringWriter = new StringWriter(); 17 | 18 | // when 19 | this.converter.WriteJson(new JsonTextWriter(stringWriter), default(IriRef), new JsonLdSerializer()); 20 | 21 | // then 22 | Assert.Equal("null", stringWriter.ToString()); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Context/Base.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace JsonLD.Entities.Context 5 | { 6 | /// 7 | /// Used to create JSON-LD @context 8 | /// 9 | public static class Base 10 | { 11 | /// 12 | /// Creates a property JSON-LD @base 13 | /// 14 | public static JProperty Is(string baseUri) 15 | { 16 | return new JProperty(JsonLdKeywords.Base, baseUri); 17 | } 18 | 19 | /// 20 | /// Creates a property JSON-LD @base 21 | /// 22 | public static JProperty Is(Uri baseUri) 23 | { 24 | return new JProperty(JsonLdKeywords.Base, baseUri); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Context/Vocab.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace JsonLD.Entities.Context 5 | { 6 | /// 7 | /// Used to create JSON-LD @context 8 | /// 9 | public static class Vocab 10 | { 11 | /// 12 | /// Creates a property JSON-LD @vocab 13 | /// 14 | public static JProperty Is(string baseUri) 15 | { 16 | return new JProperty(JsonLdKeywords.Vocab, baseUri); 17 | } 18 | 19 | /// 20 | /// Creates a property JSON-LD @vocab 21 | /// 22 | public static JProperty Is(Uri baseUri) 23 | { 24 | return new JProperty(JsonLdKeywords.Vocab, baseUri); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/JsonLdNamingStrategy.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Serialization; 2 | 3 | namespace JsonLD.Entities 4 | { 5 | /// 6 | /// Ensures that JSON-LD keywords are serialized correctly 7 | /// 8 | public class JsonLdNamingStrategy : CamelCaseNamingStrategy 9 | { 10 | /// 11 | /// Resolves the name of the property. 12 | /// 13 | protected override string ResolvePropertyName(string propertyName) 14 | { 15 | var keyword = JsonLdKeywords.GetKeywordForProperty(propertyName); 16 | if (keyword != null) 17 | { 18 | return keyword; 19 | } 20 | 21 | return base.ResolvePropertyName(propertyName); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Entities/HasInterestsGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace JsonLD.Entities.Tests.Entities 4 | { 5 | public class HasInterestsGenerator 6 | { 7 | private readonly IList interests; 8 | 9 | public HasInterestsGenerator() 10 | { 11 | this.interests = new List(); 12 | } 13 | 14 | public IEnumerable Interests 15 | { 16 | get 17 | { 18 | foreach (var interest in this.interests) 19 | { 20 | yield return interest; 21 | } 22 | } 23 | } 24 | 25 | public void AddInterest(string interst) 26 | { 27 | this.interests.Add(interst); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Documentation/Documentation.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JsonLD.Docu 5 | JsonLD.Docu 6 | net462 7 | true 8 | full 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Converters/JsonLdSetConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | 5 | namespace JsonLD.Entities.Converters 6 | { 7 | /// 8 | /// Converter for JSON-LD @sets 9 | /// 10 | /// collection element type 11 | public class JsonLdSetConverter : JsonLdCollectionConverter 12 | { 13 | /// 14 | /// Determines whether this instance can convert the specified object type. 15 | /// 16 | public override bool CanConvert(Type objectType) 17 | { 18 | return typeof(ISet).IsAssignableFrom(objectType); 19 | } 20 | 21 | /// 22 | /// Wraps the elements in 23 | /// 24 | protected override object CreateReturnedContainer(IEnumerable elements) 25 | { 26 | return new HashSet(elements); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Converters/JsonLdArrayConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace JsonLD.Entities.Converters 7 | { 8 | /// 9 | /// Converter for JSON-LD @sets 10 | /// 11 | /// collection element type 12 | public class JsonLdArrayConverter : JsonLdCollectionConverter 13 | { 14 | /// 15 | /// Determines whether this instance can convert the specified object type. 16 | /// 17 | public override bool CanConvert(Type objectType) 18 | { 19 | return typeof(T[]).IsAssignableFrom(objectType); 20 | } 21 | 22 | /// 23 | /// Creates an array of 24 | /// 25 | protected override object CreateReturnedContainer(IEnumerable elements) 26 | { 27 | return elements.ToArray(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/ContextNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace JsonLD.Entities 4 | { 5 | /// 6 | /// Represents errors which occur if JSON-LD @context cannot be found for a given type 7 | /// 8 | [Serializable] 9 | public class ContextNotFoundException : Exception 10 | { 11 | private readonly Type entityType; 12 | 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | /// Type of the entity. 17 | public ContextNotFoundException(Type entityType) 18 | { 19 | this.entityType = entityType; 20 | } 21 | 22 | /// 23 | /// Gets a message that describes the current exception. 24 | /// 25 | public override string Message 26 | { 27 | get { return string.Format("JSON-LD context not found for type {0}", this.entityType.FullName); } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Entities/WithConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace JsonLD.Entities.Tests.Entities 5 | { 6 | [JsonConverter(typeof(DummyConverter))] 7 | public class WithConverter 8 | { 9 | public WithConverter(string id) 10 | { 11 | this.Id = id; 12 | } 13 | 14 | public string Id { get; private set; } 15 | 16 | public class DummyConverter : JsonConverter 17 | { 18 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 19 | { 20 | throw new NotImplementedException(); 21 | } 22 | 23 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 24 | { 25 | reader.Skip(); 26 | 27 | return new WithConverter("TheIdentifier"); 28 | } 29 | 30 | public override bool CanConvert(Type objectType) 31 | { 32 | throw new NotImplementedException(); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Tomasz Pluskiewicz T+Code 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/paket.template: -------------------------------------------------------------------------------- 1 | type file 2 | id JsonLD.Entities 3 | include-pdbs true 4 | authors tpluscode 5 | description 6 | Simple POCO object seriallization for JSON-LD 7 | dependencies 8 | json-ld.net >= 1.0.5 9 | Newtonsoft.Json >= 9 10 | Dynamitey >= 2 11 | excludeddependencies 12 | NullGuard.Fody 13 | Costura.Fody 14 | InfoOf.Fody 15 | iconUrl 16 | https://raw.githubusercontent.com/wikibus/JsonLD.Entities/master/assets/icon.png 17 | tags 18 | rdf json-ld turtle ttl n3 notation3 semantic web 19 | copyright 20 | Copyright 2014-2016 Tomasz Pluskiewicz 21 | Icon designed by Piotrek Chuchla from The Noun Project 22 | licenseUrl 23 | https://raw.githubusercontent.com/wikibus/JsonLD.Entities/master/LICENSE 24 | projectUrl 25 | http://github.org/wikibus/JsonLd.Entities 26 | releaseNotes 27 | update json-ld.net dependency 28 | files 29 | bin/Release/net452/JsonLd.Entities.dll ==> lib/net452 30 | bin/Release/net452/JsonLd.Entities.pdb ==> lib/net452 31 | bin/Release/net452/JsonLd.Entities.xml ==> lib/net452 32 | bin/Release/netstandard1.5/JsonLd.Entities.dll ==> lib/netstandard1.5 33 | bin/Release/netstandard1.5/JsonLd.Entities.pdb ==> lib/netstandard1.5 34 | bin/Release/netstandard1.5/JsonLd.Entities.xml ==> lib/netstandard1.5 -------------------------------------------------------------------------------- /paket.dependencies: -------------------------------------------------------------------------------- 1 | source https://nuget.org/api/v2 2 | source https://www.myget.org/F/tpluscode/api/v3/index.json 3 | 4 | framework: netstandard1.5, net452, net462, netcoreapp2.0 5 | content: none 6 | redirects: off 7 | 8 | nuget json-ld.net 9 | nuget SpecFlow 2.4.0-preview20180814 10 | nuget SpecFlow.xUnit 2.4.0-preview20180814 11 | nuget NullGuard.Fody 12 | nuget xUnit 13 | nuget xunit.runner.visualstudio 14 | nuget xunit.extensibility.execution 15 | clitool dotnet-xunit 16 | nuget MethodTimer.Fody 17 | nuget dotNetRDF 18 | nuget HtmlAgilityPack 19 | nuget InfoOf.Fody 20 | nuget FakeItEasy 21 | nuget Dynamitey 22 | nuget Costura.Fody 23 | nuget Rdf.Vocabularies prerelease 24 | nuget Newtonsoft.Json 25 | nuget tpluscode.NoDocumentation.Ruleset 26 | nuget tpluscode.Library.Ruleset 27 | nuget tpluscode.UnitTests.Ruleset 28 | nuget Microsoft.NET.Test.Sdk 29 | 30 | gist tpluscode/a285267d2543466fc35c3a168c846f9f 31 | 32 | group tools 33 | source https://nuget.org/api/v2 34 | nuget Cake 35 | nuget opencover 36 | nuget gitlink 37 | nuget codecov 38 | 39 | group addins 40 | source https://nuget.org/api/v2 41 | nuget cake.paket 42 | nuget cake.codecov 43 | 44 | group modules 45 | source https://nuget.org/api/v2 46 | nuget Cake.Paket.Module -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Bindings/SerializerTestContext.cs: -------------------------------------------------------------------------------- 1 | using FakeItEasy; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace JsonLD.Entities.Tests.Bindings 5 | { 6 | public class SerializerTestContext 7 | { 8 | private readonly IContextProvider contextProvider; 9 | private readonly EntitySerializer serializer; 10 | private readonly IFrameProvider frameProvider; 11 | 12 | public SerializerTestContext() 13 | { 14 | this.contextProvider = A.Fake(); 15 | this.frameProvider = A.Fake(); 16 | this.serializer = new EntitySerializer(this.contextProvider, this.frameProvider); 17 | } 18 | 19 | public IContextProvider ContextProvider 20 | { 21 | get { return this.contextProvider; } 22 | } 23 | 24 | public EntitySerializer Serializer 25 | { 26 | get { return this.serializer; } 27 | } 28 | 29 | public string NQuads { get; set; } 30 | 31 | public JToken JsonLdObject { get; set; } 32 | 33 | public object Object { get; set; } 34 | 35 | public IFrameProvider FrameProvider 36 | { 37 | get { return this.frameProvider; } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/IEntitySerializer.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | 3 | namespace JsonLD.Entities 4 | { 5 | /// 6 | /// Contract for serializing and de-serializing RDF resources 7 | /// 8 | public interface IEntitySerializer 9 | { 10 | /// 11 | /// Deserializes the NQuads into a typed entity 12 | /// 13 | /// destination entity model type 14 | /// RDF data in NQuads. 15 | T Deserialize(string nQuads); 16 | 17 | /// 18 | /// Deserializes the JSON-LD object into a typed entity 19 | /// 20 | /// destination entity model 21 | /// a JSON-LD object 22 | T Deserialize(JToken jsonLd); 23 | 24 | /// 25 | /// Serializes the specified entity as JSON-LD. 26 | /// 27 | /// The entity. 28 | /// Options, which modify how the object is serialized. 29 | /// A compacted JSON-LD object 30 | JObject Serialize(object entity, SerializationOptions options = null); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Converters/StringUriConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using NullGuard; 4 | 5 | namespace JsonLD.Entities.Converters 6 | { 7 | /// 8 | /// Converter, which ensures that Uris are serialized as strings 9 | /// 10 | public class StringUriConverter : JsonConverter 11 | { 12 | /// 13 | /// Writes the JSON representation of the Uri. 14 | /// 15 | public override void WriteJson(JsonWriter writer, [AllowNull] object value, JsonSerializer serializer) 16 | { 17 | writer.WriteValue(value.ToString()); 18 | } 19 | 20 | /// 21 | /// Reads the JSON representation of the Uri. 22 | /// 23 | public override object ReadJson(JsonReader reader, Type objectType, [AllowNull] object existingValue, JsonSerializer serializer) 24 | { 25 | return new Uri(reader.Value.ToString(), UriKind.RelativeOrAbsolute); 26 | } 27 | 28 | /// 29 | /// Determines whether this instance can convert the specified object type. 30 | /// 31 | public override bool CanConvert(Type objectType) 32 | { 33 | return objectType == typeof(Uri); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /JsonLd.Entities.sln.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | DO_NOT_SHOW 3 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> 4 | <data><IncludeFilters /><ExcludeFilters><Filter ModuleMask="JsonLD.Entities.Tests" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /><Filter ModuleMask="JsonLD.Entities" ModuleVersionMask="*" ClassMask="Costura.*" FunctionMask="*" IsEnabled="True" /><Filter ModuleMask="JsonLD.Docu" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /><Filter ModuleMask="JsonLD.Entities" ModuleVersionMask="*" ClassMask="JsonLD.Entities.GitVersionInformation" FunctionMask="*" IsEnabled="True" /></ExcludeFilters></data> 5 | <data /> -------------------------------------------------------------------------------- /src/JsonLD.Entities/Context/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace JsonLD.Entities.Context 5 | { 6 | /// 7 | /// Used to create JSON-LD @context 8 | /// 9 | public static class StringExtensions 10 | { 11 | /// 12 | /// Creates a prefix definition property for a JSON-LD @context 13 | /// 14 | public static JProperty IsPrefixOf(this string prefix, string ns) 15 | { 16 | return new JProperty(prefix, ns); 17 | } 18 | 19 | /// 20 | /// Creates a prefix definition property for a JSON-LD @context 21 | /// 22 | public static JProperty IsPrefixOf(this string prefix, Uri ns) 23 | { 24 | return new JProperty(prefix, ns); 25 | } 26 | 27 | /// 28 | /// Creates a property definition for a JSON-LD @context 29 | /// 30 | public static PropertyBuilder IsProperty(this string property, string uriOrPrefixedName) 31 | { 32 | return new PropertyBuilder(property, uriOrPrefixedName); 33 | } 34 | 35 | /// 36 | /// Creates a language definition for a JSON-LD @context 37 | /// 38 | public static JProperty IsLanguage(this string languageCode) 39 | { 40 | return new JProperty(JsonLdKeywords.Language, languageCode); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/StaticContextProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json.Linq; 4 | using NullGuard; 5 | 6 | namespace JsonLD.Entities 7 | { 8 | /// 9 | /// Provides a predefined @context for each model type. 10 | /// 11 | public class StaticContextProvider : IContextProvider 12 | { 13 | private readonly IDictionary contexts; 14 | 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | public StaticContextProvider() 19 | { 20 | this.contexts = new Dictionary(); 21 | } 22 | 23 | /// 24 | /// Gets the expanded context for a give serialized type.. 25 | /// 26 | /// Type of the model. 27 | /// JSON object or null if @context not found 28 | [return: AllowNull] 29 | public JToken GetContext(Type modelType) 30 | { 31 | JToken context; 32 | this.contexts.TryGetValue(modelType, out context); 33 | return context; 34 | } 35 | 36 | /// 37 | /// Sets @context used by given . 38 | /// 39 | public void SetContext(Type type, JToken context) 40 | { 41 | this.contexts[type] = context; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/StaticFrameProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json.Linq; 4 | using NullGuard; 5 | 6 | namespace JsonLD.Entities 7 | { 8 | /// 9 | /// Provides a predefined frame for each model type. 10 | /// 11 | public class StaticFrameProvider : IFrameProvider 12 | { 13 | private readonly IDictionary frames; 14 | 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | public StaticFrameProvider() 19 | { 20 | this.frames = new Dictionary(); 21 | } 22 | 23 | /// 24 | /// Gets the frame. 25 | /// 26 | /// Type of the model. 27 | /// 28 | /// If the frame contains a @context, it will be replaced by 29 | /// a context provided by 30 | /// 31 | [return: AllowNull] 32 | public JObject GetFrame(Type modelType) 33 | { 34 | JObject context; 35 | this.frames.TryGetValue(modelType, out context); 36 | return context; 37 | } 38 | 39 | /// 40 | /// Sets frame used by given . 41 | /// 42 | public void SetFrame(Type type, JObject context) 43 | { 44 | this.frames[type] = context; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Context/VocabContext.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | 3 | namespace JsonLD.Entities.Context 4 | { 5 | /// 6 | /// Automatic @context used which constructs missing property mappings 7 | /// by appending property names to a base vocabulary URI 8 | /// 9 | /// Model type 10 | public class VocabContext : AutoContextBase 11 | { 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | /// The base URI. 16 | public VocabContext(string baseUri) 17 | : base(new VocabularyStrategy(baseUri)) 18 | { 19 | } 20 | 21 | /// 22 | /// Initializes a new instance of the class. 23 | /// 24 | /// The preexisting context. 25 | /// The base URI. 26 | public VocabContext(JObject context, string baseUri) 27 | : base(context, new VocabularyStrategy(baseUri)) 28 | { 29 | } 30 | 31 | private class VocabularyStrategy : AutoContextStrategy 32 | { 33 | private readonly string baseUri; 34 | 35 | public VocabularyStrategy(string baseUri) 36 | { 37 | this.baseUri = baseUri; 38 | } 39 | 40 | protected override string GetPropertyId(string propertyName) 41 | { 42 | return this.baseUri + propertyName; 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Context/CoercionBuilder.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NullGuard; 3 | 4 | namespace JsonLD.Entities.Context 5 | { 6 | /// 7 | /// Used to create properties with type coercion 8 | /// 9 | /// See http://www.w3.org/TR/json-ld/#type-coercion 10 | [NullGuard(ValidationFlags.All)] 11 | public class CoercionBuilder 12 | { 13 | private readonly JProperty property; 14 | 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | internal CoercionBuilder(JProperty property) 19 | { 20 | this.property = property.EnsureExpandedDefinition(); 21 | } 22 | 23 | /// 24 | /// Property values should be resolved as compact IRI, absolute IRI or relative IRI 25 | /// 26 | public PropertyBuilder Id() 27 | { 28 | return new PropertyBuilder(this.property.With(JsonLdKeywords.Type, JsonLdKeywords.Id)); 29 | } 30 | 31 | /// 32 | /// Property values should be resolved as vocabulary term, compact IRI, absolute IRI or relative IRI 33 | /// 34 | public PropertyBuilder Vocab() 35 | { 36 | return new PropertyBuilder(this.property.With(JsonLdKeywords.Type, JsonLdKeywords.Vocab)); 37 | } 38 | 39 | /// 40 | /// Property values should be interpreted as typed literals 41 | /// 42 | public PropertyBuilder Is(string dataType) 43 | { 44 | return new PropertyBuilder(this.property.With(JsonLdKeywords.Type, dataType)); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/JsonLD.Entities.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 0.3.6 5 | JsonLD.Entities 6 | JsonLD.Entities 7 | net452;netstandard1.5 8 | false 9 | false 10 | false 11 | false 12 | false 13 | false 14 | false 15 | false 16 | false 17 | bin\$(Configuration)\$(TargetFramework)\JsonLD.Entities.xml 18 | full 19 | true 20 | 21 | 22 | false 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Context/PropertyBuilder.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NullGuard; 3 | 4 | namespace JsonLD.Entities.Context 5 | { 6 | /// 7 | /// Used to build property maps for JSON-LD @context 8 | /// 9 | [NullGuard(ValidationFlags.All)] 10 | public class PropertyBuilder : JProperty 11 | { 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | public PropertyBuilder(string property, string uriOrPrefixedName) 16 | : base(property, uriOrPrefixedName) 17 | { 18 | } 19 | 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | internal PropertyBuilder(JProperty property) 24 | : base(property) 25 | { 26 | } 27 | 28 | /// 29 | /// Creates a builder for properties with type coercion 30 | /// 31 | public new CoercionBuilder Type() 32 | { 33 | return new CoercionBuilder(this); 34 | } 35 | 36 | /// 37 | /// Creates a builder for properties, which are @containers 38 | /// 39 | public ContainerBuilder Container() 40 | { 41 | return new ContainerBuilder(this); 42 | } 43 | 44 | /// 45 | /// Defines an internationalized property 46 | /// 47 | public PropertyBuilder Language([AllowNull] string languageCode) 48 | { 49 | var property = this.EnsureExpandedDefinition() 50 | .With(JsonLdKeywords.Language, languageCode); 51 | return new PropertyBuilder(property); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Context/ContainerBuilder.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NullGuard; 3 | 4 | namespace JsonLD.Entities.Context 5 | { 6 | /// 7 | /// Used to define property's @container 8 | /// 9 | [NullGuard(ValidationFlags.All)] 10 | public class ContainerBuilder 11 | { 12 | private readonly JProperty property; 13 | 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | internal ContainerBuilder(JProperty property) 18 | { 19 | this.property = property.EnsureExpandedDefinition(); 20 | } 21 | 22 | /// 23 | /// The property is a @set container 24 | /// 25 | public PropertyBuilder Set() 26 | { 27 | return new PropertyBuilder(this.property.With(JsonLdKeywords.Container, JsonLdKeywords.Set)); 28 | } 29 | 30 | /// 31 | /// The property is a @list container 32 | /// 33 | public PropertyBuilder List() 34 | { 35 | return new PropertyBuilder(this.property.With(JsonLdKeywords.Container, JsonLdKeywords.List)); 36 | } 37 | 38 | /// 39 | /// The property is a @index container 40 | /// 41 | public PropertyBuilder Index() 42 | { 43 | return new PropertyBuilder(this.property.With(JsonLdKeywords.Container, JsonLdKeywords.Index)); 44 | } 45 | 46 | /// 47 | /// The property is a @language map 48 | /// 49 | public PropertyBuilder Language() 50 | { 51 | return new PropertyBuilder(this.property.With(JsonLdKeywords.Container, JsonLdKeywords.Language)); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Converters/JsonLdListConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Newtonsoft.Json; 5 | using NullGuard; 6 | 7 | namespace JsonLD.Entities.Converters 8 | { 9 | /// 10 | /// Converter for JSON-LD @list 11 | /// 12 | /// collection element type 13 | public class JsonLdListConverter : JsonLdCollectionConverter 14 | { 15 | /// 16 | /// Reads the JSON representation of the object. 17 | /// 18 | public override object ReadJson(JsonReader reader, Type objectType, [AllowNull] object existingValue, JsonSerializer serializer) 19 | { 20 | if (reader.TokenType == JsonToken.StartObject) 21 | { 22 | reader.Read(); 23 | while (Equals("@list", reader.Value) == false) 24 | { 25 | reader.Read(); 26 | } 27 | 28 | reader.Read(); 29 | var actualCollection = serializer.Deserialize(reader, objectType); 30 | reader.Read(); 31 | return actualCollection; 32 | } 33 | 34 | return base.ReadJson(reader, objectType, existingValue, serializer); 35 | } 36 | 37 | /// 38 | /// Determines whether this instance can convert the specified object type. 39 | /// 40 | public override bool CanConvert(Type objectType) 41 | { 42 | return typeof(IList).IsAssignableFrom(objectType); 43 | } 44 | 45 | /// 46 | /// Wraps elements in 47 | /// 48 | protected override object CreateReturnedContainer(IEnumerable elements) 49 | { 50 | return new List(elements); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Context/ContextExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Newtonsoft.Json.Linq; 4 | 5 | namespace JsonLD.Entities.Context 6 | { 7 | /// 8 | /// Extension to work with JSON-LD @contexts 9 | /// 10 | public static class ContextExtensions 11 | { 12 | /// 13 | /// Merges the multiple @contexts by combining them within a single array. 14 | /// 15 | /// The context. 16 | /// The other contexts. 17 | /// 18 | /// original or a flattened 19 | /// 20 | public static JToken MergeWith(this JToken context, params JToken[] otherContexts) 21 | { 22 | otherContexts = otherContexts.Where(ctx => ctx != null).ToArray(); 23 | 24 | if (otherContexts.Length == 0) 25 | { 26 | return context; 27 | } 28 | 29 | JToken mergedContext = context is JArray ? context : new JArray(context); 30 | 31 | foreach (var token in SplitTokens(otherContexts)) 32 | { 33 | ((JArray)mergedContext).Add(token); 34 | } 35 | 36 | return mergedContext; 37 | } 38 | 39 | private static IEnumerable SplitTokens(IEnumerable otherContexts) 40 | { 41 | foreach (var context in otherContexts) 42 | { 43 | if (context is JArray) 44 | { 45 | foreach (var inner in SplitTokens(context)) 46 | { 47 | yield return inner; 48 | } 49 | } 50 | else 51 | { 52 | yield return context; 53 | } 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Bindings/SerializingSteps.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Dynamitey; 3 | using FakeItEasy; 4 | using JsonLD.Entities.Tests.Entities; 5 | using Newtonsoft.Json.Linq; 6 | using TechTalk.SpecFlow; 7 | using Xunit; 8 | 9 | namespace JsonLD.Entities.Tests.Bindings 10 | { 11 | [Binding] 12 | public class SerializingSteps 13 | { 14 | private readonly SerializerTestContext context; 15 | 16 | public SerializingSteps(SerializerTestContext context) 17 | { 18 | this.context = context; 19 | A.CallTo(() => context.ContextProvider.GetContext(A.Ignored)).Returns(null); 20 | } 21 | 22 | [Given(@"a person without id")] 23 | public void GivenAPersonWithoutId() 24 | { 25 | this.context.Object = new Person 26 | { 27 | Name = "Tomasz", 28 | Surname = "Pluskiewicz", 29 | BirthDate = new DateTime(1972, 9, 4), 30 | Age = 30 31 | }; 32 | } 33 | 34 | [Given(@"model of type '(.*)'")] 35 | public void GivenModelOfType(string typeName) 36 | { 37 | var model = Type.GetType(typeName, true); 38 | this.context.Object = Activator.CreateInstance(model); 39 | } 40 | 41 | [Given(@"model has interest '(.*)'")] 42 | public void GivenModelInterestsRDF(string value) 43 | { 44 | Dynamic.InvokeMemberAction(this.context.Object, "AddInterest", value); 45 | } 46 | 47 | [When(@"the object is serialized")] 48 | public void WhenTheObjectIsSerialized() 49 | { 50 | this.context.JsonLdObject = this.context.Serializer.Serialize(this.context.Object); 51 | } 52 | 53 | [Then(@"the resulting JSON-LD should be:")] 54 | public void ThenTheResultingJsonLdShouldBe(string jObject) 55 | { 56 | var expected = JObject.Parse(jObject); 57 | Assert.True(JToken.DeepEquals(this.context.JsonLdObject, expected), $"Actual object was: {this.context.JsonLdObject}"); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Converters/JsonLdCollectionConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json; 4 | using NullGuard; 5 | 6 | namespace JsonLD.Entities.Converters 7 | { 8 | /// 9 | /// Converter for JSON-LD collections 10 | /// 11 | /// collection element type 12 | public abstract class JsonLdCollectionConverter : JsonConverter 13 | { 14 | /// 15 | /// Writes the JSON representation of the object. 16 | /// 17 | public override void WriteJson(JsonWriter writer, [AllowNull] object value, JsonSerializer serializer) 18 | { 19 | writer.WriteStartArray(); 20 | foreach (var element in (IEnumerable)value) 21 | { 22 | serializer.Serialize(writer, element); 23 | } 24 | 25 | writer.WriteEndArray(); 26 | } 27 | 28 | /// 29 | /// Reads the JSON representation of the object. 30 | /// 31 | public override object ReadJson(JsonReader reader, Type objectType, [AllowNull] object existingValue, JsonSerializer serializer) 32 | { 33 | if (reader.TokenType == JsonToken.StartArray) 34 | { 35 | return this.CreateReturnedContainer(GetElementsFromArray(reader, serializer)); 36 | } 37 | 38 | var resultObject = serializer.Deserialize(reader); 39 | return this.CreateReturnedContainer(new[] { resultObject }); 40 | } 41 | 42 | /// 43 | /// Wraps element in collection object. 44 | /// 45 | protected abstract object CreateReturnedContainer(IEnumerable elements); 46 | 47 | private static IEnumerable GetElementsFromArray(JsonReader reader, JsonSerializer serializer) 48 | { 49 | reader.Read(); 50 | while (reader.TokenType != JsonToken.EndArray) 51 | { 52 | yield return serializer.Deserialize(reader); 53 | reader.Read(); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/ContextResolverTests.cs: -------------------------------------------------------------------------------- 1 | using FakeItEasy; 2 | using Newtonsoft.Json.Linq; 3 | using Vocab; 4 | using Xunit; 5 | 6 | namespace JsonLD.Entities.Tests 7 | { 8 | public class ContextResolverTests 9 | { 10 | private static readonly JToken TestContextJson; 11 | private readonly ContextResolver resolver; 12 | 13 | static ContextResolverTests() 14 | { 15 | TestContextJson = JToken.Parse(string.Format("{{ 'xsd': '{0}' }}", Xsd.BaseUri)); 16 | } 17 | 18 | public ContextResolverTests() 19 | { 20 | this.resolver = new ContextResolver(A.Fake()); 21 | } 22 | 23 | [Fact] 24 | public void Should_get_context_using_property_from_generic_type() 25 | { 26 | // when 27 | JToken context = this.resolver.GetContext(typeof(GenericType<>)); 28 | 29 | // then 30 | Assert.True(JToken.DeepEquals(context, TestContextJson)); 31 | } 32 | 33 | [Fact] 34 | public void Should_get_context_using_property_from_closed_generic_type() 35 | { 36 | // when 37 | JToken context = this.resolver.GetContext(typeof(GenericType)); 38 | 39 | // then 40 | Assert.True(JToken.DeepEquals(context, TestContextJson)); 41 | } 42 | 43 | [Fact] 44 | public void Should_prefer_method_over_property() 45 | { 46 | // when 47 | JToken context = this.resolver.GetContext(typeof(MethodAndProperty)); 48 | 49 | // then 50 | Assert.True(JToken.Equals(context, (JToken)"method")); 51 | } 52 | 53 | private class MethodAndProperty 54 | { 55 | private static JToken Context 56 | { 57 | get 58 | { 59 | return "property"; 60 | } 61 | } 62 | 63 | private static JToken GetContext(MethodAndProperty _) 64 | { 65 | return "method"; 66 | } 67 | } 68 | 69 | private class GenericType 70 | { 71 | private static JToken Context 72 | { 73 | get 74 | { 75 | return TestContextJson; 76 | } 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Serializing.feature: -------------------------------------------------------------------------------- 1 | Feature: Serializing 2 | Test serializing models to JSON-LD 3 | 4 | Scenario: Serialize simple model with blank id 5 | Given a person without id 6 | When the object is serialized 7 | Then the resulting JSON-LD should be: 8 | """ 9 | { 10 | "name": "Tomasz", 11 | "surname": "Pluskiewicz", 12 | "birthDate": "1972-09-04T00:00:00", 13 | "age": 30 14 | } 15 | """ 16 | 17 | Scenario Outline: Serialize model with single element in set 18 | Given model of type '' 19 | And model has interest 'RDF' 20 | When the object is serialized 21 | Then the resulting JSON-LD should be: 22 | """ 23 | { 24 | "interests": [ "RDF" ] 25 | } 26 | """ 27 | Examples: 28 | | type | 29 | | JsonLD.Entities.Tests.Entities.HasInterestsArray | 30 | | JsonLD.Entities.Tests.Entities.HasInterestsSet | 31 | | JsonLD.Entities.Tests.Entities.HasInterestsCollection | 32 | | JsonLD.Entities.Tests.Entities.HasInterestsEnumerable | 33 | | JsonLD.Entities.Tests.Entities.HasInterestsGenerator | 34 | 35 | Scenario: Serialize model with single element in list 36 | Given model of type 'JsonLD.Entities.Tests.Entities.HasInterestsList' 37 | And model has interest 'RDF' 38 | When the object is serialized 39 | Then the resulting JSON-LD should be: 40 | """ 41 | { 42 | "interests": [ "RDF" ] 43 | } 44 | """ 45 | 46 | Scenario Outline: Serialize model with empty collection 47 | Given model of type '' 48 | When the object is serialized 49 | Then the resulting JSON-LD should be: 50 | """ 51 | { 52 | } 53 | """ 54 | Examples: 55 | | type | 56 | | JsonLD.Entities.Tests.Entities.HasInterestsGenerator | 57 | | JsonLD.Entities.Tests.Entities.HasInterestsSet | 58 | 59 | Scenario: Serialize model with prefixed name in ClassAttribute 60 | Given model of type 'JsonLD.Entities.Tests.Entities.PersonWithPrefixedClass' 61 | And @context is: 62 | """ 63 | { 64 | "ex": "http://example.com/ontology#" 65 | } 66 | """ 67 | When the object is serialized 68 | Then the resulting JSON-LD should be: 69 | """ 70 | { 71 | "@type": "ex:Person" 72 | } 73 | """ -------------------------------------------------------------------------------- /src/JsonLD.Entities/Converters/IriRefConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using NullGuard; 4 | 5 | namespace JsonLD.Entities.Converters 6 | { 7 | /// 8 | /// Converter of 9 | /// 10 | public class IriRefConverter : JsonConverter 11 | { 12 | /// 13 | /// Writes the JSON representing the . 14 | /// 15 | public override void WriteJson(JsonWriter writer, [AllowNull] object value, JsonSerializer serializer) 16 | { 17 | var iriRef = (IriRef)value; 18 | if (iriRef == default(IriRef)) 19 | { 20 | writer.WriteNull(); 21 | } 22 | else 23 | { 24 | writer.WriteStartObject(); 25 | writer.WritePropertyName(JsonLdKeywords.Id); 26 | writer.WriteValue(iriRef.Value); 27 | writer.WriteEndObject(); 28 | } 29 | } 30 | 31 | /// 32 | /// Reads the JSON into the a object. 33 | /// 34 | public override object ReadJson(JsonReader reader, Type objectType, [AllowNull] object existingValue, JsonSerializer serializer) 35 | { 36 | if (reader.TokenType == JsonToken.String) 37 | { 38 | return new IriRef(reader.Value.ToString()); 39 | } 40 | 41 | if (reader.TokenType == JsonToken.StartObject) 42 | { 43 | string iriRef = null; 44 | 45 | while (reader.Read() && reader.TokenType != JsonToken.EndObject) 46 | { 47 | if (reader.TokenType != JsonToken.PropertyName || (string)reader.Value != JsonLdKeywords.Id) 48 | { 49 | continue; 50 | } 51 | 52 | reader.Read(); 53 | iriRef = (string)reader.Value; 54 | } 55 | 56 | return iriRef != null ? new IriRef(iriRef) : default(IriRef); 57 | } 58 | 59 | throw new InvalidOperationException(string.Format("Cannot deserialize token type {0} as IriRef", reader.TokenType)); 60 | } 61 | 62 | /// 63 | /// True if is an 64 | /// 65 | public override bool CanConvert(Type objectType) 66 | { 67 | return objectType == typeof(IriRef); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/TypeExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Serialization; 8 | 9 | namespace JsonLD.Entities 10 | { 11 | /// 12 | /// Useful extensions of 13 | /// 14 | public static class TypeExtension 15 | { 16 | /// 17 | /// Gets the class identifier for an entity type. 18 | /// 19 | public static Uri GetTypeIdentifier(this Type type) 20 | { 21 | var typesProperty = type.GetProperty("Type", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public) ?? 22 | type.GetProperty("Types", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public) ?? 23 | type.GetAnnotatedTypeProperty(); 24 | 25 | if (typesProperty == null) 26 | { 27 | throw new InvalidOperationException($"Type {type} does not statically declare @type"); 28 | } 29 | 30 | var getter = typesProperty.GetGetMethod(true); 31 | dynamic typeValue = getter.Invoke(null, null); 32 | 33 | if (typeValue is IEnumerable && Enumerable.Count(typeValue) == 1) 34 | { 35 | typeValue = Enumerable.Single(typeValue); 36 | } 37 | 38 | if (typeValue is Uri) 39 | { 40 | return typeValue; 41 | } 42 | 43 | if (typeValue is string) 44 | { 45 | return new Uri(typeValue); 46 | } 47 | 48 | throw new InvalidOperationException("Cannot convert value to Uri"); 49 | } 50 | 51 | /// 52 | /// Determines whether the should be compacted after serialization. 53 | /// 54 | internal static bool IsMarkedForCompaction(this Type type) 55 | { 56 | return type.GetTypeInfo().GetCustomAttributes(typeof(SerializeCompactedAttribute), true).Any(); 57 | } 58 | 59 | private static PropertyInfo GetAnnotatedTypeProperty(this Type type) 60 | { 61 | return (from prop in type.GetProperties(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public) 62 | let jsonProperty = prop.GetCustomAttributes(typeof(JsonPropertyAttribute), false).SingleOrDefault() as JsonPropertyAttribute 63 | where jsonProperty != null 64 | where jsonProperty.PropertyName == JsonLdKeywords.Type 65 | select prop).FirstOrDefault(); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /JsonLd.Entities.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.2037 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonLD.Entities", "src\JsonLD.Entities\JsonLD.Entities.csproj", "{CCB7B927-5AD6-4B98-ABA3-92EA18351B7D}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EAFC3ABA-17B5-4557-86A7-545A17506D30}" 9 | ProjectSection(SolutionItems) = preProject 10 | .gitignore = .gitignore 11 | appveyor.yml = appveyor.yml 12 | build.cake = build.cake 13 | build.ps1 = build.ps1 14 | GitVersionConfig.yaml = GitVersionConfig.yaml 15 | paket.dependencies = paket.dependencies 16 | paket.lock = paket.lock 17 | Settings.StyleCop = Settings.StyleCop 18 | EndProjectSection 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonLD.Entities.Tests", "src\JsonLD.Entities.Tests\JsonLD.Entities.Tests.csproj", "{AE56D865-7892-4A1B-B09D-D697E7F6F894}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Documentation", "src\Documentation\Documentation.csproj", "{AF6A37F7-16E9-4DC9-B56C-1667BD88F2AC}" 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {CCB7B927-5AD6-4B98-ABA3-92EA18351B7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {CCB7B927-5AD6-4B98-ABA3-92EA18351B7D}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {CCB7B927-5AD6-4B98-ABA3-92EA18351B7D}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {CCB7B927-5AD6-4B98-ABA3-92EA18351B7D}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {AE56D865-7892-4A1B-B09D-D697E7F6F894}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {AE56D865-7892-4A1B-B09D-D697E7F6F894}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {AE56D865-7892-4A1B-B09D-D697E7F6F894}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {AE56D865-7892-4A1B-B09D-D697E7F6F894}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {AF6A37F7-16E9-4DC9-B56C-1667BD88F2AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {AF6A37F7-16E9-4DC9-B56C-1667BD88F2AC}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {AF6A37F7-16E9-4DC9-B56C-1667BD88F2AC}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {AF6A37F7-16E9-4DC9-B56C-1667BD88F2AC}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | GlobalSection(ExtensibilityGlobals) = postSolution 47 | SolutionGuid = {A94662AA-C848-451B-808C-9519B2558FB5} 48 | EndGlobalSection 49 | EndGlobal 50 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Context/AutoContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace JsonLD.Entities.Context 5 | { 6 | /// 7 | /// Automatic @context based on properties and class identifier 8 | /// 9 | /// Model type 10 | /// 11 | public class AutoContext : AutoContextBase 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | public AutoContext() 17 | : this(typeof(T).GetTypeIdentifier()) 18 | { 19 | } 20 | 21 | /// 22 | /// Initializes a new instance of the class. 23 | /// 24 | /// The current @context. 25 | public AutoContext(JObject context) 26 | : this(context, typeof(T).GetTypeIdentifier()) 27 | { 28 | } 29 | 30 | /// 31 | /// Initializes a new instance of the class. 32 | /// 33 | /// The class identifier. 34 | public AutoContext(Uri classId) 35 | : base(new ClassNameStrategy(classId)) 36 | { 37 | } 38 | 39 | /// 40 | /// Initializes a new instance of the class 41 | /// by extending definitions from 42 | /// 43 | /// The current @context. 44 | /// The class identifier. 45 | public AutoContext(JObject context, Uri classId) 46 | : base(context, new ClassNameStrategy(classId)) 47 | { 48 | } 49 | 50 | private class ClassNameStrategy : AutoContextStrategy 51 | { 52 | private const string SlashClassIdAppendFormat = "{0}#{1}"; 53 | private const string HashClassIdAppendFormat = "{0}/{1}"; 54 | private readonly Uri classId; 55 | 56 | public ClassNameStrategy(Uri classId) 57 | { 58 | this.classId = classId; 59 | } 60 | 61 | protected override string GetPropertyId(string propertyName) 62 | { 63 | var format = HashClassIdAppendFormat; 64 | 65 | if (string.IsNullOrWhiteSpace(this.classId.Fragment)) 66 | { 67 | format = SlashClassIdAppendFormat; 68 | } 69 | 70 | return string.Format(format, this.classId, propertyName); 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/JsonLD.Entities.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JsonLD.Entities.Tests 5 | JsonLD.Entities.Tests 6 | net462 7 | false 8 | false 9 | false 10 | false 11 | false 12 | false 13 | false 14 | false 15 | false 16 | true 17 | full 18 | true 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | True 29 | True 30 | DeserializingJsonLD.feature 31 | 32 | 33 | True 34 | True 35 | DeserializingRDF.feature 36 | 37 | 38 | True 39 | True 40 | Serializing.feature 41 | 42 | 43 | 44 | 45 | SpecFlowSingleFileGenerator 46 | DeserializingJsonLD.feature.cs 47 | 48 | 49 | SpecFlowSingleFileGenerator 50 | DeserializingRDF.feature.cs 51 | 52 | 53 | SpecFlowSingleFileGenerator 54 | Serializing.feature.cs 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/IriRefTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace JsonLD.Entities.Tests 5 | { 6 | public class IriRefTests 7 | { 8 | private const string TestUri = "http://example.com/relative/path"; 9 | 10 | [Fact] 11 | public void Should_work_with_relative_Uri_string() 12 | { 13 | // given 14 | const string path = "/relative/path"; 15 | 16 | // when 17 | var iriRef = new IriRef(path); 18 | 19 | // then 20 | Assert.Equal(new Uri(path, UriKind.Relative).ToString(), iriRef.Value); 21 | } 22 | 23 | [Fact] 24 | public void Should_work_with_absolute_Uri_string() 25 | { 26 | // given 27 | const string path = TestUri; 28 | 29 | // when 30 | var iriRef = new IriRef(path); 31 | 32 | // then 33 | Assert.Equal(new Uri(path, UriKind.Absolute).ToString(), iriRef.Value); 34 | } 35 | 36 | [Fact] 37 | public void Two_instances_should_be_equal_when_Uri_is_equal() 38 | { 39 | // given 40 | const string path = TestUri; 41 | 42 | // then 43 | Assert.True(new IriRef(path) == new IriRef(path)); 44 | Assert.True(new IriRef(path).Equals(new IriRef(path))); 45 | } 46 | 47 | [Fact] 48 | public void Should_be_explicitly_castable_from_Uri() 49 | { 50 | // given 51 | var expected = new IriRef(TestUri); 52 | 53 | // when 54 | IriRef iriRef = (IriRef)new Uri(TestUri); 55 | 56 | // then 57 | Assert.Equal(expected, iriRef); 58 | } 59 | 60 | [Fact] 61 | public void Should_be_explicitly_castable_from_string() 62 | { 63 | // given 64 | var expected = new IriRef(TestUri); 65 | 66 | // when 67 | var iriRef = (IriRef)TestUri; 68 | 69 | // then 70 | Assert.Equal(expected, iriRef); 71 | } 72 | 73 | [Fact] 74 | public void Should_be_explicitly_castable_from_null_string() 75 | { 76 | // given 77 | var expected = default(IriRef); 78 | 79 | // when 80 | var iriRef = (IriRef)(string)null; 81 | 82 | // then 83 | Assert.Equal(expected, iriRef); 84 | } 85 | 86 | [Fact] 87 | public void Should_be_explicitly_castable_from_null_Uri() 88 | { 89 | // given 90 | var expected = default(IriRef); 91 | 92 | // when 93 | var iriRef = (IriRef)(Uri)null; 94 | 95 | // then 96 | Assert.Equal(expected, iriRef); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Converters/JsonLdLiteralConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Serialization; 4 | using NullGuard; 5 | 6 | namespace JsonLD.Entities.Converters 7 | { 8 | /// 9 | /// pending doc 10 | /// 11 | public class JsonLdLiteralConverter : JsonConverter 12 | { 13 | private static readonly JsonLdSerializer LiteralSerializer; 14 | 15 | /// 16 | /// Initializes static members of the class. 17 | /// 18 | static JsonLdLiteralConverter() 19 | { 20 | LiteralSerializer = new JsonLdSerializer 21 | { 22 | ContractResolver = new CamelCasePropertyNamesContractResolver() 23 | }; 24 | } 25 | 26 | /// 27 | /// Writes the JSON representation of the object. 28 | /// 29 | public override void WriteJson(JsonWriter writer, [AllowNull] object value, JsonSerializer serializer) 30 | { 31 | writer.WriteValue(value); 32 | } 33 | 34 | /// 35 | /// Reads the JSON representation of the object. 36 | /// 37 | [return: AllowNull] 38 | public sealed override object ReadJson( 39 | JsonReader reader, 40 | Type objectType, 41 | [AllowNull] object existingValue, 42 | JsonSerializer serializer) 43 | { 44 | if (reader.TokenType != JsonToken.StartObject) 45 | { 46 | return this.DeserializeLiteral(reader, objectType, serializer); 47 | } 48 | 49 | object value = null; 50 | while (reader.TokenType != JsonToken.EndObject) 51 | { 52 | reader.Read(); 53 | 54 | if (reader.TokenType == JsonToken.PropertyName && Equals(reader.Value, "@value")) 55 | { 56 | reader.Read(); 57 | value = this.DeserializeLiteral(reader, objectType, serializer); 58 | } 59 | else 60 | { 61 | reader.Skip(); 62 | } 63 | } 64 | 65 | return value; 66 | } 67 | 68 | /// 69 | /// Determines whether this instance can convert the specified object type. 70 | /// 71 | public override bool CanConvert(Type objectType) 72 | { 73 | return true; 74 | } 75 | 76 | /// 77 | /// When implemented in derived classes can be used to customize deserialization logic for literal value 78 | /// 79 | protected virtual object DeserializeLiteral(JsonReader reader, Type objectType, JsonSerializer serializer) 80 | { 81 | return LiteralSerializer.Deserialize(reader, objectType); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Documentation/Serializing/EntityTypes/Readme.cs: -------------------------------------------------------------------------------- 1 | /** 2 | # Documentation 3 | 4 | ## Serializing typed entities 5 | 6 | In JSON-LD (and RDF in general), types of an object are declared with a property like any other, called rdf:type. In JSON-LD though it is 7 | represented as a special `@type` property. Below examples show how to defines types and have them serialized to JSON. 8 | 9 | First let's import the required namespaces. 10 | **/ 11 | 12 | using System; 13 | using System.Collections.Generic; 14 | using JsonLD.Entities; 15 | using Newtonsoft.Json; 16 | using Newtonsoft.Json.Linq; 17 | using Xunit; 18 | 19 | public class SerializingTypedEntities 20 | { 21 | 22 | /** 23 | ### Declaring types explicitly 24 | 25 | Easiest way to define an instance's `@type`s is to declare a `Types` or `Type` property. It can also be private, protected or static, but then 26 | `[JsonProperty]` attribue must be used so that it's serialized. Similarily to the `Id` property, the JSON property will be prefixed by the 27 | `@` character by convention. 28 | 29 | The property can return any type or collection thereof, provided that the serialized value is a string. Thus any custom converter could also 30 | be used. 31 | 32 | It is also be possible to use any other property and give it a proper name by setting the attribute like `[JsonProperty(JsonLdKeywords.Type)]` 33 | **/ 34 | 35 | public class TypesAsSingleUri 36 | { 37 | public Uri Type 38 | { 39 | get { return new Uri("http://schema.org/Person"); } 40 | } 41 | } 42 | 43 | public class TypesPropertyStatic 44 | { 45 | [JsonProperty(JsonLdKeywords.Type)] 46 | public static Uri Type 47 | { 48 | get { return new Uri("http://schema.org/Person"); } 49 | } 50 | } 51 | 52 | public class TypesAsStringCollection 53 | { 54 | public IEnumerable Types 55 | { 56 | get { yield return "http://schema.org/Person"; } 57 | } 58 | } 59 | 60 | public class TypesAsPrivatePropertyWithCustomName 61 | { 62 | [JsonProperty(JsonLdKeywords.Type)] 63 | private IEnumerable Classes 64 | { 65 | get { yield return new Uri("http://schema.org/Person"); } 66 | } 67 | } 68 | 69 | [Theory] 70 | [InlineData(typeof(TypesAsSingleUri), "{ '@type': 'http://schema.org/Person' }")] 71 | [InlineData(typeof(TypesPropertyStatic), "{ '@type': 'http://schema.org/Person' }")] 72 | [InlineData(typeof(TypesAsStringCollection), "{ '@type': [ 'http://schema.org/Person' ] }")] 73 | [InlineData(typeof(TypesAsPrivatePropertyWithCustomName), "{ '@type': [ 'http://schema.org/Person' ] }")] 74 | public void SerializesTypesPropertyAsAtTypes(Type type, string expectedJson) 75 | { 76 | // given 77 | var expected = JObject.Parse(expectedJson); 78 | var entity = Activator.CreateInstance(type); 79 | var serializer = new EntitySerializer(); 80 | 81 | // when 82 | var json = serializer.Serialize(entity); 83 | 84 | // then 85 | Assert.True(JToken.DeepEquals(json, expected), $"Actual object was {json}"); 86 | } 87 | } -------------------------------------------------------------------------------- /src/Documentation/Serializing/EntityTypes/Readme.mkd: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | ## Serializing typed entities 4 | 5 | In JSON-LD (and RDF in general), types of an object are declared with a property like any other, called rdf:type. In JSON-LD though it is 6 | represented as a special `@type` property. Below examples show how to defines types and have them serialized to JSON. 7 | 8 | First let's import the required namespaces. 9 | 10 | 11 | ``` c# 12 | using System; 13 | using System.Collections.Generic; 14 | using JsonLD.Entities; 15 | using Newtonsoft.Json; 16 | using Newtonsoft.Json.Linq; 17 | using Xunit; 18 | 19 | public class SerializingTypedEntities 20 | { 21 | ``` 22 | 23 | ### Declaring types explicitly 24 | 25 | Easiest way to define an instance's `@type`s is to declare a `Types` or `Type` property. It can also be private, protected or static, but then 26 | `[JsonProperty]` attribue must be used so that it's serialized. Similarily to the `Id` property, the JSON property will be prefixed by the 27 | `@` character by convention. 28 | 29 | The property can return any type or collection thereof, provided that the serialized value is a string. Thus any custom converter could also 30 | be used. 31 | 32 | It is also be possible to use any other property and give it a proper name by setting the attribute like `[JsonProperty(JsonLdKeywords.Type)]` 33 | 34 | ``` c# 35 | public class TypesAsSingleUri 36 | { 37 | public Uri Type 38 | { 39 | get { return new Uri("http://schema.org/Person"); } 40 | } 41 | } 42 | 43 | public class TypesPropertyStatic 44 | { 45 | [JsonProperty(JsonLdKeywords.Type)] 46 | public static Uri Type 47 | { 48 | get { return new Uri("http://schema.org/Person"); } 49 | } 50 | } 51 | 52 | public class TypesAsStringCollection 53 | { 54 | public IEnumerable Types 55 | { 56 | get { yield return "http://schema.org/Person"; } 57 | } 58 | } 59 | 60 | public class TypesAsPrivatePropertyWithCustomName 61 | { 62 | [JsonProperty(JsonLdKeywords.Type)] 63 | private IEnumerable Classes 64 | { 65 | get { yield return new Uri("http://schema.org/Person"); } 66 | } 67 | } 68 | 69 | [Theory] 70 | [InlineData(typeof(TypesAsSingleUri), "{ '@type': 'http://schema.org/Person' }")] 71 | [InlineData(typeof(TypesPropertyStatic), "{ '@type': 'http://schema.org/Person' }")] 72 | [InlineData(typeof(TypesAsStringCollection), "{ '@type': [ 'http://schema.org/Person' ] }")] 73 | [InlineData(typeof(TypesAsPrivatePropertyWithCustomName), "{ '@type': [ 'http://schema.org/Person' ] }")] 74 | public void SerializesTypesPropertyAsAtTypes(Type type, string expectedJson) 75 | { 76 | // given 77 | var expected = JObject.Parse(expectedJson); 78 | var entity = Activator.CreateInstance(type); 79 | var serializer = new EntitySerializer(); 80 | 81 | // when 82 | var json = serializer.Serialize(entity); 83 | 84 | // then 85 | Assert.True(JToken.DeepEquals(json, expected), $"Actual object was {json}"); 86 | } 87 | } 88 | ``` 89 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Bindings/EntityVerificationSteps.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Globalization; 4 | using System.Linq; 5 | using Dynamitey; 6 | using TechTalk.SpecFlow; 7 | using Xunit; 8 | 9 | namespace JsonLD.Entities.Tests.Bindings 10 | { 11 | [Binding] 12 | public class EntityVerificationSteps 13 | { 14 | private readonly ScenarioContext scenarioContext; 15 | 16 | public EntityVerificationSteps(ScenarioContext scenarioContext) 17 | { 18 | this.scenarioContext = scenarioContext; 19 | } 20 | 21 | private object Entity 22 | { 23 | get { return scenarioContext["Entity"]; } 24 | } 25 | 26 | [Then(@"object should have property '(.*)' equal to '(.*)'")] 27 | public void ThenObjectShouldHavePropertyEqualTo(string propertyName, string expectedValue) 28 | { 29 | var actualValue = Dynamic.InvokeGet(Entity, propertyName); 30 | 31 | Assert.Equal(expectedValue, actualValue); 32 | } 33 | 34 | [Then(@"object should have DateTime property '(.*)' equal to '(\d\d-\d\d-\d\d\d\d)'")] 35 | public void ThenObjectShouldHaveDateTimePropertyEqualTo(string propertyName, string expectedDateString) 36 | { 37 | var actualValue = Dynamic.InvokeGet(Entity, propertyName); 38 | var expectedValue = DateTime.ParseExact(expectedDateString, "dd-MM-yyyy", CultureInfo.InvariantCulture); 39 | 40 | Assert.Equal(expectedValue, actualValue); 41 | } 42 | 43 | [Then(@"object should have property '(.*)' containg string '(.*)'")] 44 | public void ThenObjectShouldHavePropertyContaingString(string propName, string expectedValue) 45 | { 46 | IEnumerable collection = Dynamic.InvokeGet(Entity, propName); 47 | 48 | Assert.Contains(expectedValue, collection.Cast()); 49 | } 50 | 51 | [Then(@"object should have empty property '(.*)'")] 52 | public void ThenObjectShouldHaveEmptyProperty(string propName) 53 | { 54 | IEnumerable collection = Dynamic.InvokeGet(Entity, propName); 55 | 56 | Assert.Empty(collection); 57 | } 58 | 59 | [Then(@"object should have object property '(.*)'")] 60 | public void ThenObjectShouldHaveObjectProperty(string propertyName) 61 | { 62 | var actualValue = Dynamic.InvokeGet(Entity, propertyName); 63 | Assert.NotNull(actualValue); 64 | scenarioContext.Set(actualValue, propertyName); 65 | } 66 | 67 | [Then(@"object '(.*)' should have property '(.*)' equal to '(.*)'")] 68 | public void ThenObjectShouldHavePropertyEqualTo(string objectName, string propertyName, string expectedValue) 69 | { 70 | var obj = scenarioContext[objectName]; 71 | var actualValue = Dynamic.InvokeGet(obj, propertyName); 72 | 73 | Assert.Equal(expectedValue, actualValue); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/DeserializingRDF.feature: -------------------------------------------------------------------------------- 1 | Feature: Deserializing RDF data into objects 2 | 3 | @NQuads 4 | Scenario: Deserialize simple resource entity 5 | Given NQuads: 6 | """ 7 | . 8 | "Tomasz" . 9 | "Pluskiewicz" . 10 | "1975-08-15" . 11 | """ 12 | And @context is: 13 | """ 14 | { 15 | foaf: "http://xmlns.com/foaf/0.1/", 16 | name: "foaf:givenName", 17 | surname: "foaf:familyName", 18 | birthDate: "http://example.com/ontology#dateOfBirth" 19 | } 20 | """ 21 | When I deserialize into 'JsonLD.Entities.Tests.Entities.Person' 22 | Then object should have property 'Name' equal to 'Tomasz' 23 | And object should have property 'Surname' equal to 'Pluskiewicz' 24 | And object should have DateTime property 'BirthDate' equal to '15-08-1975' 25 | 26 | @NQuads 27 | Scenario Outline: Deserialize single element list into collection 28 | Given NQuads: 29 | """ 30 | _:list . 31 | _:list "RDF" . 32 | _:list . 33 | """ 34 | And @context is: 35 | """ 36 | { 37 | "foaf": "http://xmlns.com/foaf/0.1/", 38 | "interests": { "@id": "foaf:topic_interest", "@container": "@list" } 39 | } 40 | """ 41 | When I deserialize into '' 42 | Then object should have property 'Interests' containg string 'RDF' 43 | Examples: 44 | | type | 45 | | JsonLD.Entities.Tests.Entities.HasInterestsList | 46 | 47 | @NQuads 48 | Scenario Outline: Deserialize single element into collection 49 | Given NQuads: 50 | """ 51 | "RDF" . 52 | """ 53 | And @context is: 54 | """ 55 | { 56 | "foaf": "http://xmlns.com/foaf/0.1/", 57 | "interests": { "@id": "foaf:topic_interest", "@container": "@set" } 58 | } 59 | """ 60 | When I deserialize into '' 61 | Then object should have property 'Interests' containg string 'RDF' 62 | Examples: 63 | | type | 64 | | JsonLD.Entities.Tests.Entities.HasInterestsArray | 65 | | JsonLD.Entities.Tests.Entities.HasInterestsEnumerable | 66 | | JsonLD.Entities.Tests.Entities.HasInterestsCollection | 67 | | JsonLD.Entities.Tests.Entities.HasInterestsSet | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Context/PropertyExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | using System.Reflection; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Linq; 7 | using NullGuard; 8 | 9 | namespace JsonLD.Entities.Context 10 | { 11 | /// 12 | /// JSON-LD extensions to 13 | /// 14 | [NullGuard(ValidationFlags.All)] 15 | public static class PropertyExtensions 16 | { 17 | private static readonly JsonLdContractResolver ContractResolver = new JsonLdContractResolver(); 18 | 19 | /// 20 | /// Gets the property from LINQ expression. 21 | /// 22 | /// target type 23 | /// The property return type. 24 | /// The property expression. 25 | /// Parameter must be a property access expression 26 | public static PropertyInfo GetProperty(this Expression> propertyExpression) 27 | { 28 | if (!(propertyExpression.Body is MemberExpression)) 29 | { 30 | throw new ArgumentException("Parameter must be a property access expression", nameof(propertyExpression)); 31 | } 32 | 33 | var memberExpression = (MemberExpression)propertyExpression.Body; 34 | return (PropertyInfo)memberExpression.Member; 35 | } 36 | 37 | /// 38 | /// Gets the name of the JSON key the 39 | /// will be serialized to. 40 | /// 41 | public static string GetJsonPropertyName(this PropertyInfo property) 42 | { 43 | var jsonProperty = property.GetCustomAttributes(typeof(JsonPropertyAttribute), true) 44 | .Cast().SingleOrDefault(); 45 | if (jsonProperty != null && jsonProperty.PropertyName != null) 46 | { 47 | return jsonProperty.PropertyName; 48 | } 49 | 50 | return ContractResolver.GetResolvedPropertyName(property.Name); 51 | } 52 | 53 | /// 54 | /// Ensures the property is an expanded definition. 55 | /// 56 | internal static JProperty EnsureExpandedDefinition(this JProperty property) 57 | { 58 | if (property.Value is JObject) 59 | { 60 | return property; 61 | } 62 | 63 | return new JProperty(property.Name, new JObject(new JProperty(JsonLdKeywords.Id, property.Value))); 64 | } 65 | 66 | /// 67 | /// Appends a property to the property definition 68 | /// 69 | internal static JProperty With(this JProperty property, string name, [AllowNull] string value) 70 | { 71 | property.Value[name] = value; 72 | return property; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/IriRef.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using JsonLD.Entities.Converters; 4 | using Newtonsoft.Json; 5 | using NullGuard; 6 | 7 | namespace JsonLD.Entities 8 | { 9 | /// 10 | /// Represents a Uri reference, which will force an expanded form 11 | /// when JSON-LD is serialized 12 | /// 13 | [SuppressMessage("StyleCop.CSharp.DocumentationRules", "*", Justification = "Ignore equality memebers")] 14 | [JsonConverter(typeof(IriRefConverter))] 15 | public struct IriRef 16 | { 17 | /// 18 | /// Initializes a new instance of the struct. 19 | /// 20 | /// The URI. 21 | public IriRef(string value) 22 | { 23 | this.Value = value; 24 | } 25 | 26 | /// 27 | /// Initializes a new instance of the struct. 28 | /// 29 | /// The identifier. 30 | public IriRef(Uri uri) 31 | : this(uri.ToString()) 32 | { 33 | } 34 | 35 | /// 36 | /// Gets the URI. 37 | /// 38 | [JsonProperty(JsonLdKeywords.Id)] 39 | public string Value { [return: AllowNull] get; } 40 | 41 | #pragma warning disable SA1600 // Elements must be documented 42 | #pragma warning disable 1591 43 | public static explicit operator IriRef([AllowNull] Uri uri) 44 | { 45 | if (uri == null) 46 | { 47 | return default(IriRef); 48 | } 49 | 50 | return new IriRef(uri); 51 | } 52 | 53 | public static explicit operator IriRef([AllowNull] string uriString) 54 | { 55 | if (uriString == null) 56 | { 57 | return default(IriRef); 58 | } 59 | 60 | return new IriRef(uriString); 61 | } 62 | 63 | public static bool operator ==(IriRef left, IriRef right) 64 | { 65 | return left.Equals(right); 66 | } 67 | 68 | public static bool operator !=(IriRef left, IriRef right) 69 | { 70 | return !left.Equals(right); 71 | } 72 | 73 | public bool Equals(IriRef other) 74 | { 75 | return string.Equals(this.Value, other.Value); 76 | } 77 | 78 | public override bool Equals([AllowNull] object obj) 79 | { 80 | if (ReferenceEquals(null, obj)) 81 | { 82 | return false; 83 | } 84 | 85 | return obj is IriRef && this.Equals((IriRef)obj); 86 | } 87 | 88 | public override int GetHashCode() 89 | { 90 | return this.Value?.GetHashCode() ?? 0; 91 | } 92 | 93 | public override string ToString() 94 | { 95 | if (this.Value != null) 96 | { 97 | return this.Value; 98 | } 99 | 100 | return string.Empty; 101 | } 102 | #pragma warning restore SA1600 // Elements must be documented 103 | #pragma warning restore 1591 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/JsonLdKeywords.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NullGuard; 3 | 4 | namespace JsonLD.Entities 5 | { 6 | /// 7 | /// Contains JSON-LD special property names 8 | /// 9 | [NullGuard(ValidationFlags.All)] 10 | public static class JsonLdKeywords 11 | { 12 | /// 13 | /// @id keyword 14 | /// 15 | public const string Id = "@id"; 16 | 17 | /// 18 | /// @type keyword 19 | /// 20 | public const string Type = "@type"; 21 | 22 | /// 23 | /// @base keyword 24 | /// 25 | public const string Base = "@base"; 26 | 27 | /// 28 | /// @vocab keyword 29 | /// 30 | public const string Vocab = "@vocab"; 31 | 32 | /// 33 | /// @context keyword 34 | /// 35 | public const string Context = "@context"; 36 | 37 | /// 38 | /// @language keyword 39 | /// 40 | public const string Language = "@language"; 41 | 42 | /// 43 | /// @container keyword 44 | /// 45 | public const string Container = "@container"; 46 | 47 | /// 48 | /// @list keyword 49 | /// 50 | public const string List = "@list"; 51 | 52 | /// 53 | /// @set keyword 54 | /// 55 | public const string Set = "@set"; 56 | 57 | /// 58 | /// @index keyword 59 | /// 60 | public const string Index = "@index"; 61 | 62 | private static readonly IDictionary KnownPropertyNames = new Dictionary(); 63 | 64 | static JsonLdKeywords() 65 | { 66 | KnownPropertyNames.Add("Id", Id); 67 | KnownPropertyNames.Add("Type", Type); 68 | KnownPropertyNames.Add("Types", Type); 69 | KnownPropertyNames.Add("Context", Context); 70 | } 71 | 72 | /// 73 | /// Determines whether the specified value is a JSON-LD keyword. 74 | /// 75 | public static bool IsKeyword(string value) 76 | { 77 | return value == Id || 78 | value == Type || 79 | value == Base || 80 | value == Vocab || 81 | value == Context || 82 | value == Container || 83 | value == List || 84 | value == Set || 85 | value == Index; 86 | } 87 | 88 | /// 89 | /// Gets the keyword for a C# property. 90 | /// 91 | /// Name of the property. 92 | /// a JSON-LD keyword or null 93 | [return: AllowNull] 94 | internal static string GetKeywordForProperty(string propertyName) 95 | { 96 | KnownPropertyNames.TryGetValue(propertyName, out propertyName); 97 | 98 | return propertyName; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Documentation/CreatingContext/FluentContext/Readme.cs: -------------------------------------------------------------------------------- 1 | /** 2 | # Documentation 3 | 4 | ## Building the `@context` programmatically 5 | 6 | The most reasonable way for creating the `@context` is to produce a `JObject` (or `JArray`) with the desired structure programmatically. 7 | This way it would also be possible to reuse and modify common contexts shared by hierarchies of classes. To simplify the repetitive chore 8 | of creating complex objects and introduce some semantics into the code, JsonLd.Entities introduces a number of helper methods, which can be 9 | used to create parts of the `@context` strucutre. They make it simpler to use advanced features like [type coercion][coercion], 10 | [internationalization][i8n] or [@reverse][reverse]. 11 | 12 | First let's start with the usual preamble. 13 | **/ 14 | 15 | using System; 16 | using JsonLD.Entities.Context; 17 | using Newtonsoft.Json.Linq; 18 | using Xunit; 19 | 20 | public class FluentSyntaxForBuildingContext 21 | { 22 | 23 | /** 24 | ### Define `@context` inline with class definition 25 | **/ 26 | 27 | [Fact] 28 | public void BuildComplexContextSimply() 29 | { 30 | // given 31 | const string expected = @" 32 | { 33 | '@base': 'http://example.com/', 34 | '@vocab': 'http://schema.org/', 35 | 'dcterms': 'http://purl.org/dc/terms/', 36 | 'xsd': 'http://www.w3.org/2001/XMLSchema#', 37 | 'title': 'dcterms:title', 38 | 'creator': { 39 | '@id': 'dcterms:creator', 40 | '@type': '@id' 41 | }, 42 | 'medium': { 43 | '@id': 'dcterms:medium', 44 | '@container': '@set', 45 | '@type': '@vocab' 46 | }, 47 | 'date': { 48 | '@id': 'dcterms:date', 49 | '@type': 'xsd:date' 50 | }, 51 | '@language': 'en', 52 | 'label': { 53 | '@id': 'http://www.w3.org/2004/02/skos/core#prefLabel', 54 | '@language': null 55 | }, 56 | 'altLabel': { 57 | '@id': 'http://www.w3.org/2004/02/skos/core#altLabel', 58 | '@container': '@language', 59 | '@type': 'xsd:string' 60 | } 61 | }"; 62 | 63 | // when 64 | var context = new JObject( 65 | Base.Is("http://example.com/"), 66 | Vocab.Is(new Uri("http://schema.org/")), 67 | "dcterms".IsPrefixOf("http://purl.org/dc/terms/"), 68 | "xsd".IsPrefixOf(new Uri("http://www.w3.org/2001/XMLSchema#")), 69 | "title".IsProperty("dcterms:title"), 70 | "creator".IsProperty("dcterms:creator") 71 | .Type().Id(), 72 | "medium".IsProperty("dcterms:medium") 73 | .Container().Set() 74 | .Type().Vocab(), 75 | "date".IsProperty("dcterms:date") 76 | .Type().Is("xsd:date"), 77 | "en".IsLanguage(), 78 | "label".IsProperty("http://www.w3.org/2004/02/skos/core#prefLabel") 79 | .Language(null), 80 | "altLabel".IsProperty("http://www.w3.org/2004/02/skos/core#altLabel") 81 | .Container().Language() 82 | .Type().Is("xsd:string")); 83 | 84 | // then 85 | context = JObject.Parse(context.ToString()); // DeepEqual fails otherwise 86 | Assert.True(JToken.DeepEquals(context, JObject.Parse(expected)), $"Actual context was {context}"); 87 | } 88 | } 89 | 90 | /** 91 | [coercion]: http://www.w3.org/TR/json-ld/#type-coercion 92 | [reverse]: http://www.w3.org/TR/json-ld/#reverse-properties 93 | [i8n]: http://www.w3.org/TR/json-ld/#string-internationalization 94 | **/ -------------------------------------------------------------------------------- /src/Documentation/CreatingContext/FluentContext/Readme.mkd: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | ## Building the `@context` programmatically 4 | 5 | The most reasonable way for creating the `@context` is to produce a `JObject` (or `JArray`) with the desired structure programmatically. 6 | This way it would also be possible to reuse and modify common contexts shared by hierarchies of classes. To simplify the repetitive chore 7 | of creating complex objects and introduce some semantics into the code, JsonLd.Entities introduces a number of helper methods, which can be 8 | used to create parts of the `@context` strucutre. They make it simpler to use advanced features like [type coercion][coercion], 9 | [internationalization][i8n] or [@reverse][reverse]. 10 | 11 | First let's start with the usual preamble. 12 | 13 | 14 | ``` c# 15 | using System; 16 | using JsonLD.Entities.Context; 17 | using Newtonsoft.Json.Linq; 18 | using Xunit; 19 | 20 | public class FluentSyntaxForBuildingContext 21 | { 22 | ``` 23 | 24 | ### Define `@context` inline with class definition 25 | 26 | ``` c# 27 | [Fact] 28 | public void BuildComplexContextSimply() 29 | { 30 | // given 31 | const string expected = @" 32 | { 33 | '@base': 'http://example.com/', 34 | '@vocab': 'http://schema.org/', 35 | 'dcterms': 'http://purl.org/dc/terms/', 36 | 'xsd': 'http://www.w3.org/2001/XMLSchema#', 37 | 'title': 'dcterms:title', 38 | 'creator': { 39 | '@id': 'dcterms:creator', 40 | '@type': '@id' 41 | }, 42 | 'medium': { 43 | '@id': 'dcterms:medium', 44 | '@container': '@set', 45 | '@type': '@vocab' 46 | }, 47 | 'date': { 48 | '@id': 'dcterms:date', 49 | '@type': 'xsd:date' 50 | }, 51 | '@language': 'en', 52 | 'label': { 53 | '@id': 'http://www.w3.org/2004/02/skos/core#prefLabel', 54 | '@language': null 55 | }, 56 | 'altLabel': { 57 | '@id': 'http://www.w3.org/2004/02/skos/core#altLabel', 58 | '@container': '@language', 59 | '@type': 'xsd:string' 60 | } 61 | }"; 62 | 63 | // when 64 | var context = new JObject( 65 | Base.Is("http://example.com/"), 66 | Vocab.Is(new Uri("http://schema.org/")), 67 | "dcterms".IsPrefixOf("http://purl.org/dc/terms/"), 68 | "xsd".IsPrefixOf(new Uri("http://www.w3.org/2001/XMLSchema#")), 69 | "title".IsProperty("dcterms:title"), 70 | "creator".IsProperty("dcterms:creator") 71 | .Type().Id(), 72 | "medium".IsProperty("dcterms:medium") 73 | .Container().Set() 74 | .Type().Vocab(), 75 | "date".IsProperty("dcterms:date") 76 | .Type().Is("xsd:date"), 77 | "en".IsLanguage(), 78 | "label".IsProperty("http://www.w3.org/2004/02/skos/core#prefLabel") 79 | .Language(null), 80 | "altLabel".IsProperty("http://www.w3.org/2004/02/skos/core#altLabel") 81 | .Container().Language() 82 | .Type().Is("xsd:string")); 83 | 84 | // then 85 | context = JObject.Parse(context.ToString()); // DeepEqual fails otherwise 86 | Assert.True(JToken.DeepEquals(context, JObject.Parse(expected)), $"Actual context was {context}"); 87 | } 88 | } 89 | ``` 90 | 91 | [coercion]: http://www.w3.org/TR/json-ld/#type-coercion 92 | [reverse]: http://www.w3.org/TR/json-ld/#reverse-properties 93 | [i8n]: http://www.w3.org/TR/json-ld/#string-internationalization 94 | -------------------------------------------------------------------------------- /src/Documentation/ResolvingContext/Readme.cs: -------------------------------------------------------------------------------- 1 | /** 2 | # Documentation 3 | 4 | ## Defining entities's @context and @frame 5 | 6 | To correctly serialize and deserialize JSON-LD documents into given models it may be necessary to process them with a `@context` or `@frame` 7 | so that it's structure conforms to a given schema. This page lists the ways in which it is possible to define `@context` and `@frame` for 8 | the C# types. 9 | 10 | First let's import the required namespaces. 11 | **/ 12 | 13 | using System; 14 | using JsonLD.Entities; 15 | using Newtonsoft.Json.Linq; 16 | using Xunit; 17 | 18 | /** 19 | ### Define `@context` inline with class definition 20 | **/ 21 | 22 | public class DefiningContextInline 23 | { 24 | 25 | private const string TestContext = "{ '@base': 'http://example.com/' }"; 26 | 27 | /** 28 | The simples way is to define a `Context` property. It must be static and return a string or `JToken`. It can also be private. 29 | 30 | Below are example entity types with various ways of defining the `@context` as property. 31 | **/ 32 | 33 | public class ContextInlineProperty 34 | { 35 | public static JObject Context 36 | { 37 | get { return JObject.Parse(TestContext); } 38 | } 39 | } 40 | 41 | public class ContextInlinePrivateProperty 42 | { 43 | private static JObject Context 44 | { 45 | get { return JObject.Parse(TestContext); } 46 | } 47 | } 48 | 49 | public class ContextInlineStaticStringProperty 50 | { 51 | public static string Context 52 | { 53 | get { return TestContext; } 54 | } 55 | } 56 | 57 | /** 58 | When each of the above will be serailized, the inline context will be used. 59 | **/ 60 | 61 | [Theory] 62 | [InlineData(typeof(ContextInlineProperty))] 63 | [InlineData(typeof(ContextInlinePrivateProperty))] 64 | [InlineData(typeof(ContextInlineStaticStringProperty))] 65 | public void UsesInlineContextPropertyWhenSerializing(Type entityType) 66 | { 67 | // given 68 | const string expected = "{ '@context': { '@base': 'http://example.com/' } }"; 69 | var entity = Activator.CreateInstance(entityType); 70 | var serializer = new EntitySerializer(); 71 | 72 | // when 73 | var json = serializer.Serialize(entity); 74 | 75 | // then 76 | Assert.True(JToken.DeepEquals(json, JObject.Parse(expected)), $"Actual object is {json}"); 77 | } 78 | 79 | /** 80 | Finally, the type can declare a static `GetContext` method, which takes an object of said type as parameter. 81 | This way a `@context` can be built dynamically. 82 | 83 | Note that when both property and method is present, the mthod will be preferred. 84 | **/ 85 | 86 | public class ContextInlineMethod 87 | { 88 | private readonly string _base; 89 | 90 | public ContextInlineMethod(string @base) 91 | { 92 | _base = @base; 93 | } 94 | 95 | private static JObject GetContext(ContextInlineMethod instance) 96 | { 97 | return JObject.Parse($@"{{ 98 | '@base': 'http://example.com/{instance._base}/' 99 | }}"); 100 | } 101 | } 102 | 103 | [Fact] 104 | public void UsesGetContextMethodSerializing() 105 | { 106 | // given 107 | const string expected = "{ '@context': { '@base': 'http://example.com/test/' } }"; 108 | var entity = new ContextInlineMethod("test"); 109 | var serializer = new EntitySerializer(); 110 | 111 | // when 112 | var json = serializer.Serialize(entity); 113 | 114 | // then 115 | Assert.True(JToken.DeepEquals(json, JObject.Parse(expected)), $"Actual object is {json}"); 116 | } 117 | } -------------------------------------------------------------------------------- /src/Documentation/ResolvingContext/Readme.mkd: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | ## Defining entities's @context and @frame 4 | 5 | To correctly serialize and deserialize JSON-LD documents into given models it may be necessary to process them with a `@context` or `@frame` 6 | so that it's structure conforms to a given schema. This page lists the ways in which it is possible to define `@context` and `@frame` for 7 | the C# types. 8 | 9 | First let's import the required namespaces. 10 | 11 | 12 | ``` c# 13 | using System; 14 | using JsonLD.Entities; 15 | using Newtonsoft.Json.Linq; 16 | using Xunit; 17 | ``` 18 | 19 | ### Define `@context` inline with class definition 20 | 21 | ``` c# 22 | public class DefiningContextInline 23 | { 24 | 25 | private const string TestContext = "{ '@base': 'http://example.com/' }"; 26 | ``` 27 | 28 | The simples way is to define a `Context` property. It must be static and return a string or `JToken`. It can also be private. 29 | 30 | Below are example entity types with various ways of defining the `@context` as property. 31 | 32 | ``` c# 33 | public class ContextInlineProperty 34 | { 35 | public static JObject Context 36 | { 37 | get { return JObject.Parse(TestContext); } 38 | } 39 | } 40 | 41 | public class ContextInlinePrivateProperty 42 | { 43 | private static JObject Context 44 | { 45 | get { return JObject.Parse(TestContext); } 46 | } 47 | } 48 | 49 | public class ContextInlineStaticStringProperty 50 | { 51 | public static string Context 52 | { 53 | get { return TestContext; } 54 | } 55 | } 56 | ``` 57 | 58 | When each of the above will be serailized, the inline context will be used. 59 | 60 | ``` c# 61 | [Theory] 62 | [InlineData(typeof(ContextInlineProperty))] 63 | [InlineData(typeof(ContextInlinePrivateProperty))] 64 | [InlineData(typeof(ContextInlineStaticStringProperty))] 65 | public void UsesInlineContextPropertyWhenSerializing(Type entityType) 66 | { 67 | // given 68 | const string expected = "{ '@context': { '@base': 'http://example.com/' } }"; 69 | var entity = Activator.CreateInstance(entityType); 70 | var serializer = new EntitySerializer(); 71 | 72 | // when 73 | var json = serializer.Serialize(entity); 74 | 75 | // then 76 | Assert.True(JToken.DeepEquals(json, JObject.Parse(expected)), $"Actual object is {json}"); 77 | } 78 | ``` 79 | 80 | Finally, the type can declare a static `GetContext` method, which takes an object of said type as parameter. 81 | This way a `@context` can be built dynamically. 82 | 83 | Note that a 84 | 85 | 86 | ``` c# 87 | public class ContextInlineMethod 88 | { 89 | private readonly string _base; 90 | 91 | public ContextInlineMethod(string @base) 92 | { 93 | _base = @base; 94 | } 95 | 96 | private static JObject GetContext(ContextInlineMethod instance) 97 | { 98 | return JObject.Parse($@"{{ 99 | '@base': 'http://example.com/{instance._base}/' 100 | }}"); 101 | } 102 | public static string Context 103 | { 104 | get { return TestContext; } 105 | } 106 | } 107 | 108 | [Fact] 109 | public void UsesGetContextMethodSerializing() 110 | { 111 | // given 112 | const string expected = "{ '@context': { '@base': 'http://example.com/test/' } }"; 113 | var entity = new ContextInlineMethod("test"); 114 | var serializer = new EntitySerializer(); 115 | 116 | // when 117 | var json = serializer.Serialize(entity); 118 | 119 | // then 120 | Assert.True(JToken.DeepEquals(json, JObject.Parse(expected)), $"Actual object is {json}"); 121 | } 122 | } 123 | ``` 124 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Bindings/DeserializingSteps.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using FakeItEasy; 4 | using Newtonsoft.Json.Linq; 5 | using TechTalk.SpecFlow; 6 | 7 | namespace JsonLD.Entities.Tests.Bindings 8 | { 9 | [Binding] 10 | public class DeserializingSteps 11 | { 12 | private static readonly MethodInfo DeserializeQuadsMethod; 13 | 14 | private static readonly MethodInfo DeserializeJsonMethod; 15 | 16 | private readonly SerializerTestContext testContext; 17 | private readonly ScenarioContext scenarioContext; 18 | 19 | static DeserializingSteps() 20 | { 21 | DeserializeQuadsMethod = typeof(IEntitySerializer).GetMethod("Deserialize", new[] { typeof(string) }); 22 | DeserializeJsonMethod = typeof(IEntitySerializer).GetMethod("Deserialize", new[] { typeof(JToken) }); 23 | } 24 | 25 | public DeserializingSteps(SerializerTestContext context, ScenarioContext scenarioContext) 26 | { 27 | this.testContext = context; 28 | this.scenarioContext = scenarioContext; 29 | } 30 | 31 | [Given(@"@context is:")] 32 | public void GivenContextIs(string jsonLdContext) 33 | { 34 | scenarioContext.Set(JObject.Parse(jsonLdContext), "@context"); 35 | } 36 | 37 | [Given(@"frame is")] 38 | public void GivenFrameIs(string inputFrame) 39 | { 40 | scenarioContext.Set(JObject.Parse(inputFrame), "frame"); 41 | } 42 | 43 | [Given(@"NQuads:")] 44 | public void GivenRDFData(string nQuads) 45 | { 46 | this.testContext.NQuads = nQuads; 47 | } 48 | 49 | [Given(@"JSON-LD:")] 50 | public void GivenJsonLd(string jsonLd) 51 | { 52 | this.testContext.JsonLdObject = JToken.Parse(jsonLd); 53 | } 54 | 55 | [Scope(Tag = "NQuads")] 56 | [When(@"I deserialize into '(.*)'")] 57 | public void WhenIDeserializeNQuads(string typeName) 58 | { 59 | var entityType = Type.GetType(typeName, true); 60 | 61 | this.SetupProviders(entityType); 62 | var typedDeserialize = DeserializeQuadsMethod.MakeGenericMethod(entityType); 63 | 64 | var entity = typedDeserialize.Invoke(this.testContext.Serializer, new object[] { this.testContext.NQuads }); 65 | 66 | scenarioContext.Set(entity, "Entity"); 67 | } 68 | 69 | [Scope(Tag = "JsonLD")] 70 | [When(@"I deserialize into '(.*)'")] 71 | public void WhenIDeserializeInto(string typeName) 72 | { 73 | var entityType = Type.GetType(typeName, true); 74 | 75 | this.SetupProviders(entityType); 76 | var typedDeserialize = DeserializeJsonMethod.MakeGenericMethod(entityType); 77 | 78 | var entity = typedDeserialize.Invoke(this.testContext.Serializer, new object[] { this.testContext.JsonLdObject }); 79 | 80 | scenarioContext.Set(entity, "Entity"); 81 | } 82 | 83 | private void SetupProviders(Type entityType) 84 | { 85 | JObject context = null; 86 | JObject frame = null; 87 | 88 | if (scenarioContext.ContainsKey("@context")) 89 | { 90 | context = scenarioContext.Get("@context"); 91 | } 92 | 93 | if (scenarioContext.ContainsKey("frame")) 94 | { 95 | frame = scenarioContext.Get("frame"); 96 | } 97 | 98 | A.CallTo(() => this.testContext.ContextProvider.GetContext(entityType)).Returns(context); 99 | A.CallTo(() => this.testContext.FrameProvider.GetFrame(entityType)).Returns(frame); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ![icon](https://raw.githubusercontent.com/wikibus/JsonLD.Entities/master/assets/icon.png) 2 | 3 | # JsonLD.Entities [![Build status](https://ci.appveyor.com/api/projects/status/u4riv8ftspthkvgh/branch/master?svg=true)](https://ci.appveyor.com/project/tpluscode78631/jsonld-entities/branch/master) [![NuGet version](https://badge.fury.io/nu/jsonld.entities.svg)](https://badge.fury.io/nu/jsonld.entities) [![codecov.io][cov-badge]][cov-link] [![codefactor][codefactor-badge]][codefactor-link] 4 | 5 | JsonLD.Entities is a small library for serializing and deserializing POCOs in JSON-LD. 6 | 7 | ## What is JSON-LD? 8 | 9 | If you are not familiar with JSON-LD you could first go to it's [main website][jsonld]. 10 | 11 | JSON-LD is the newest [RDF][rdf] serialization format. It means that serialized 12 | data has structure and unambiguous meaning. For example if there is a `name` property, it can be identified becasue the `name` relation 13 | itself can be identified by the use of URI (and usually URLs). 14 | 15 | In general JSON-LD has a familiar json structure, which makes it best suited as the entrypoint to the Semantic Web at large. It works by 16 | simply extending the typical json document structure. In reality it gets a _little_ bit more complex, because json has a tree structure, 17 | but RDF represents graphs. And it is possible to represent a graph as multiple equivalent trees. 18 | 19 | To learn more about JSON-LD you should visit its [formal specification][jsonld-spec] and the [playground][playground], where you can 20 | experiment. 21 | 22 | ## Getting JsonLD.Entities 23 | 24 | The project's CI build creates nupkgs automatically and they are published on NuGet. To install use the below command in 25 | Package Manager Console. 26 | 27 | ``` 28 | install-package JsonLD.Entities 29 | ``` 30 | 31 | Prerelease builds are available on AppVeyor's project feed: `https://ci.appveyor.com/nuget/jsonld-entities-aavhsnxi7xjp` 32 | 33 | ## Building 34 | 35 | The project was created in VS 2010 and should build without problems on any VS 2010 and newer. Note that external packages aren't 36 | downloaded with NuGet, but rather with a new tool called [Paket](http://fsprojects.github.io/Paket/). For a better developer eperience 37 | there is a [Visual Studio Extension](https://visualstudiogallery.msdn.microsoft.com/ce104917-e8b3-4365-9490-8432c6e75c36). 38 | Otherwise run this in console from repository root: 39 | 40 | ``` batch 41 | > .paket\paket.bootstrapper.exe 42 | > .paket\paket.exe restore 43 | ``` 44 | 45 | ## Usage examples 46 | 47 | All usage samples are written in the [Literate Programming][lp] manner and can be conveniently viewed on GitHub in the [Documentation project][docs] 48 | 49 | Graph image from [W3C](http://www.w3.org/RDF/) originally desgined by [Bill Schwappacher](mailto:bill@tracermedia.com). 50 | 51 | [playground]: http://json-ld.org/playground/ 52 | [jsonld-spec]: http://json-ld.org/spec/latest/json-ld/ 53 | [jsonld-api]: http://www.w3.org/TR/json-ld-api/ 54 | [jsonld]: http://json-ld.org 55 | [rdf]: http://en.wikipedia.org/wiki/Resource_Description_Framework 56 | [readme]: http://github.com/wikibus/JsonLD.Entities/blob/master/src/Documentation/Readme.cs 57 | [jsonld-context]: http://www.w3.org/TR/json-ld/#the-context 58 | [docs]: https://github.com/wikibus/JsonLD.Entities/tree/master/src/Documentation 59 | [lp]: https://en.wikipedia.org/wiki/Literate_programming 60 | [cov-badge]: https://codecov.io/github/wikibus/JsonLD.Entities/coverage.svg?branch=master 61 | [cov-link]: https://codecov.io/github/wikibus/JsonLD.Entities?branch=master 62 | [codefactor-badge]: https://www.codefactor.io/repository/github/wikibus/JsonLD.Entities/badge/master 63 | [codefactor-link]: https://www.codefactor.io/repository/github/wikibus/JsonLD.Entities/overview/master 64 | 65 | ## Releasing 66 | 67 | Use [`versionize`](https://github.com/saintedlama/versionize) totag the next release. 68 | 69 | ``` 70 | dotnet tool install --global Versionize 71 | versionize 72 | git push --follow-tags origin master 73 | ``` -------------------------------------------------------------------------------- /.paket/paket.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | true 7 | $(MSBuildThisFileDirectory) 8 | $(MSBuildThisFileDirectory)..\ 9 | $(PaketRootPath)paket.lock 10 | $(PaketRootPath)paket-files\paket.restore.cached 11 | /Library/Frameworks/Mono.framework/Commands/mono 12 | mono 13 | 14 | 15 | 16 | 17 | $(PaketRootPath)paket.exe 18 | $(PaketToolsPath)paket.exe 19 | "$(PaketExePath)" 20 | $(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)" 21 | 22 | 23 | 24 | 25 | 26 | $(MSBuildProjectFullPath).paket.references 27 | 28 | 29 | 30 | 31 | $(MSBuildProjectDirectory)\$(MSBuildProjectName).paket.references 32 | 33 | 34 | 35 | 36 | $(MSBuildProjectDirectory)\paket.references 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | $(PaketCommand) restore --references-file "$(PaketReferences)" 49 | 50 | RestorePackages; $(BuildDependsOn); 51 | 52 | 53 | 54 | true 55 | 56 | 57 | 58 | $([System.IO.File]::ReadAllText('$(PaketRestoreCacheFile)')) 59 | $([System.IO.File]::ReadAllText('$(PaketLockFilePath)')) 60 | true 61 | false 62 | true 63 | 64 | 65 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/Helpers/TypeExtensionTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json; 4 | using Xunit; 5 | 6 | namespace JsonLD.Entities.Tests.Helpers 7 | { 8 | public class TypeExtensionTests 9 | { 10 | [Fact] 11 | public void Should_return_type_from_string_Type_property() 12 | { 13 | // given 14 | var expected = new Uri("http://example.com/vocab/TypeAsString"); 15 | 16 | // when 17 | var typeIdentifier = typeof(TypeAsString).GetTypeIdentifier(); 18 | 19 | // then 20 | Assert.Equal(expected, typeIdentifier); 21 | } 22 | 23 | [Fact] 24 | public void Should_return_type_from_Uri_Type_property() 25 | { 26 | // given 27 | var expected = new Uri("http://example.com/vocab/TypeAsUri"); 28 | 29 | // when 30 | var typeIdentifier = typeof(TypeAsUri).GetTypeIdentifier(); 31 | 32 | // then 33 | Assert.Equal(expected, typeIdentifier); 34 | } 35 | 36 | [Fact] 37 | public void Should_return_type_from_Uri_Types_property() 38 | { 39 | // given 40 | var expected = new Uri("http://example.com/vocab/TypesAsUri"); 41 | 42 | // when 43 | var typeIdentifier = typeof(TypesAsUri).GetTypeIdentifier(); 44 | 45 | // then 46 | Assert.Equal(expected, typeIdentifier); 47 | } 48 | 49 | [Fact] 50 | public void Should_return_type_from_string_Types_property() 51 | { 52 | // given 53 | var expected = new Uri("http://example.com/vocab/TypesAsString"); 54 | 55 | // when 56 | var typeIdentifier = typeof(TypesAsString).GetTypeIdentifier(); 57 | 58 | // then 59 | Assert.Equal(expected, typeIdentifier); 60 | } 61 | 62 | [Fact] 63 | public void Should_return_type_from_annotated_property() 64 | { 65 | // given 66 | var expected = new Uri("http://example.com/vocab/CustomProperty"); 67 | 68 | // when 69 | var typeIdentifier = typeof(CustomProperty).GetTypeIdentifier(); 70 | 71 | // then 72 | Assert.Equal(expected, typeIdentifier); 73 | } 74 | 75 | [Fact] 76 | public void Should_throw_when_Types_contains_multiple_elements() 77 | { 78 | Assert.Throws(() => typeof(MultipleTypesAsUri).GetTypeIdentifier()); 79 | } 80 | 81 | [Fact] 82 | public void Should_throw_when_class_doesnt_have_a_static_types_property() 83 | { 84 | Assert.Throws(() => typeof(object).GetTypeIdentifier()); 85 | } 86 | 87 | private class TypeAsString 88 | { 89 | private static string Type => "http://example.com/vocab/TypeAsString"; 90 | } 91 | 92 | private class TypeAsUri 93 | { 94 | public static Uri Type => new Uri("http://example.com/vocab/TypeAsUri"); 95 | } 96 | 97 | private class TypesAsUri 98 | { 99 | public static Uri[] Types => new[] 100 | { 101 | new Uri("http://example.com/vocab/TypesAsUri") 102 | }; 103 | } 104 | 105 | private class TypesAsString 106 | { 107 | public static IList Types => new List 108 | { 109 | "http://example.com/vocab/TypesAsString" 110 | }; 111 | } 112 | 113 | private class MultipleTypesAsUri 114 | { 115 | public static IEnumerable Types 116 | { 117 | get 118 | { 119 | yield return new Uri("http://example.com/vocab/MultipleTypesAsUri"); 120 | yield return new Uri("http://example.com/vocab/SecondType"); 121 | } 122 | } 123 | } 124 | 125 | private class CustomProperty 126 | { 127 | [JsonProperty(JsonLdKeywords.Type)] 128 | private static string Class => "http://example.com/vocab/CustomProperty"; 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /src/JsonLD.Entities/JsonLdContractResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using JsonLD.Entities.Converters; 7 | using Newtonsoft.Json; 8 | using Newtonsoft.Json.Serialization; 9 | 10 | namespace JsonLD.Entities 11 | { 12 | /// 13 | /// Camel-case contract resolver with overrides for JSON-LD features 14 | /// 15 | public sealed class JsonLdContractResolver : CamelCasePropertyNamesContractResolver 16 | { 17 | private static readonly ICollection SetTypes = new HashSet(); 18 | private static readonly ICollection ListTypes = new HashSet(); 19 | private static readonly JsonLdNamingStrategy JsonLdNamingStrategy = new JsonLdNamingStrategy(); 20 | 21 | static JsonLdContractResolver() 22 | { 23 | SetTypes.Add(typeof(ICollection<>)); 24 | SetTypes.Add(typeof(IEnumerable)); 25 | SetTypes.Add(typeof(ISet<>)); 26 | ListTypes.Add(typeof(IList)); 27 | ListTypes.Add(typeof(IList<>)); 28 | } 29 | 30 | /// 31 | /// Initializes a new instance of the class. 32 | /// 33 | public JsonLdContractResolver() 34 | { 35 | this.NamingStrategy = JsonLdNamingStrategy; 36 | } 37 | 38 | /// 39 | /// Resolves the contract for a given type. 40 | /// 41 | /// The type to resolve a contract for. 42 | public override JsonContract ResolveContract(Type type) 43 | { 44 | var contract = base.ResolveContract(type); 45 | 46 | if (contract is JsonArrayContract) 47 | { 48 | Type converterType; 49 | Type elementType = ((JsonArrayContract)contract).CollectionItemType; 50 | 51 | if (type.GetTypeInfo().BaseType == typeof(Array)) 52 | { 53 | converterType = typeof(JsonLdArrayConverter<>); 54 | } 55 | else if (IsListType(type)) 56 | { 57 | converterType = typeof(JsonLdListConverter<>); 58 | } 59 | else 60 | { 61 | converterType = typeof(JsonLdSetConverter<>); 62 | } 63 | 64 | contract.Converter = (JsonConverter)Activator.CreateInstance(converterType.MakeGenericType(elementType)); 65 | } 66 | else if (contract is JsonPrimitiveContract && contract.Converter == null) 67 | { 68 | contract.Converter = new JsonLdLiteralConverter(); 69 | } 70 | 71 | return contract; 72 | } 73 | 74 | /// 75 | /// Resolves the contract converter. 76 | /// 77 | protected override JsonConverter ResolveContractConverter(Type type) 78 | { 79 | if (type == typeof(Uri)) 80 | { 81 | return new StringUriConverter(); 82 | } 83 | 84 | return base.ResolveContractConverter(type); 85 | } 86 | 87 | /// 88 | /// Ensures that empty collections aren't serialized 89 | /// 90 | protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) 91 | { 92 | var property = base.CreateProperty(member, memberSerialization); 93 | 94 | if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType) && property.PropertyType != typeof(string)) 95 | { 96 | property.ShouldSerialize = instance => 97 | { 98 | var collection = property.ValueProvider.GetValue(instance); 99 | return collection != null && ((IEnumerable)collection).Cast().Any(); 100 | }; 101 | } 102 | 103 | return property; 104 | } 105 | 106 | private static bool IsListType(Type type) 107 | { 108 | return typeof(IList).IsAssignableFrom(type) 109 | || (type.GetTypeInfo().IsGenericType && typeof(IList<>).IsAssignableFrom(type.GetGenericTypeDefinition())); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/Context/AutoContextBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | using Newtonsoft.Json.Linq; 5 | 6 | namespace JsonLD.Entities.Context 7 | { 8 | /// 9 | /// Base class for automatically creating @context for the given type 10 | /// 11 | /// model type 12 | public abstract class AutoContextBase : JObject 13 | { 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | protected AutoContextBase(AutoContextStrategy strategy) 18 | { 19 | strategy.InitializeProperties(this); 20 | } 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | protected AutoContextBase(JObject context, AutoContextStrategy strategy) 26 | : base(context) 27 | { 28 | strategy.InitializeProperties(this); 29 | } 30 | 31 | /// 32 | /// Allows setting up additional context mapping properties for 33 | /// a mapping such as type coercion of language 34 | /// 35 | /// The return type of property. 36 | /// 37 | /// if selected property is not found in the @context 38 | /// 39 | public AutoContextBase Property( 40 | Expression> propertyExpression, 41 | Func setupMapping) 42 | { 43 | var property = propertyExpression.GetProperty(); 44 | string contextKey = property.GetJsonPropertyName(); 45 | 46 | if (this[contextKey] == null) 47 | { 48 | var message = string.Format("Cannot find property '{0}' in @context", contextKey); 49 | throw new ArgumentException(message, nameof(propertyExpression)); 50 | } 51 | 52 | var currentMappedPredicate = GetPropertyKey(this[contextKey]); 53 | if (currentMappedPredicate == null) 54 | { 55 | var message = string.Format("Invalid value in @context mapped to property {0}", contextKey); 56 | throw new InvalidOperationException(message); 57 | } 58 | 59 | var newMapping = setupMapping(contextKey.IsProperty(currentMappedPredicate)); 60 | this[contextKey] = newMapping.Value; 61 | 62 | return this; 63 | } 64 | 65 | private static string GetPropertyKey(JToken jToken) 66 | { 67 | if (jToken is JObject) 68 | { 69 | return ((JObject)jToken)[JsonLdKeywords.Id].ToString(); 70 | } 71 | 72 | if (jToken is JValue) 73 | { 74 | return jToken.ToString(); 75 | } 76 | 77 | return null; 78 | } 79 | 80 | /// 81 | /// Implementing classes define how JSON property is 82 | /// translated to a predicate URI 83 | /// 84 | protected abstract class AutoContextStrategy 85 | { 86 | /// 87 | /// Initializes the mappings for properties unmapped in @context. 88 | /// 89 | internal void InitializeProperties(JObject context) 90 | { 91 | foreach (var property in typeof(T).GetProperties()) 92 | { 93 | var contextKey = property.GetJsonPropertyName(); 94 | if (JsonLdKeywords.IsKeyword(contextKey)) 95 | { 96 | continue; 97 | } 98 | 99 | if (context[contextKey] == null) 100 | { 101 | context.Add(contextKey, this.GetPropertyId(contextKey)); 102 | } 103 | } 104 | } 105 | 106 | /// 107 | /// Gets the property identifier. 108 | /// 109 | /// Name of the JSON property. 110 | protected abstract string GetPropertyId(string propertyName); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | .vs 9 | 10 | # Build results 11 | [Dd]ebug/ 12 | [Dd]ebugPublic/ 13 | [Rr]elease/ 14 | [Rr]eleases/ 15 | x64/ 16 | x86/ 17 | build/ 18 | bld/ 19 | [Bb]in/ 20 | [Oo]bj/ 21 | **/VersionAssemblyInfo.cs 22 | 23 | # Roslyn cache directories 24 | *.ide/ 25 | 26 | # MSTest test Results 27 | [Tt]est[Rr]esult*/ 28 | [Bb]uild[Ll]og.* 29 | 30 | #NUNIT 31 | *.VisualState.xml 32 | TestResult.xml 33 | 34 | # Build Results of an ATL Project 35 | [Dd]ebugPS/ 36 | [Rr]eleasePS/ 37 | dlldata.c 38 | 39 | *_i.c 40 | *_p.c 41 | *_i.h 42 | *.ilk 43 | *.meta 44 | *.obj 45 | *.pch 46 | *.pdb 47 | *.pgc 48 | *.pgd 49 | *.rsp 50 | *.sbr 51 | *.tlb 52 | *.tli 53 | *.tlh 54 | *.tmp 55 | *.tmp_proj 56 | *.log 57 | *.vspscc 58 | *.vssscc 59 | .builds 60 | *.pidb 61 | *.svclog 62 | *.scc 63 | 64 | # Chutzpah Test files 65 | _Chutzpah* 66 | 67 | # Visual C++ cache files 68 | ipch/ 69 | *.aps 70 | *.ncb 71 | *.opensdf 72 | *.sdf 73 | *.cachefile 74 | 75 | # Visual Studio profiler 76 | *.psess 77 | *.vsp 78 | *.vspx 79 | 80 | # TFS 2012 Local Workspace 81 | $tf/ 82 | 83 | # Guidance Automation Toolkit 84 | *.gpState 85 | 86 | # ReSharper is a .NET coding add-in 87 | _ReSharper*/ 88 | *.[Rr]e[Ss]harper 89 | *.DotSettings.user 90 | 91 | # JustCode is a .NET coding addin-in 92 | .JustCode 93 | 94 | # TeamCity is a build add-in 95 | _TeamCity* 96 | 97 | # DotCover is a Code Coverage Tool 98 | *.dotCover 99 | opencover.xml 100 | 101 | # NCrunch 102 | _NCrunch_* 103 | .*crunch*.local.xml 104 | 105 | # MightyMoose 106 | *.mm.* 107 | AutoTest.Net/ 108 | 109 | # Web workbench (sass) 110 | .sass-cache/ 111 | 112 | # Installshield output folder 113 | [Ee]xpress/ 114 | 115 | # DocProject is a documentation generator add-in 116 | DocProject/buildhelp/ 117 | DocProject/Help/*.HxT 118 | DocProject/Help/*.HxC 119 | DocProject/Help/*.hhc 120 | DocProject/Help/*.hhk 121 | DocProject/Help/*.hhp 122 | DocProject/Help/Html2 123 | DocProject/Help/html 124 | 125 | # Click-Once directory 126 | publish/ 127 | 128 | # Publish Web Output 129 | *.[Pp]ublish.xml 130 | *.azurePubxml 131 | # TODO: Comment the next line if you want to checkin your web deploy settings 132 | # but database connection strings (with potential passwords) will be unencrypted 133 | *.pubxml 134 | *.publishproj 135 | 136 | # NuGet Packages 137 | *.nupkg 138 | # The packages folder can be ignored because of Package Restore 139 | **/packages/ 140 | # except build/, which is used as an MSBuild target. 141 | !**/packages/build/ 142 | # If using the old MSBuild-Integrated Package Restore, uncomment this: 143 | #!**/packages/repositories.config 144 | **/paket-files 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | sql/ 155 | *.Cache 156 | ClientBin/ 157 | [Ss]tyle[Cc]op.* 158 | ~$* 159 | *~ 160 | *.dbmdl 161 | *.dbproj.schemaview 162 | *.pfx 163 | *.publishsettings 164 | node_modules/ 165 | 166 | # RIA/Silverlight projects 167 | Generated_Code/ 168 | 169 | # Backup & report files from converting an old project file 170 | # to a newer Visual Studio version. Backup files are not needed, 171 | # because we have git ;-) 172 | _UpgradeReport_Files/ 173 | Backup*/ 174 | UpgradeLog*.XML 175 | UpgradeLog*.htm 176 | 177 | # SQL Server files 178 | *.mdf 179 | *.ldf 180 | 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | 186 | # Microsoft Fakes 187 | FakesAssemblies/ 188 | 189 | # ========================= 190 | # Operating System Files 191 | # ========================= 192 | 193 | # OSX 194 | # ========================= 195 | 196 | .DS_Store 197 | .AppleDouble 198 | .LSOverride 199 | 200 | # Icon must end with two \r 201 | Icon 202 | 203 | 204 | # Thumbnails 205 | ._* 206 | 207 | # Files that might appear on external disk 208 | .Spotlight-V100 209 | .Trashes 210 | 211 | # Directories potentially created on remote AFP share 212 | .AppleDB 213 | .AppleDesktop 214 | Network Trash Folder 215 | Temporary Items 216 | .apdisk 217 | 218 | # Windows 219 | # ========================= 220 | 221 | # Windows image file caches 222 | Thumbs.db 223 | ehthumbs.db 224 | 225 | # Folder config file 226 | Desktop.ini 227 | 228 | # Recycle Bin used on file shares 229 | $RECYCLE.BIN/ 230 | 231 | # Windows Installer files 232 | *.cab 233 | *.msi 234 | *.msm 235 | *.msp 236 | 237 | .NETFramework,Version=v4.0.AssemblyAttributes.mkd 238 | src/JsonLD.Entities.Tests/ParsingTests 239 | VersionAssemblyInfo.cs 240 | .paket/paket.exe 241 | 242 | !tools/ 243 | tools/* 244 | !tools/llite/ 245 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/ContextTests/ContextExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using JsonLD.Entities.Context; 2 | using Newtonsoft.Json.Linq; 3 | using Xunit; 4 | 5 | namespace JsonLD.Entities.Tests.ContextTests 6 | { 7 | public class ContextExtensionsTests 8 | { 9 | [Fact] 10 | public void Should_combine_multiple_objects_into_an_array() 11 | { 12 | // given 13 | var context1 = JObject.Parse("{ 'xsd': 'http://xsd.ns' }"); 14 | var context2 = JObject.Parse("{ 'foaf': 'http://foaf.ns' }"); 15 | var expected = JToken.Parse("[ { 'xsd': 'http://xsd.ns' }, { 'foaf': 'http://foaf.ns' } ]"); 16 | 17 | // when 18 | var merged = context1.MergeWith(context2); 19 | 20 | // then 21 | Assert.True(JToken.DeepEquals(merged, expected)); 22 | } 23 | 24 | [Fact] 25 | public void Should_return_original_if_no_additional_contexts_passed() 26 | { 27 | // given 28 | var context = JObject.Parse("{ 'xsd': 'http://xsd.ns' }"); 29 | 30 | // when 31 | var merged = context.MergeWith(); 32 | 33 | // then 34 | Assert.Same(context, merged); 35 | } 36 | 37 | [Fact] 38 | public void Should_return_original_if_nulls_are_passed() 39 | { 40 | // given 41 | var context = JObject.Parse("{ 'xsd': 'http://xsd.ns' }"); 42 | 43 | // when 44 | var merged = context.MergeWith(null, null); 45 | 46 | // then 47 | Assert.Same(context, merged); 48 | } 49 | 50 | [Fact] 51 | public void Should_flatten_additonal_array_contexts() 52 | { 53 | // given 54 | var context1 = JObject.Parse("{ 'xsd': 'http://xsd.ns' }"); 55 | var context2 = JToken.Parse("[ { 'sch': 'http://schema.org' }, { 'foaf': 'http://foaf.ns' } ]"); 56 | var expected = JToken.Parse("[ { 'xsd': 'http://xsd.ns' }, { 'sch': 'http://schema.org' }, { 'foaf': 'http://foaf.ns' } ]"); 57 | 58 | // when 59 | var merged = context1.MergeWith(context2); 60 | 61 | // then 62 | Assert.True(JToken.DeepEquals(merged, expected)); 63 | } 64 | 65 | [Fact] 66 | public void Should_append_to_original_if_it_is_array() 67 | { 68 | // given 69 | var context1 = JToken.Parse("[ { 'sch': 'http://schema.org' }, { 'foaf': 'http://foaf.ns' } ]"); 70 | var context2 = JObject.Parse("{ 'xsd': 'http://xsd.ns' }"); 71 | var expected = JToken.Parse("[ { 'sch': 'http://schema.org' }, { 'foaf': 'http://foaf.ns' }, { 'xsd': 'http://xsd.ns' } ]"); 72 | 73 | // when 74 | var merged = context1.MergeWith(context2); 75 | 76 | // then 77 | Assert.True(JToken.DeepEquals(merged, expected)); 78 | } 79 | 80 | [Fact] 81 | public void Should_merge_multiple_arrays() 82 | { 83 | // given 84 | var context1 = JToken.Parse("[ { 'sch': 'http://schema.org' }, { 'foaf': 'http://foaf.ns' } ]"); 85 | var context2 = JToken.Parse("[ { 'xsd': 'http://xsd.ns' }, { 'bibo': 'http://bibo.ns' } ]"); 86 | var context3 = JToken.Parse("[ { 'dc': 'http://dc.terms' }, { 'owl': 'http://owl.ns' } ]"); 87 | var expected = JToken.Parse(@" 88 | [ 89 | { 'sch': 'http://schema.org' }, 90 | { 'foaf': 'http://foaf.ns' }, 91 | { 'xsd': 'http://xsd.ns' }, 92 | { 'bibo': 'http://bibo.ns' }, 93 | { 'dc': 'http://dc.terms' }, 94 | { 'owl': 'http://owl.ns' } 95 | ]"); 96 | 97 | // when 98 | var merged = context1.MergeWith(context2, context3); 99 | 100 | // then 101 | Assert.True(JToken.DeepEquals(merged, expected)); 102 | } 103 | 104 | [Fact] 105 | public void Should_merge_external_contexts() 106 | { 107 | // given 108 | var context1 = JToken.Parse("[ { 'sch': 'http://schema.org' }, { 'foaf': 'http://foaf.ns' } ]"); 109 | var context2 = JToken.Parse("'http://external.ctx'"); 110 | var context3 = JToken.Parse("[ { 'dc': 'http://dc.terms' }, { 'owl': 'http://owl.ns' } ]"); 111 | var expected = JToken.Parse(@" 112 | [ 113 | { 'sch': 'http://schema.org' }, 114 | { 'foaf': 'http://foaf.ns' }, 115 | 'http://external.ctx', 116 | { 'dc': 'http://dc.terms' }, 117 | { 'owl': 'http://owl.ns' } 118 | ]"); 119 | 120 | // when 121 | var merged = context1.MergeWith(context2, context3); 122 | 123 | // then 124 | Assert.True(JToken.DeepEquals(merged, expected)); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Documentation/Serializing/EnsuringCompactedResource/Readme.cs: -------------------------------------------------------------------------------- 1 | /** 2 | # Documentation 3 | 4 | ## Ensuring serialized object is compacted 5 | 6 | First let's import the required namespaces. 7 | **/ 8 | 9 | using JsonLD.Entities; 10 | using Newtonsoft.Json; 11 | using Newtonsoft.Json.Linq; 12 | using Xunit; 13 | 14 | public class EnsuringCompactedJson 15 | { 16 | 17 | /** 18 | 19 | It is possible to use absolute URIs as `[JsonProperty]` so that the serialized object doesn't need to use 20 | a `@context` at all. This is especially useful for example if a class is used as nested objects and defining 21 | context on each class in the tree would add one on each level, even if it redefines the same terms. 22 | 23 | ``` json 24 | { 25 | "@context": { 26 | "foaf": "http://xmlns.com/foaf/0.1/", 27 | "name": "foaf:name", 28 | "interest": "foaf:interest" 29 | }, 30 | "@type": "Person", 31 | "name": "Tomasz Pluskiewicz", 32 | "interest": { 33 | "@context": { 34 | "foaf": "http://xmlns.com/foaf/0.1/", 35 | "name": "foaf:name" 36 | }, 37 | "name": "JSON-LD" 38 | } 39 | } 40 | ``` 41 | 42 | This can be avoided by mapping properties to absolute 43 | 44 | **/ 45 | 46 | public class PersonWithInterest 47 | { 48 | [JsonProperty("http://xmlns.com/foaf/0.1/interest")] 49 | public Interest Interest { get; set;} 50 | 51 | [JsonProperty("http://xmlns.com/foaf/0.1/name")] 52 | public string Name { get; set; } 53 | } 54 | 55 | public class Interest 56 | { 57 | [JsonProperty("http://xmlns.com/foaf/0.1/name")] 58 | public string Name { get; set; } 59 | } 60 | 61 | private static readonly JObject Context = JObject.Parse(@"{ 62 | 'foaf': 'http://xmlns.com/foaf/0.1/', 63 | 'name': 'foaf:name', 64 | 'interest': 'foaf:interest' 65 | }"); 66 | 67 | [Fact] 68 | public void ShouldForceCompactSerializedModelWhenRequested() 69 | { 70 | // given 71 | var person = new PersonWithInterest 72 | { 73 | Name = "Tomasz Pluskiewicz", 74 | Interest = new Interest { Name = "JSON-LD" } 75 | }; 76 | var contextProvider = new StaticContextProvider(); 77 | contextProvider.SetContext(typeof(PersonWithInterest), Context); 78 | var serializer = new EntitySerializer(contextProvider); 79 | 80 | // when 81 | dynamic serialized = serializer.Serialize(person, new SerializationOptions { SerializeCompacted = true }); 82 | 83 | // then 84 | Assert.Equal("Tomasz Pluskiewicz", serialized.name.ToString()); 85 | Assert.Equal("JSON-LD", serialized.interest.name.ToString()); 86 | } 87 | 88 | /** 89 | It is also possible to force this behaviour on by annotating a class with the `[SerializeCompacted]` 90 | attrbute. This could be useful in scenarios where the owner of the serialized classes doesn't control how 91 | and when the `EntitySerializer` is called. 92 | **/ 93 | 94 | [SerializeCompacted] 95 | public class PersonWithInterest2 96 | { 97 | [JsonProperty("http://xmlns.com/foaf/0.1/interest")] 98 | public Interest Interest { get; set;} 99 | 100 | [JsonProperty("http://xmlns.com/foaf/0.1/name")] 101 | public string Name { get; set; } 102 | } 103 | 104 | [Fact] 105 | public void ShouldForceCompactSerializedModelWhenSetByAttribute() 106 | { 107 | // given 108 | var person = new PersonWithInterest2 109 | { 110 | Name = "Tomasz Pluskiewicz", 111 | Interest = new Interest { Name = "JSON-LD" } 112 | }; 113 | var contextProvider = new StaticContextProvider(); 114 | contextProvider.SetContext(typeof(PersonWithInterest2), Context); 115 | var serializer = new EntitySerializer(contextProvider); 116 | 117 | // when 118 | dynamic serialized = serializer.Serialize(person); 119 | 120 | // then 121 | Assert.Equal("Tomasz Pluskiewicz", serialized.name.ToString()); 122 | Assert.Equal("JSON-LD", serialized.interest.name.ToString()); 123 | } 124 | 125 | /** 126 | And this also works on inherited classes. 127 | **/ 128 | 129 | public class PersonWithInterest3: PersonWithInterest2 130 | { 131 | } 132 | 133 | [Fact] 134 | public void ShouldForceCompactSerializedModelWhenSetByAttribute_Inherited() 135 | { 136 | // given 137 | var person = new PersonWithInterest3 138 | { 139 | Name = "Tomasz Pluskiewicz", 140 | Interest = new Interest { Name = "JSON-LD" } 141 | }; 142 | var contextProvider = new StaticContextProvider(); 143 | contextProvider.SetContext(typeof(PersonWithInterest3), Context); 144 | var serializer = new EntitySerializer(contextProvider); 145 | 146 | // when 147 | dynamic serialized = serializer.Serialize(person); 148 | 149 | // then 150 | Assert.Equal("Tomasz Pluskiewicz", serialized.name.ToString()); 151 | Assert.Equal("JSON-LD", serialized.interest.name.ToString()); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/Documentation/Serializing/EnsuringCompactedResource/Readme.mkd: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | ## Ensuring serialized object is compacted 4 | 5 | First let's import the required namespaces. 6 | 7 | ``` c# 8 | using JsonLD.Entities; 9 | using Newtonsoft.Json; 10 | using Newtonsoft.Json.Linq; 11 | using Xunit; 12 | 13 | public class EnsuringCompactedJson 14 | { 15 | ``` 16 | 17 | It is possible to use absolute URIs as `[JsonProperty]` so that the serialized object doesn't need to use 18 | a `@context` at all. This is especially useful for example if a class is used as nested objects and defining 19 | context on each class in the tree would add one on each level, even if it redefines the same terms. 20 | 21 | ``` json 22 | { 23 | "@context": { 24 | "foaf": "http://xmlns.com/foaf/0.1/", 25 | "name": "foaf:name", 26 | "interest": "foaf:interest" 27 | }, 28 | "@type": "Person", 29 | "name": "Tomasz Pluskiewicz", 30 | "interest": { 31 | "@context": { 32 | "foaf": "http://xmlns.com/foaf/0.1/", 33 | "name": "foaf:name" 34 | }, 35 | "name": "JSON-LD" 36 | } 37 | } 38 | ``` 39 | 40 | This can be avoided by mapping properties to absolute 41 | 42 | ``` c# 43 | public class PersonWithInterest 44 | { 45 | [JsonProperty("http://xmlns.com/foaf/0.1/interest")] 46 | public Interest Interest { get; set;} 47 | 48 | [JsonProperty("http://xmlns.com/foaf/0.1/name")] 49 | public string Name { get; set; } 50 | } 51 | 52 | public class Interest 53 | { 54 | [JsonProperty("http://xmlns.com/foaf/0.1/name")] 55 | public string Name { get; set; } 56 | } 57 | 58 | private static readonly JObject Context = JObject.Parse(@"{ 59 | 'foaf': 'http://xmlns.com/foaf/0.1/', 60 | 'name': 'foaf:name', 61 | 'interest': 'foaf:interest' 62 | }"); 63 | 64 | [Fact] 65 | public void ShouldForceCompactSerializedModelWhenRequested() 66 | { 67 | // given 68 | var person = new PersonWithInterest 69 | { 70 | Name = "Tomasz Pluskiewicz", 71 | Interest = new Interest { Name = "JSON-LD" } 72 | }; 73 | var contextProvider = new StaticContextProvider(); 74 | contextProvider.SetContext(typeof(PersonWithInterest), Context); 75 | var serializer = new EntitySerializer(contextProvider); 76 | 77 | // when 78 | dynamic serialized = serializer.Serialize(person, new SerializationOptions { SerializeCompacted = true }); 79 | 80 | // then 81 | Assert.Equal("Tomasz Pluskiewicz", serialized.name.ToString()); 82 | Assert.Equal("JSON-LD", serialized.interest.name.ToString()); 83 | } 84 | ``` 85 | 86 | It is also possible to force this behaviour on by annotating a class with the `[SerializeCompacted]` 87 | attrbute. This could be useful in scenarios where the owner of the serialized classes doesn't control how 88 | and when the `EntitySerializer` is called. 89 | 90 | ``` c# 91 | [SerializeCompacted] 92 | public class PersonWithInterest2 93 | { 94 | [JsonProperty("http://xmlns.com/foaf/0.1/interest")] 95 | public Interest Interest { get; set;} 96 | 97 | [JsonProperty("http://xmlns.com/foaf/0.1/name")] 98 | public string Name { get; set; } 99 | } 100 | 101 | [Fact] 102 | public void ShouldForceCompactSerializedModelWhenSetByAttribute() 103 | { 104 | // given 105 | var person = new PersonWithInterest2 106 | { 107 | Name = "Tomasz Pluskiewicz", 108 | Interest = new Interest { Name = "JSON-LD" } 109 | }; 110 | var contextProvider = new StaticContextProvider(); 111 | contextProvider.SetContext(typeof(PersonWithInterest2), Context); 112 | var serializer = new EntitySerializer(contextProvider); 113 | 114 | // when 115 | dynamic serialized = serializer.Serialize(person); 116 | 117 | // then 118 | Assert.Equal("Tomasz Pluskiewicz", serialized.name.ToString()); 119 | Assert.Equal("JSON-LD", serialized.interest.name.ToString()); 120 | } 121 | ``` 122 | 123 | And this also works on inherited classes. 124 | 125 | ``` c# 126 | public class PersonWithInterest3: PersonWithInterest2 127 | { 128 | } 129 | 130 | [Fact] 131 | public void ShouldForceCompactSerializedModelWhenSetByAttribute_Inherited() 132 | { 133 | // given 134 | var person = new PersonWithInterest3 135 | { 136 | Name = "Tomasz Pluskiewicz", 137 | Interest = new Interest { Name = "JSON-LD" } 138 | }; 139 | var contextProvider = new StaticContextProvider(); 140 | contextProvider.SetContext(typeof(PersonWithInterest3), Context); 141 | var serializer = new EntitySerializer(contextProvider); 142 | 143 | // when 144 | dynamic serialized = serializer.Serialize(person); 145 | 146 | // then 147 | Assert.Equal("Tomasz Pluskiewicz", serialized.name.ToString()); 148 | Assert.Equal("JSON-LD", serialized.interest.name.ToString()); 149 | } 150 | } 151 | ``` 152 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/EntitySerializer.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using JsonLD.Core; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Linq; 5 | using NullGuard; 6 | 7 | namespace JsonLD.Entities 8 | { 9 | /// 10 | /// Entity serializer 11 | /// 12 | public class EntitySerializer : IEntitySerializer 13 | { 14 | private readonly ContextResolver contextResolver; 15 | private readonly IFrameProvider frameProvider; 16 | private readonly JsonSerializer jsonSerializer; 17 | 18 | /// 19 | /// Initializes a new instance of the class. 20 | /// 21 | public EntitySerializer() 22 | : this(new NullContextProvider(), new NullFrameProvider()) 23 | { 24 | } 25 | 26 | /// 27 | /// Initializes a new instance of the class. 28 | /// 29 | /// The JSON-LD @context provider. 30 | public EntitySerializer(IContextProvider contextProvider) 31 | : this(contextProvider, new NullFrameProvider()) 32 | { 33 | } 34 | 35 | /// 36 | /// Initializes a new instance of the class. 37 | /// 38 | /// The JSON-LD @context provider. 39 | /// The JSON-LD frame provider. 40 | public EntitySerializer(IContextProvider contextProvider, IFrameProvider frameProvider) 41 | : this(new ContextResolver(contextProvider)) 42 | { 43 | this.frameProvider = frameProvider; 44 | } 45 | 46 | private EntitySerializer(ContextResolver contextResolver) 47 | { 48 | this.contextResolver = contextResolver; 49 | this.jsonSerializer = new JsonLdSerializer(); 50 | } 51 | 52 | /// 53 | /// Deserializes the NQuads into a typed model 54 | /// 55 | /// destination entity model type 56 | /// RDF data in NQuads. 57 | public T Deserialize(string nQuads) 58 | { 59 | var jsonLd = JsonLdProcessor.FromRDF(nQuads); 60 | var context = this.contextResolver.GetContext(typeof(T)); 61 | var frame = this.frameProvider.GetFrame(typeof(T)); 62 | if (context == null) 63 | { 64 | throw new ContextNotFoundException(typeof(T)); 65 | } 66 | 67 | return this.Deserialize(jsonLd, context, frame); 68 | } 69 | 70 | /// 71 | /// Deserializes the JSON-LD object into a typed model. 72 | /// 73 | /// destination entity model 74 | /// a JSON-LD object 75 | public T Deserialize(JToken jsonLd) 76 | { 77 | var jsonLdContext = this.contextResolver.GetContext(typeof(T)); 78 | var frame = this.frameProvider.GetFrame(typeof(T)); 79 | 80 | return this.Deserialize(jsonLd, jsonLdContext, frame); 81 | } 82 | 83 | /// 84 | /// Serializes the specified entity as JSON-LD. 85 | /// 86 | /// 87 | /// A compacted JSON-LD object 88 | /// 89 | public JObject Serialize(object entity, [AllowNull] SerializationOptions options = null) 90 | { 91 | options = options ?? new SerializationOptions(); 92 | var jsonLd = JObject.FromObject(entity, this.jsonSerializer); 93 | 94 | var context = this.contextResolver.GetContext(entity); 95 | if (context != null && IsNotEmpty(context)) 96 | { 97 | jsonLd.AddFirst(new JProperty("@context", context)); 98 | 99 | if (options.SerializeCompacted || entity.GetType().IsMarkedForCompaction()) 100 | { 101 | jsonLd = JsonLdProcessor.Compact(jsonLd, context, new JsonLdOptions()); 102 | } 103 | } 104 | 105 | return jsonLd; 106 | } 107 | 108 | private static bool IsNotEmpty(JToken context) 109 | { 110 | if (context is JObject) 111 | { 112 | return ((JObject)context).Count > 0; 113 | } 114 | 115 | var array = context as JArray; 116 | if (array != null) 117 | { 118 | return array.Any(IsNotEmpty); 119 | } 120 | 121 | return true; 122 | } 123 | 124 | private T Deserialize(JToken jsonLd, JToken context, JObject frame) 125 | { 126 | if (context == null) 127 | { 128 | return jsonLd.ToObject(this.jsonSerializer); 129 | } 130 | 131 | if (frame == null) 132 | { 133 | return JsonLdProcessor.Compact(jsonLd, context, new JsonLdOptions()).ToObject(this.jsonSerializer); 134 | } 135 | 136 | frame["@context"] = context; 137 | var framed = JsonLdProcessor.Frame(jsonLd, frame, new JsonLdOptions()); 138 | return framed["@graph"].Single().ToObject(this.jsonSerializer); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/JsonLD.Entities.Tests/EntitySerializerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FakeItEasy; 3 | using JsonLD.Entities.Tests.ContextTestEntities; 4 | using JsonLD.Entities.Tests.Entities; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Linq; 7 | using Xunit; 8 | 9 | namespace JsonLD.Entities.Tests 10 | { 11 | public class EntitySerializerTests 12 | { 13 | private readonly IContextProvider provider; 14 | private readonly EntitySerializer serializer; 15 | 16 | public EntitySerializerTests() 17 | { 18 | this.provider = A.Fake(); 19 | this.serializer = new EntitySerializer(this.provider); 20 | } 21 | 22 | [Fact] 23 | public void Deserializing_quads_should_throw_when_context_isnt_found() 24 | { 25 | // given 26 | A.CallTo(() => this.provider.GetContext(typeof(Person))).Returns(null); 27 | 28 | // when 29 | Assert.Throws(() => this.serializer.Deserialize(string.Empty)); 30 | } 31 | 32 | [Fact] 33 | public void Should_serialize_id_as_string() 34 | { 35 | // given 36 | A.CallTo(() => this.provider.GetContext(typeof(Person))).Returns(JObject.Parse(@"{ 37 | 'foaf': 'http://xmlns.com/foaf/0.1/', 38 | 'name': 'foaf:givenName', 39 | 'surname': 'foaf:familyName', 40 | 'birthDate': 'http://example.com/ontology#dateOfBirth' 41 | }")); 42 | var id = new Uri("http://example.org/Some/Person"); 43 | 44 | // when 45 | var person = this.serializer.Serialize(new Person { Id = id }); 46 | 47 | // then 48 | var jId = Assert.IsType(person["@id"]); 49 | Assert.Equal("http://example.org/Some/Person", jId.Value); 50 | } 51 | 52 | [Fact] 53 | public void Should_use_GetContext_method_from_base_class() 54 | { 55 | // given 56 | var obj = new DerivedClass { Name = "Tomasz" }; 57 | 58 | // when 59 | var serialized = this.serializer.Serialize(obj); 60 | 61 | // then 62 | Assert.NotNull(serialized["@context"]); 63 | } 64 | 65 | [Fact] 66 | public void Should_compact_when_one_of_contexts_is_empty() 67 | { 68 | // given 69 | A.CallTo(() => this.provider.GetContext(typeof(DerivedClass))).Returns(new JArray 70 | { 71 | new JObject { { "foaf", "http://not.foaf" } }, 72 | new JObject() 73 | }); 74 | var obj = new DerivedClass { Name = "Tomasz" }; 75 | 76 | // when 77 | var serialized = this.serializer.Serialize(obj, new SerializationOptions { SerializeCompacted = true }); 78 | 79 | // then 80 | Assert.NotNull(serialized["@context"]); 81 | } 82 | 83 | [Theory] 84 | [InlineData("{ 'property': 'http://example.com/absolute/id' }")] 85 | [InlineData("{ 'property': '/relative/id' }")] 86 | public void Should_deserialize_compacted_IriRef(string json) 87 | { 88 | // given 89 | dynamic raw = JsonConvert.DeserializeObject(json); 90 | var iriRef = new IriRef(raw.property.ToString()); 91 | 92 | // when 93 | var deserialized = this.serializer.Deserialize((JToken)raw); 94 | 95 | // then 96 | Assert.Equal(iriRef, deserialized.Property); 97 | } 98 | 99 | [Theory] 100 | [InlineData("{ 'property': { '@id': 'http://example.com/absolute/id' } }")] 101 | [InlineData("{ 'property': { '@id': '/relative/id' } }")] 102 | public void Should_deserialize_expanded_IriRef(string json) 103 | { 104 | // given 105 | dynamic raw = JsonConvert.DeserializeObject(json); 106 | var iriRef = new IriRef(raw.property["@id"].ToString()); 107 | 108 | // when 109 | var deserialized = this.serializer.Deserialize((JToken)raw); 110 | 111 | // then 112 | Assert.Equal(iriRef, deserialized.Property); 113 | } 114 | 115 | [Fact] 116 | public void Should_deserialize_null_IriRef() 117 | { 118 | // given 119 | dynamic raw = JsonConvert.DeserializeObject("{ 'property': { '@id': null } }"); 120 | 121 | // when 122 | var deserialized = this.serializer.Deserialize((JToken)raw); 123 | 124 | // then 125 | Assert.Equal(default(IriRef), deserialized.Property); 126 | } 127 | 128 | [Fact] 129 | public void Should_deserialize_null_Uri() 130 | { 131 | // given 132 | dynamic raw = JsonConvert.DeserializeObject("{ 'uriProperty': null }"); 133 | 134 | // when 135 | var deserialized = this.serializer.Deserialize((JToken)raw); 136 | 137 | // then 138 | Assert.Null(deserialized.UriProperty); 139 | } 140 | 141 | [Fact] 142 | public void Should_deserialize_when_entity_serializer_was_created_with_paremterless_constructor() 143 | { 144 | // given 145 | dynamic raw = JsonConvert.DeserializeObject("{ 'uriProperty': null }"); 146 | 147 | // when 148 | var deserialized = new EntitySerializer().Deserialize((JToken)raw); 149 | 150 | // then 151 | Assert.Null(deserialized.UriProperty); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/JsonLD.Entities/ContextResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using Dynamitey; 7 | using Microsoft.CSharp.RuntimeBinder; 8 | using Newtonsoft.Json.Linq; 9 | using NullGuard; 10 | 11 | namespace JsonLD.Entities 12 | { 13 | /// 14 | /// Resolves a @context for entity types 15 | /// 16 | public sealed class ContextResolver 17 | { 18 | private readonly IContextProvider contextProvider; 19 | 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | /// The context provider. 24 | public ContextResolver(IContextProvider contextProvider) 25 | { 26 | this.contextProvider = contextProvider; 27 | } 28 | 29 | /// 30 | /// Gets @context for an entity type. 31 | /// 32 | [return: AllowNull] 33 | public JToken GetContext(Type type) 34 | { 35 | var context = this.GetContextFromProvider(type) ?? GetContextFromProperty(type); 36 | 37 | return EnsureContextType(context); 38 | } 39 | 40 | /// 41 | /// Gets @context for an entity instance. 42 | /// 43 | [return: AllowNull] 44 | public JToken GetContext(object entity) 45 | { 46 | var context = this.GetContextFromProvider(entity.GetType()) 47 | ?? GetContextFromMethod(entity) 48 | ?? GetContextFromProperty(entity.GetType()); 49 | 50 | return EnsureContextType(context); 51 | } 52 | 53 | private static JToken EnsureContextType(dynamic context) 54 | { 55 | if (context == null) 56 | { 57 | return null; 58 | } 59 | 60 | if (context is string) 61 | { 62 | return JToken.Parse(context); 63 | } 64 | 65 | if (context is JToken) 66 | { 67 | return context; 68 | } 69 | 70 | throw new InvalidOperationException(string.Format("Invalid context type {0}. Must be string or JToken", context.GetType())); 71 | } 72 | 73 | private static dynamic GetContextFromProperty(Type type) 74 | { 75 | try 76 | { 77 | const string propertyName = "get_Context"; 78 | InvokeMemberName invokeArgs = propertyName; 79 | if (type.GetTypeInfo().IsGenericTypeDefinition) 80 | { 81 | var typeArgs = Enumerable.Repeat(typeof(object), type.GetGenericArguments().Length); 82 | type = type.MakeGenericType(typeArgs.ToArray()); 83 | } 84 | 85 | var staticContext = InvokeContext.CreateStatic; 86 | return Dynamic.InvokeGet(staticContext(type), "Context"); 87 | } 88 | catch (RuntimeBinderException) 89 | { 90 | return FallbackResolver.GetContext(type, null); 91 | } 92 | } 93 | 94 | private static dynamic GetContextFromMethod(object entity) 95 | { 96 | try 97 | { 98 | var staticContext = InvokeContext.CreateStatic; 99 | return Dynamic.InvokeMember(staticContext(entity.GetType()), "GetContext", entity); 100 | } 101 | catch (RuntimeBinderException) 102 | { 103 | return FallbackResolver.GetContext(entity.GetType(), entity); 104 | } 105 | } 106 | 107 | private JToken GetContextFromProvider(Type type) 108 | { 109 | var context = this.contextProvider.GetContext(type); 110 | 111 | if (context != null) 112 | { 113 | return context; 114 | } 115 | 116 | return null; 117 | } 118 | 119 | private static class FallbackResolver 120 | { 121 | private static readonly IDictionary> ContextCache 122 | = new ConcurrentDictionary>(); 123 | 124 | public static dynamic GetContext(Type type, [AllowNull] object instance) 125 | { 126 | if (!ContextCache.ContainsKey(type)) 127 | { 128 | var getContextMethod = type 129 | .GetMethod("GetContext", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); 130 | 131 | if (getContextMethod != null) 132 | { 133 | ContextCache.Add(type, e => getContextMethod.Invoke(null, new[] { e })); 134 | } 135 | else 136 | { 137 | var getContext = type 138 | .GetProperty("Context", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); 139 | 140 | if (getContext != null) 141 | { 142 | ContextCache.Add(type, _ => getContext.GetMethod.Invoke(null, null)); 143 | } 144 | else 145 | { 146 | ContextCache.Add(type, _ => null); 147 | } 148 | } 149 | } 150 | 151 | return ContextCache[type].Invoke(instance); 152 | } 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/Documentation/Serializing/WorkingWithURIs/Readme.mkd: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | ## URI values in JSON-LD 4 | 5 | In JSON-LD there are two ways to represent a IRI reference. The basic syntax is the expanded form, 6 | where the property value is actually a JSON object 7 | 8 | ``` json 9 | { 10 | "@context": { 11 | "property": "http://example.com/property" 12 | }, 13 | "property": { 14 | "@id": "http://example.com/value" 15 | } 16 | } 17 | ``` 18 | 19 | It is however possible to modify the `@context` so that the property value is simple string in JSON, 20 | while it represents the same triples. This technique is called [type coercion][coercion]. 21 | The above example can be simplified to 22 | 23 | ``` json 24 | { 25 | "@context": { 26 | "property": { 27 | "@id": "http://example.com/property", 28 | "@type": "@id" 29 | } 30 | }, 31 | "property": "http://example.com/value" 32 | } 33 | ``` 34 | 35 | When [`@context` is removed](http://tinyurl.com/hg6wfpu) both trees above are represented as 36 | 37 | ``` json 38 | { 39 | "http://example.com/property": { 40 | "@id": "http://example.com/value" 41 | } 42 | } 43 | ``` 44 | 45 | Notice how the definition of `property` changed in the `@context`. Otherwise the value `"http://example.com/value"` 46 | would be interpreted as simple string and [upon removing the context](http://tinyurl.com/jz7usbs) other RDF triples would be returned. 47 | 48 | ``` json 49 | { 50 | "http://example.com/property": "http://example.com/value" 51 | } 52 | ``` 53 | 54 | Let's see hot handle this in JsonLd.Entities: 55 | 56 | ``` c# 57 | using System; 58 | using JsonLD.Core; 59 | using JsonLD.Entities; 60 | using JsonLD.Entities.Context; 61 | using Newtonsoft.Json; 62 | using Newtonsoft.Json.Linq; 63 | using Xunit; 64 | 65 | public class SerializingUriProperties 66 | { 67 | private const string PropertyUri = "http://example.com/property"; 68 | private const string UriValue = "http://example.com/value"; 69 | 70 | readonly IEntitySerializer serializer = new EntitySerializer(); 71 | ``` 72 | 73 | ## Serializing URI properties 74 | 75 | ### Default behaviour (without `@context`) 76 | 77 | By default `System.Uri` values are serialized as strings. If there is no `@context`, the property must be mapped 78 | to an absolute URL using the `[JsonProperty]` attribute 79 | 80 | ``` c# 81 | private class UriPropertyMappedToAbsoluteUri 82 | { 83 | [JsonProperty(PropertyUri)] 84 | public Uri Property { get; set; } 85 | } 86 | 87 | [Fact] 88 | public void Should_serialize_URI_values_as_strings() 89 | { 90 | // given 91 | var serialized = this.serializer.Serialize(new UriPropertyMappedToAbsoluteUri { Property = new Uri(UriValue) }); 92 | 93 | // when 94 | // to remove @context, an empty JObject can be passed 95 | var noContext = JsonLdProcessor.Compact(serialized, new JObject(), new JsonLdOptions()); 96 | 97 | // then 98 | Assert.Equal(UriValue, noContext[PropertyUri].ToString()); 99 | } 100 | ``` 101 | 102 | This behaviour is by design, so that the resulting JSON object is as idiomatic as possible ou of the box. 103 | It is up to the consumer to supply proper `@context`, which ensures that [type coercion][coercion] is applied. 104 | 105 | ### Default property name (with `@context`) 106 | 107 | Instead of mapping a property to full predicate URI as in class `UriPropertyMappedToAbsoluteUri` above it is 108 | possible to define the mapping in the context so that default JSON serialization rules apply and have the context 109 | determins how values expand to RDF terms. 110 | 111 | ``` c# 112 | private class UriPropertyWithContext 113 | { 114 | public Uri Property { get; set; } 115 | 116 | public static JObject Context 117 | { 118 | get 119 | { 120 | return new JObject 121 | { 122 | "property".IsProperty(PropertyUri).Type().Id() 123 | }; 124 | } 125 | } 126 | } 127 | 128 | [Fact] 129 | public void Should_serialize_URI_values_as_strings_with_context() 130 | { 131 | // given 132 | var serialized = this.serializer.Serialize(new UriPropertyWithContext { Property = new Uri(UriValue) }); 133 | 134 | // when 135 | // to remove @context, an empty JObject can be passed 136 | var noContext = JsonLdProcessor.Compact(serialized, new JObject(), new JsonLdOptions()); 137 | 138 | // then 139 | Assert.Equal(UriValue, noContext[PropertyUri]["@id"].ToString()); 140 | } 141 | ``` 142 | 143 | ### Forcing serializing expanded URI 144 | 145 | There may be circumstances, such as with an externally supplied `@context`, that in may be necessary to 146 | ensure that the `Uri` value is serialized in it's expanded form. For that there is the `IriRef` struct. 147 | 148 | ``` c# 149 | private class UriPropertyForcedToExpand 150 | { 151 | public IriRef Property { get; set; } 152 | 153 | public static JObject Context 154 | { 155 | get 156 | { 157 | return new JObject 158 | { 159 | "property".IsProperty(PropertyUri) 160 | }; 161 | } 162 | } 163 | } 164 | 165 | [Fact] 166 | public void Should_serialize_URI_values_as_expanded_object() 167 | { 168 | // given 169 | var serialized = this.serializer.Serialize(new UriPropertyForcedToExpand { Property = new IriRef(UriValue) }); 170 | 171 | // when 172 | // to remove @context, an empty JObject can be passed 173 | var noContext = JsonLdProcessor.Compact(serialized, new JObject(), new JsonLdOptions()); 174 | 175 | // then 176 | Assert.Equal(UriValue, noContext[PropertyUri]["@id"].ToString()); 177 | } 178 | } 179 | ``` 180 | 181 | [coercion]: https://www.w3.org/TR/json-ld/#type-coercion 182 | -------------------------------------------------------------------------------- /src/Documentation/Serializing/WorkingWithURIs/Readme.cs: -------------------------------------------------------------------------------- 1 | /** 2 | # Documentation 3 | 4 | ## URI values in JSON-LD 5 | 6 | In JSON-LD there are two ways to represent a IRI reference. The basic syntax is the expanded form, 7 | where the property value is actually a JSON object 8 | 9 | ``` json 10 | { 11 | "@context": { 12 | "property": "http://example.com/property" 13 | }, 14 | "property": { 15 | "@id": "http://example.com/value" 16 | } 17 | } 18 | ``` 19 | 20 | It is however possible to modify the `@context` so that the property value is simple string in JSON, 21 | while it represents the same triples. This technique is called [type coercion][coercion]. 22 | The above example can be simplified to 23 | 24 | ``` json 25 | { 26 | "@context": { 27 | "property": { 28 | "@id": "http://example.com/property", 29 | "@type": "@id" 30 | } 31 | }, 32 | "property": "http://example.com/value" 33 | } 34 | ``` 35 | 36 | When [`@context` is removed](http://tinyurl.com/hg6wfpu) both trees above are represented as 37 | 38 | ``` json 39 | { 40 | "http://example.com/property": { 41 | "@id": "http://example.com/value" 42 | } 43 | } 44 | ``` 45 | 46 | Notice how the definition of `property` changed in the `@context`. Otherwise the value `"http://example.com/value"` 47 | would be interpreted as simple string and [upon removing the context](http://tinyurl.com/jz7usbs) other RDF triples would be returned. 48 | 49 | ``` json 50 | { 51 | "http://example.com/property": "http://example.com/value" 52 | } 53 | ``` 54 | 55 | Let's see hot handle this in JsonLd.Entities: 56 | **/ 57 | 58 | using System; 59 | using JsonLD.Core; 60 | using JsonLD.Entities; 61 | using JsonLD.Entities.Context; 62 | using Newtonsoft.Json; 63 | using Newtonsoft.Json.Linq; 64 | using Xunit; 65 | 66 | public class SerializingUriProperties 67 | { 68 | private const string PropertyUri = "http://example.com/property"; 69 | private const string UriValue = "http://example.com/value"; 70 | 71 | readonly IEntitySerializer serializer = new EntitySerializer(); 72 | 73 | /** 74 | 75 | ## Serializing URI properties 76 | 77 | ### Default behaviour (without `@context`) 78 | 79 | By default `System.Uri` values are serialized as strings. If there is no `@context`, the property must be mapped 80 | to an absolute URL using the `[JsonProperty]` attribute 81 | **/ 82 | 83 | private class UriPropertyMappedToAbsoluteUri 84 | { 85 | [JsonProperty(PropertyUri)] 86 | public Uri Property { get; set; } 87 | } 88 | 89 | [Fact] 90 | public void Should_serialize_URI_values_as_strings() 91 | { 92 | // given 93 | var serialized = this.serializer.Serialize(new UriPropertyMappedToAbsoluteUri { Property = new Uri(UriValue) }); 94 | 95 | // when 96 | // to remove @context, an empty JObject can be passed 97 | var noContext = JsonLdProcessor.Compact(serialized, new JObject(), new JsonLdOptions()); 98 | 99 | // then 100 | Assert.Equal(UriValue, noContext[PropertyUri].ToString()); 101 | } 102 | 103 | /** 104 | 105 | This behaviour is by design, so that the resulting JSON object is as idiomatic as possible ou of the box. 106 | It is up to the consumer to supply proper `@context`, which ensures that [type coercion][coercion] is applied. 107 | 108 | ### Default property name (with `@context`) 109 | 110 | Instead of mapping a property to full predicate URI as in class `UriPropertyMappedToAbsoluteUri` above it is 111 | possible to define the mapping in the context so that default JSON serialization rules apply and have the context 112 | determins how values expand to RDF terms. 113 | 114 | **/ 115 | 116 | private class UriPropertyWithContext 117 | { 118 | public Uri Property { get; set; } 119 | 120 | public static JObject Context 121 | { 122 | get 123 | { 124 | return new JObject 125 | { 126 | "property".IsProperty(PropertyUri).Type().Id() 127 | }; 128 | } 129 | } 130 | } 131 | 132 | [Fact] 133 | public void Should_serialize_URI_values_as_strings_with_context() 134 | { 135 | // given 136 | var serialized = this.serializer.Serialize(new UriPropertyWithContext { Property = new Uri(UriValue) }); 137 | 138 | // when 139 | // to remove @context, an empty JObject can be passed 140 | var noContext = JsonLdProcessor.Compact(serialized, new JObject(), new JsonLdOptions()); 141 | 142 | // then 143 | Assert.Equal(UriValue, noContext[PropertyUri]["@id"].ToString()); 144 | } 145 | 146 | /** 147 | 148 | ### Forcing serializing expanded URI 149 | 150 | There may be circumstances, such as with an externally supplied `@context`, that in may be necessary to 151 | ensure that the `Uri` value is serialized in it's expanded form. For that there is the `IriRef` struct. 152 | 153 | **/ 154 | 155 | private class UriPropertyForcedToExpand 156 | { 157 | public IriRef Property { get; set; } 158 | 159 | public static JObject Context 160 | { 161 | get 162 | { 163 | return new JObject 164 | { 165 | "property".IsProperty(PropertyUri) 166 | }; 167 | } 168 | } 169 | } 170 | 171 | [Fact] 172 | public void Should_serialize_URI_values_as_expanded_object() 173 | { 174 | // given 175 | var serialized = this.serializer.Serialize(new UriPropertyForcedToExpand { Property = new IriRef(UriValue) }); 176 | 177 | // when 178 | // to remove @context, an empty JObject can be passed 179 | var noContext = JsonLdProcessor.Compact(serialized, new JObject(), new JsonLdOptions()); 180 | 181 | // then 182 | Assert.Equal(UriValue, noContext[PropertyUri]["@id"].ToString()); 183 | } 184 | } 185 | 186 | /** 187 | [coercion]: https://www.w3.org/TR/json-ld/#type-coercion 188 | **/ -------------------------------------------------------------------------------- /src/Documentation/Readme.cs: -------------------------------------------------------------------------------- 1 | /** 2 | # Documentation 3 | 4 | ## About the examples 5 | 6 | All examples in the JsonLd.Docu project and its subfolders are generated from actual code files. Each folder contains a Readme.cs file, 7 | which is parsed and converted into a Readme.md file so that it's nicely viewable on GitHub. Each example is a compiled and runnable unit 8 | test, which you can try out and debug in the solution. 9 | 10 | ## Basic usage 11 | 12 | First let's import the required namespaces. 13 | **/ 14 | 15 | using System; 16 | using System.Collections.Generic; 17 | using JsonLD.Entities; 18 | using Newtonsoft.Json.Linq; 19 | using Xunit; 20 | 21 | /** 22 | ### Deserialization 23 | 24 | #### Deserializing the model as-is 25 | 26 | The easiest operation possible is to deserialize a JSON-LD object without any changes. The example models will be deserialized to 27 | instances of a Person class. 28 | **/ 29 | 30 | public class Person 31 | { 32 | public Uri Id { get; set; } 33 | 34 | public string Name { get; set; } 35 | 36 | public string LastName { get; set; } 37 | 38 | public IEnumerable Types 39 | { 40 | get { yield return new Uri("http://xmlns.com/foaf/0.1/Person"); } 41 | } 42 | } 43 | 44 | /** 45 | Serialization and deserialization is done by instances of `IEntitySerializer`. It's default implementation requires you to pass a 46 | IContextProvider, which provides [@context][jsonld-context] objects for serialized types. Because, we don't want to use a context in the 47 | first test, the IContextProvider object won't be set up in any way. 48 | 49 | Note how the JSON-LD `@id` is by convention deserialized to the `Person#Id` property. 50 | **/ 51 | 52 | public class Deserialization 53 | { 54 | 55 | [Fact] 56 | public void Can_deserialize_with_existing_structure() 57 | { 58 | // given 59 | var json = JObject.Parse(@" 60 | { 61 | '@context': { 62 | 'foaf': 'http://xmlns.com/foaf/0.1/', 63 | 'name': 'foaf:name', 64 | 'lastName': 'foaf:familyName', 65 | 'Person': 'foaf:Person' 66 | }, 67 | '@id': 'http://t-code.pl/#tomasz', 68 | '@type': 'Person', 69 | 'name': 'Tomasz', 70 | 'lastName': 'Pluskiewicz' 71 | }"); 72 | 73 | // when 74 | IEntitySerializer serializer = new EntitySerializer(new StaticContextProvider()); 75 | var person = serializer.Deserialize(json); 76 | 77 | // then 78 | Assert.Equal("Tomasz", person.Name); 79 | Assert.Equal("Pluskiewicz", person.LastName); 80 | Assert.Equal(new Uri("http://t-code.pl/#tomasz"), person.Id); 81 | } 82 | 83 | /** 84 | #### Deserialize with specific @context 85 | 86 | Oftentimes, like in public API, you could receive models, which do not conform to some specific JSON structure. With JSON-LD it is possible, 87 | becuase any document can be represented in numerous equivalent ways. For that purpose the [specification][jsonld-spec] defines a set of 88 | [algorithms][jsonld-api], which can transform a JSON-LD document between those representations. 89 | 90 | Below example shows how the default `IContextProvider` is used to adjust the document strucuture before deserializing. Perceptive reader 91 | would have probably noticed already that the `@context` must conform to model's properties. Pascal case in c#, camel case in JSON. 92 | **/ 93 | 94 | [Fact] 95 | public void Can_deserialize_with_changed_context() 96 | { 97 | // given 98 | var expanded = JObject.Parse(@" 99 | { 100 | '@id': 'http://t-code.pl/#tomasz', 101 | '@type': 'http://xmlns.com/foaf/0.1/Person', 102 | 'http://xmlns.com/foaf/0.1/name': 'Tomasz', 103 | 'http://xmlns.com/foaf/0.1/familyName': 'Pluskiewicz' 104 | }"); 105 | 106 | var @context = JObject.Parse(@" 107 | { 108 | 'foaf': 'http://xmlns.com/foaf/0.1/', 109 | 'name': 'foaf:name', 110 | 'lastName': 'foaf:familyName', 111 | 'Person': 'foaf:Person' 112 | }"); 113 | 114 | var contextProvider = new StaticContextProvider(); 115 | contextProvider.SetContext(typeof(Person), @context); 116 | 117 | // when 118 | IEntitySerializer serializer = new EntitySerializer(contextProvider); 119 | var person = serializer.Deserialize(expanded); 120 | 121 | // then 122 | Assert.Equal("Tomasz", person.Name); 123 | Assert.Equal("Pluskiewicz", person.LastName); 124 | Assert.Equal(new Uri("http://t-code.pl/#tomasz"), person.Id); 125 | } 126 | } 127 | 128 | /** 129 | ### Serialization 130 | 131 | Of course it also possible to serialize POCO objects to JSON-LD objects. 132 | **/ 133 | 134 | public class Serialization 135 | { 136 | 137 | [Fact] 138 | public void Can_serialize_object_to_JSON_LD() 139 | { 140 | // given 141 | var person = new Person 142 | { 143 | Id = new Uri("http://t-code.pl/#tomasz"), 144 | Name = "Tomasz", 145 | LastName = "Pluskiewicz" 146 | }; 147 | 148 | // when 149 | IEntitySerializer serializer = new EntitySerializer(); 150 | dynamic json = serializer.Serialize(person); 151 | 152 | // then 153 | Assert.Equal("Tomasz", (string)json.name); 154 | Assert.Equal("Pluskiewicz", (string)json.lastName); 155 | Assert.Equal("http://t-code.pl/#tomasz", (string)json["@id"]); 156 | Assert.Equal("http://xmlns.com/foaf/0.1/Person", (string)json["@type"][0]); 157 | } 158 | } 159 | 160 | /** 161 | [playground]: http://json-ld.org/playground/ 162 | [jsonld-spec]: http://json-ld.org/spec/latest/json-ld/ 163 | [jsonld-api]: http://www.w3.org/TR/json-ld-api/ 164 | [jsonld]: http://json-ld.org 165 | [rdf]: http://en.wikipedia.org/wiki/Resource_Description_Framework 166 | [readme]: http://github.com/wikibus/JsonLD.Entities/blob/master/src/JsonLD.Docu/Readme.cs 167 | [jsonld-context]: http://www.w3.org/TR/json-ld/#the-context 168 | **/ -------------------------------------------------------------------------------- /src/Documentation/Readme.mkd: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | ## About the examples 4 | 5 | All examples in the JsonLd.Docu project and its subfolders are generated from actual code files. Each folder contains a Readme.cs file, 6 | which is parsed and converted into a Readme.md file so that it's nicely viewable on GitHub. Each example is a compiled and runnable unit 7 | test, which you can try out and debug in the solution. 8 | 9 | ## Basic usage 10 | 11 | First let's import the required namespaces. 12 | 13 | 14 | ``` c# 15 | using System; 16 | using System.Collections.Generic; 17 | using JsonLD.Entities; 18 | using Newtonsoft.Json.Linq; 19 | using Xunit; 20 | ``` 21 | 22 | ### Deserialization 23 | 24 | #### Deserializing the model as-is 25 | 26 | The easiest operation possible is to deserialize a JSON-LD object without any changes. The example models will be deserialized to 27 | instances of a Person class. 28 | 29 | 30 | ``` c# 31 | public class Person 32 | { 33 | public Uri Id { get; set; } 34 | 35 | public string Name { get; set; } 36 | 37 | public string LastName { get; set; } 38 | 39 | public IEnumerable Types 40 | { 41 | get { yield return new Uri("http://xmlns.com/foaf/0.1/Person"); } 42 | } 43 | } 44 | ``` 45 | 46 | Serialization and deserialization is done by instances of `IEntitySerializer`. It's default implementation requires you to pass a 47 | IContextProvider, which provides [@context][jsonld-context] objects for serialized types. Because, we don't want to use a context in the 48 | first test, the IContextProvider object won't be set up in any way. 49 | 50 | Note how the JSON-LD `@id` is by convention deserialized to the `Person#Id` property. 51 | 52 | ``` c# 53 | public class Deserialization 54 | { 55 | 56 | [Fact] 57 | public void Can_deserialize_with_existing_structure() 58 | { 59 | // given 60 | var json = JObject.Parse(@" 61 | { 62 | '@context': { 63 | 'foaf': 'http://xmlns.com/foaf/0.1/', 64 | 'name': 'foaf:name', 65 | 'lastName': 'foaf:familyName', 66 | 'Person': 'foaf:Person' 67 | }, 68 | '@id': 'http://t-code.pl/#tomasz', 69 | '@type': 'Person', 70 | 'name': 'Tomasz', 71 | 'lastName': 'Pluskiewicz' 72 | }"); 73 | 74 | // when 75 | IEntitySerializer serializer = new EntitySerializer(new StaticContextProvider()); 76 | var person = serializer.Deserialize(json); 77 | 78 | // then 79 | Assert.Equal("Tomasz", person.Name); 80 | Assert.Equal("Pluskiewicz", person.LastName); 81 | Assert.Equal(new Uri("http://t-code.pl/#tomasz"), person.Id); 82 | } 83 | ``` 84 | 85 | #### Deserialize with specific @context 86 | 87 | Oftentimes, like in public API, you could receive models, which do not conform to some specific JSON structure. With JSON-LD it is possible, 88 | becuase any document can be represented in numerous equivalent ways. For that purpose the [specification][jsonld-spec] defines a set of 89 | [algorithms][jsonld-api], which can transform a JSON-LD document between those representations. 90 | 91 | Below example shows how the default `IContextProvider` is used to adjust the document strucuture before deserializing. Perceptive reader 92 | would have probably noticed already that the `@context` must conform to model's properties. Pascal case in c#, camel case in JSON. 93 | 94 | ``` c# 95 | [Fact] 96 | public void Can_deserialize_with_changed_context() 97 | { 98 | // given 99 | var expanded = JObject.Parse(@" 100 | { 101 | '@id': 'http://t-code.pl/#tomasz', 102 | '@type': 'http://xmlns.com/foaf/0.1/Person', 103 | 'http://xmlns.com/foaf/0.1/name': 'Tomasz', 104 | 'http://xmlns.com/foaf/0.1/familyName': 'Pluskiewicz' 105 | }"); 106 | 107 | var @context = JObject.Parse(@" 108 | { 109 | 'foaf': 'http://xmlns.com/foaf/0.1/', 110 | 'name': 'foaf:name', 111 | 'lastName': 'foaf:familyName', 112 | 'Person': 'foaf:Person' 113 | }"); 114 | 115 | var contextProvider = new StaticContextProvider(); 116 | contextProvider.SetContext(typeof(Person), @context); 117 | 118 | // when 119 | IEntitySerializer serializer = new EntitySerializer(contextProvider); 120 | var person = serializer.Deserialize(expanded); 121 | 122 | // then 123 | Assert.Equal("Tomasz", person.Name); 124 | Assert.Equal("Pluskiewicz", person.LastName); 125 | Assert.Equal(new Uri("http://t-code.pl/#tomasz"), person.Id); 126 | } 127 | } 128 | ``` 129 | 130 | ### Serialization 131 | 132 | Of course it also possible to serialize POCO objects to JSON-LD objects. 133 | 134 | ``` c# 135 | public class Serialization 136 | { 137 | 138 | [Fact] 139 | public void Can_serialize_object_to_JSON_LD() 140 | { 141 | // given 142 | var person = new Person 143 | { 144 | Id = new Uri("http://t-code.pl/#tomasz"), 145 | Name = "Tomasz", 146 | LastName = "Pluskiewicz" 147 | }; 148 | 149 | // when 150 | IEntitySerializer serializer = new EntitySerializer(); 151 | dynamic json = serializer.Serialize(person); 152 | 153 | // then 154 | Assert.Equal("Tomasz", (string)json.name); 155 | Assert.Equal("Pluskiewicz", (string)json.lastName); 156 | Assert.Equal("http://t-code.pl/#tomasz", (string)json["@id"]); 157 | Assert.Equal("http://xmlns.com/foaf/0.1/Person", (string)json["@type"][0]); 158 | } 159 | } 160 | ``` 161 | 162 | [playground]: http://json-ld.org/playground/ 163 | [jsonld-spec]: http://json-ld.org/spec/latest/json-ld/ 164 | [jsonld-api]: http://www.w3.org/TR/json-ld-api/ 165 | [jsonld]: http://json-ld.org 166 | [rdf]: http://en.wikipedia.org/wiki/Resource_Description_Framework 167 | [readme]: http://github.com/wikibus/JsonLD.Entities/blob/master/src/JsonLD.Docu/Readme.cs 168 | [jsonld-context]: http://www.w3.org/TR/json-ld/#the-context 169 | -------------------------------------------------------------------------------- /src/Documentation/Deserializing/LiteralValues/Readme.cs: -------------------------------------------------------------------------------- 1 | /** 2 | # Documentation 3 | 4 | ## Working with literal values 5 | 6 | As always, here are the required namespace imports. 7 | **/ 8 | 9 | using System; 10 | using System.Net; 11 | using JsonLD.Entities; 12 | using JsonLD.Entities.Converters; 13 | using Newtonsoft.Json; 14 | using Newtonsoft.Json.Linq; 15 | using Xunit; 16 | 17 | /** 18 | ### Deserialize expanded form of literals 19 | 20 | JSON-LD have two ways of explicitly representing typed literal. They can be typed in the `@context`, in which case the JSON property value is 21 | a simple string token. 22 | 23 | ``` js 24 | { 25 | "@context": { 26 | "age": { 27 | "@id": "http://example.com/onto#age", 28 | "@type": "http://www.w3.org/2001/XMLSchema#integer" 29 | } 30 | }, 31 | "age": "28" 32 | } 33 | ``` 34 | 35 | The other way is to directly state the value's type by expanding the literal to JSON object. See how the `@type` is moved from the 36 | `@context` and a `@value` keyword is introduced. 37 | 38 | ``` js 39 | { 40 | "@context": { 41 | "age": { 42 | "@id": "http://example.com/onto#age" 43 | } 44 | }, 45 | "age": { 46 | "@value": "28", 47 | "@type": "http://www.w3.org/2001/XMLSchema#integer" 48 | } 49 | } 50 | ``` 51 | 52 | The two examples above may be equivalent but the second one, which you will probably commonly encounter when working with JSON-LD, poses a 53 | problem for default Newtonsoft.Json serializer, because JSON object is not expected for primitive type such as `int` or `bool`. 54 | **/ 55 | 56 | public class DeserializationOfLiterals 57 | { 58 | 59 | /** 60 | JsonLd.Entities will try to deserialize the `@value` instead. Note however that if the actual type and C# property types don't match a 61 | round trip deserialization and serialization could produce different JSON-LD. 62 | **/ 63 | 64 | public class PersonWithAge 65 | { 66 | public Uri Id { get; set; } 67 | 68 | public long Age { get; set; } 69 | } 70 | 71 | [Fact] 72 | public void Can_deserialize_expanded_literal() 73 | { 74 | // given 75 | var jsonLd = JObject.Parse(@" 76 | { 77 | '@context': { 78 | 'age': { 79 | '@id': 'http://example.com/onto#age' 80 | } 81 | }, 82 | 'age': { 83 | '@value': '28', 84 | '@type': 'http://www.w3.org/2001/XMLSchema#integer' 85 | } 86 | }"); 87 | 88 | // when 89 | IEntitySerializer serializer = new EntitySerializer(new StaticContextProvider()); 90 | var person = serializer.Deserialize(jsonLd); 91 | 92 | // then 93 | Assert.Equal(28, person.Age); 94 | } 95 | 96 | /** 97 | ### Using custom converter to serialize class instance as literal 98 | 99 | Of course if is also possible to use Newtonsoft.Json's attributes to define custom converter for a property. For example we may want to use 100 | a custom converter to convert `IPAddress` to string values. 101 | **/ 102 | 103 | public class RequestLogItem 104 | { 105 | [JsonConverter(typeof(IPAddressConverter))] 106 | public IPAddress Ip { get; set; } 107 | } 108 | 109 | /** 110 | It's vital, that the converter is derived from `JsonLdLiteralConverter`. This way expanded literals are handled correctly. If it were 111 | derived from the deafult `JsonConverter` it would fail to deserialize such literals. 112 | **/ 113 | 114 | public class IPAddressConverter : JsonLdLiteralConverter 115 | { 116 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 117 | { 118 | writer.WriteValue(value.ToString()); 119 | } 120 | 121 | protected override object DeserializeLiteral(JsonReader reader, Type objectType, JsonSerializer serializer) 122 | { 123 | return IPAddress.Parse((string)reader.Value); 124 | } 125 | 126 | public override bool CanConvert(Type objectType) 127 | { 128 | return objectType == typeof(IPAddress); 129 | } 130 | } 131 | 132 | [Fact] 133 | public void Can_deserialize_class_from_literal() 134 | { 135 | // given 136 | var jsonLd = JObject.Parse(@" 137 | { 138 | '@context': { 139 | 'ip': { 140 | '@id': 'http://rdfs.org/sioc/ns#ip_address' 141 | } 142 | }, 143 | 'ip': '148.9.20.34' 144 | }"); 145 | 146 | // when 147 | IEntitySerializer serializer = new EntitySerializer(new StaticContextProvider()); 148 | var person = serializer.Deserialize(jsonLd); 149 | 150 | // then 151 | Assert.Equal(IPAddress.Parse("148.9.20.34"), person.Ip); 152 | } 153 | 154 | /** 155 | And it would also work if the literal was given in it's expanded form. 156 | **/ 157 | 158 | [Fact] 159 | public void Can_deserialize_class_from_expanded_literal() 160 | { 161 | // given 162 | var jsonLd = JObject.Parse(@" 163 | { 164 | '@context': { 165 | 'ip': { 166 | '@id': 'http://rdfs.org/sioc/ns#ip_address' 167 | } 168 | }, 169 | 'ip': { '@value': '148.9.20.34' } 170 | }"); 171 | 172 | // when 173 | IEntitySerializer serializer = new EntitySerializer(new StaticContextProvider()); 174 | var person = serializer.Deserialize(jsonLd); 175 | 176 | // then 177 | Assert.Equal(IPAddress.Parse("148.9.20.34"), person.Ip); 178 | } 179 | 180 | /** 181 | And lastly it is possible to serialize such an object to literal. Note that compacted value will always be produced, so it's important to 182 | create a fitting `@context` so that the JSON-LD document is valid and correct. 183 | **/ 184 | 185 | [Fact] 186 | public void Can_serialize_class_instance_to_literal() 187 | { 188 | // given 189 | var entity = new RequestLogItem { Ip = IPAddress.Parse("148.9.20.34") }; 190 | var contextProvider = new StaticContextProvider(); 191 | var context = JObject.Parse(@" 192 | { 193 | 'ip': { 194 | '@id': 'http://rdfs.org/sioc/ns#ip_address' 195 | } 196 | }"); 197 | contextProvider.SetContext(typeof(RequestLogItem), context); 198 | 199 | // when 200 | IEntitySerializer serializer = new EntitySerializer(contextProvider); 201 | dynamic jsonLd = serializer.Serialize(entity); 202 | 203 | // then 204 | Assert.Equal("148.9.20.34", (string)jsonLd.ip); 205 | } 206 | } -------------------------------------------------------------------------------- /src/Documentation/Deserializing/LiteralValues/Readme.mkd: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | ## Working with literal values 4 | 5 | As always, here are the required namespace imports. 6 | 7 | ``` c# 8 | using System; 9 | using System.Net; 10 | using JsonLD.Entities; 11 | using JsonLD.Entities.Converters; 12 | using Newtonsoft.Json; 13 | using Newtonsoft.Json.Linq; 14 | using Xunit; 15 | ``` 16 | 17 | ### Deserialize expanded form of literals 18 | 19 | JSON-LD have two ways of explicitly representing typed literal. They can be typed in the `@context`, in which case the JSON property value is 20 | a simple string token. 21 | 22 | ``` js 23 | { 24 | "@context": { 25 | "age": { 26 | "@id": "http://example.com/onto#age", 27 | "@type": "http://www.w3.org/2001/XMLSchema#integer" 28 | } 29 | }, 30 | "age": "28" 31 | } 32 | ``` 33 | 34 | The other way is to directly state the value's type by expanding the literal to JSON object. See how the `@type` is moved from the 35 | `@context` and a `@value` keyword is introduced. 36 | 37 | ``` js 38 | { 39 | "@context": { 40 | "age": { 41 | "@id": "http://example.com/onto#age" 42 | } 43 | }, 44 | "age": { 45 | "@value": "28", 46 | "@type": "http://www.w3.org/2001/XMLSchema#integer" 47 | } 48 | } 49 | ``` 50 | 51 | The two examples above may be equivalent but the second one, which you will probably commonly encounter when working with JSON-LD, poses a 52 | problem for default Newtonsoft.Json serializer, because JSON object is not expected for primitive type such as `int` or `bool`. 53 | 54 | ``` c# 55 | public class DeserializationOfLiterals 56 | { 57 | ``` 58 | 59 | JsonLd.Entities will try to deserialize the `@value` instead. Note however that if the actual type and C# property types don't match a 60 | round trip deserialization and serialization could produce different JSON-LD. 61 | 62 | ``` c# 63 | public class PersonWithAge 64 | { 65 | public Uri Id { get; set; } 66 | 67 | public long Age { get; set; } 68 | } 69 | 70 | [Fact] 71 | public void Can_deserialize_expanded_literal() 72 | { 73 | // given 74 | var jsonLd = JObject.Parse(@" 75 | { 76 | '@context': { 77 | 'age': { 78 | '@id': 'http://example.com/onto#age' 79 | } 80 | }, 81 | 'age': { 82 | '@value': '28', 83 | '@type': 'http://www.w3.org/2001/XMLSchema#integer' 84 | } 85 | }"); 86 | 87 | // when 88 | IEntitySerializer serializer = new EntitySerializer(new StaticContextProvider()); 89 | var person = serializer.Deserialize(jsonLd); 90 | 91 | // then 92 | Assert.Equal(28, person.Age); 93 | } 94 | ``` 95 | 96 | ### Using custom converter to serialize class instance as literal 97 | 98 | Of course if is also possible to use Newtonsoft.Json's attributes to define custom converter for a property. For example we may want to use 99 | a custom converter to convert `IPAddress` to string values. 100 | 101 | ``` c# 102 | public class RequestLogItem 103 | { 104 | [JsonConverter(typeof(IPAddressConverter))] 105 | public IPAddress Ip { get; set; } 106 | } 107 | ``` 108 | 109 | It's vital, that the converter is derived from `JsonLdLiteralConverter`. This way expanded literals are handled correctly. If it were 110 | derived from the deafult `JsonConverter` it would fail to deserialize such literals. 111 | 112 | ``` c# 113 | public class IPAddressConverter : JsonLdLiteralConverter 114 | { 115 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 116 | { 117 | writer.WriteValue(value.ToString()); 118 | } 119 | 120 | protected override object DeserializeLiteral(JsonReader reader, Type objectType, JsonSerializer serializer) 121 | { 122 | return IPAddress.Parse((string)reader.Value); 123 | } 124 | 125 | public override bool CanConvert(Type objectType) 126 | { 127 | return objectType == typeof(IPAddress); 128 | } 129 | } 130 | 131 | [Fact] 132 | public void Can_deserialize_class_from_literal() 133 | { 134 | // given 135 | var jsonLd = JObject.Parse(@" 136 | { 137 | '@context': { 138 | 'ip': { 139 | '@id': 'http://rdfs.org/sioc/ns#ip_address' 140 | } 141 | }, 142 | 'ip': '148.9.20.34' 143 | }"); 144 | 145 | // when 146 | IEntitySerializer serializer = new EntitySerializer(new StaticContextProvider()); 147 | var person = serializer.Deserialize(jsonLd); 148 | 149 | // then 150 | Assert.Equal(IPAddress.Parse("148.9.20.34"), person.Ip); 151 | } 152 | ``` 153 | 154 | And it would also work if the literal was given in it's expanded form. 155 | 156 | ``` c# 157 | [Fact] 158 | public void Can_deserialize_class_from_expanded_literal() 159 | { 160 | // given 161 | var jsonLd = JObject.Parse(@" 162 | { 163 | '@context': { 164 | 'ip': { 165 | '@id': 'http://rdfs.org/sioc/ns#ip_address' 166 | } 167 | }, 168 | 'ip': { '@value': '148.9.20.34' } 169 | }"); 170 | 171 | // when 172 | IEntitySerializer serializer = new EntitySerializer(new StaticContextProvider()); 173 | var person = serializer.Deserialize(jsonLd); 174 | 175 | // then 176 | Assert.Equal(IPAddress.Parse("148.9.20.34"), person.Ip); 177 | } 178 | ``` 179 | 180 | And lastly it is possible to serialize such an object to literal. Note that compacted value will always be produced, so it's important to 181 | create a fitting `@context` so that the JSON-LD document is valid and correct. 182 | 183 | ``` c# 184 | [Fact] 185 | public void Can_serialize_class_instance_to_literal() 186 | { 187 | // given 188 | var entity = new RequestLogItem { Ip = IPAddress.Parse("148.9.20.34") }; 189 | var contextProvider = new StaticContextProvider(); 190 | var context = JObject.Parse(@" 191 | { 192 | 'ip': { 193 | '@id': 'http://rdfs.org/sioc/ns#ip_address' 194 | } 195 | }"); 196 | contextProvider.SetContext(typeof(RequestLogItem), context); 197 | 198 | // when 199 | IEntitySerializer serializer = new EntitySerializer(contextProvider); 200 | dynamic jsonLd = serializer.Serialize(entity); 201 | 202 | // then 203 | Assert.Equal("148.9.20.34", (string)jsonLd.ip); 204 | } 205 | } 206 | ``` 207 | -------------------------------------------------------------------------------- /src/Documentation/CreatingContext/AutoContext/Readme.mkd: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | ## Building the `@context` shorthands 4 | 5 | The JSON-LD `@context` can be build manually from scratch, but in some cases 6 | it may be possible to reduce the context to some common base for each property 7 | and set it up accordingly. 8 | 9 | Currently there are two strategies for creating terms for properties. 10 | 11 | ### Class identifier as base 12 | 13 | First way is to concatenate the class identifier with property names. This can be done 14 | in two ways: 15 | 16 | 1. If the type is a hash URI, append to the hash fragment: 17 | 18 | `http://example.com/vocab#Person` -> `http://example.com/vocab#Person/propertyName` 19 | 20 | 1. Otherwise append the property name as hash fragment: 21 | 22 | `http://example.com/vocab/Person` -> `http://example.com/vocab/Person#propertyName` 23 | 24 | 25 | ``` c# 26 | using System; 27 | using JsonLD.Entities.Context; 28 | using Newtonsoft.Json; 29 | using Newtonsoft.Json.Linq; 30 | using Xunit; 31 | 32 | public class AutomaticContextBuilding 33 | { 34 | private class Person 35 | { 36 | private static string Type => "http://example.com/vocab/Person"; 37 | 38 | public string Name { get; set; } 39 | 40 | // also works with custom property names 41 | [JsonProperty("lastName")] 42 | public string Surname { get; set; } 43 | 44 | public static JObject Context => new AutoContext(); 45 | } 46 | 47 | [Fact] 48 | public void Context_can_be_built_from_type_id() 49 | { 50 | // given 51 | dynamic context = Person.Context; 52 | 53 | // then 54 | Assert.Equal("http://example.com/vocab/Person#name", (string)context.name); 55 | Assert.Equal("http://example.com/vocab/Person#lastName", (string)context.lastName); 56 | } 57 | } 58 | ``` 59 | 60 | Note that is if the class type doesn't have a statically resolvable Type identifier 61 | (that is, using a static property or annotation), then it will be necessary to create 62 | `AutoContext` with a constructor which takes URL as one of its parameter. 63 | 64 | 65 | 66 | 67 | ### Vocabulary IRI as base 68 | 69 | Another way is to use a common vocabulary base URI to construct identifiers for class' 70 | properties. It is done similarly as above, with a `VocabContext` type. Property names 71 | will simply be concatenated with it. 72 | 73 | ``` c# 74 | public class VocabularyBasedContextBuilding 75 | { 76 | private class Person 77 | { 78 | public string Name { get; set; } 79 | 80 | // also works with custom property names 81 | [JsonProperty("lastName")] 82 | public string Surname { get; set; } 83 | 84 | public static JObject Context => new VocabContext("http://vocab.example.com/terms#"); 85 | } 86 | 87 | [Fact] 88 | public void Context_can_be_built_with_vocabulary_base() 89 | { 90 | // given 91 | dynamic context = Person.Context; 92 | 93 | // then 94 | Assert.Equal("http://vocab.example.com/terms#name", (string)context.name); 95 | Assert.Equal("http://vocab.example.com/terms#lastName", (string)context.lastName); 96 | } 97 | } 98 | ``` 99 | 100 | ## Modifying the automatic context 101 | 102 | It is possible to retrieve the generated mapping and modify it using the [context API][api]. 103 | 104 | ``` c# 105 | public class ModifyingAutoGeneratedContext 106 | { 107 | private class Multilanguage 108 | { 109 | public string Translations { get; set; } 110 | 111 | public static JObject Context 112 | { 113 | get 114 | { 115 | return new VocabContext("http://vocab.example.com/terms#") 116 | .Property(p => p.Translations, property => property.Type().Id().Container().Language()); 117 | } 118 | } 119 | } 120 | 121 | [Fact] 122 | public void Context_can_be_built_with_vocabulary_base() 123 | { 124 | // given 125 | var expected = @" 126 | { 127 | 'translations': { 128 | '@id': 'http://vocab.example.com/terms#translations', 129 | '@type': '@id', 130 | '@container': '@language' 131 | } 132 | }"; 133 | 134 | // when 135 | dynamic context = Multilanguage.Context; 136 | 137 | // then 138 | Assert.True(JToken.DeepEquals(context, JObject.Parse(expected)), $"Actual context was {context}"); 139 | } 140 | } 141 | ``` 142 | 143 | ## Custom automatic context 144 | 145 | If you ever find the need to implement a different logic for generating the 146 | `@context` you can derive from the abstract [`AutoContextBase`][acb] class and implement 147 | an `AutoContextStrategy`. 148 | 149 | ``` c# 150 | public class SplitPersonalityAutoContext : AutoContextBase 151 | { 152 | private const string Base = "http://example.com/vocab#"; 153 | 154 | public SplitPersonalityAutoContext() 155 | : base(new SplittingStrategy()) 156 | { 157 | } 158 | 159 | public SplitPersonalityAutoContext(JObject context) 160 | : base(context, new SplittingStrategy()) 161 | { 162 | } 163 | 164 | /// 165 | /// Reverses the property name, if is starts with a letter a-m 166 | /// 167 | private class SplittingStrategy : AutoContextStrategy 168 | { 169 | protected override string GetPropertyId(string propertyName) 170 | { 171 | if (propertyName[0] <= 'm') 172 | { 173 | var charArray = propertyName.ToCharArray(); 174 | Array.Reverse(charArray); 175 | propertyName = new string(charArray); 176 | } 177 | 178 | return Base + propertyName; 179 | } 180 | } 181 | } 182 | ``` 183 | 184 | ### Extending an existing context 185 | 186 | The `AutoContextBase` type and its default implementation have constructors, which take 187 | a `JObject` parameter. When provided, any existing properties already declared will not be 188 | overridden by those generated. 189 | 190 | [acb]: https://github.com/wikibus/JsonLD.Entities/blob/master/src/JsonLD.Entities/Context/AutoContextBase.cs 191 | [api]: /wikibus/JsonLD.Entities/tree/master/src/Documentation/CreatingContext/FluentContext 192 | --------------------------------------------------------------------------------