diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..c9c342f7
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,47 @@
+version: 2
+updates:
+- package-ecosystem: nuget
+ labels: [dependency]
+ groups:
+ microsoft:
+ patterns: [Microsoft.*, System.*]
+ xunit:
+ patterns: [xunit.*]
+ directory: /
+ schedule:
+ interval: monthly
+ target-branch: main
+
+- package-ecosystem: nuget
+ labels: [dependency, support]
+ groups:
+ microsoft:
+ patterns: [Microsoft.*, System.*]
+ xunit:
+ patterns: [xunit.*]
+ directory: /
+ schedule:
+ interval: monthly
+ target-branch: support/1.x
+ ignore:
+ - dependency-name: Newtonsoft.Json
+
+- package-ecosystem: github-actions
+ labels: [dependency]
+ groups:
+ actions:
+ patterns: [actions/*]
+ directory: /
+ schedule:
+ interval: monthly
+ target-branch: main
+
+- package-ecosystem: github-actions
+ labels: [dependency, support]
+ groups:
+ actions:
+ patterns: [actions/*]
+ directory: /
+ schedule:
+ interval: monthly
+ target-branch: support/1.x
diff --git a/.github/mergify.yml b/.github/mergify.yml
new file mode 100644
index 00000000..9e668864
--- /dev/null
+++ b/.github/mergify.yml
@@ -0,0 +1,26 @@
+pull_request_rules:
+ - name: Automatic approve on dependabot PR
+ conditions:
+ - author~=^dependabot(|-preview)\[bot\]$
+ - base=main
+ actions:
+ review:
+ type: APPROVE
+
+ - name: Automatic merge on approval
+ conditions:
+ - author~=^dependabot(|-preview)\[bot\]$
+ - '#commits-behind=0' # Only merge up to date pull requests
+ - check-success=build
+ - check-success=build & run tests
+ - base=main
+ actions:
+ merge:
+
+ - name: Thank contributor
+ conditions:
+ - merged
+ - -author~=^.*\[bot\]$
+ actions:
+ comment:
+ message: "Thank you for your contribution, @{{author}}!"
diff --git a/.github/stale.yml b/.github/stale.yml
new file mode 100644
index 00000000..5daeec95
--- /dev/null
+++ b/.github/stale.yml
@@ -0,0 +1,30 @@
+# Number of days of inactivity before an issue becomes stale
+daysUntilStale: 90
+
+# Number of days of inactivity before a stale issue is closed
+daysUntilClose: 30
+
+# Issues with these labels will never be considered stale
+exemptLabels:
+ - pinned
+ - security
+ - bug
+ - planning
+
+# Set to true to ignore issues in a milestone (defaults to false)
+exemptMilestones: true
+
+# Set to true to ignore issues with an assignee (defaults to false)
+exemptAssignees: true
+
+# Label to use when marking an issue as stale
+staleLabel: stale
+
+# Comment to post when marking an issue as stale. Set to `false` to disable
+markComment: >
+ This issue has been automatically marked as stale because it has not had
+ recent activity. After 30 days from now, it will be closed if no further
+ activity occurs. Thank you for your contributions.
+
+# Comment to post when closing a stale issue. Set to `false` to disable
+closeComment: false
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
new file mode 100644
index 00000000..ced83a5e
--- /dev/null
+++ b/.github/workflows/docker.yml
@@ -0,0 +1,21 @@
+name: docker
+
+on:
+ push:
+ branches: [main, support/*]
+ tags: ["*"]
+ pull_request:
+ branches: [main, support/*]
+
+jobs:
+ docker:
+ name: build & run tests
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: docker build
+ run: docker build -t json-ld.net .
+ - name: docker test
+ run: docker run --rm json-ld.net dotnet test
diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
new file mode 100644
index 00000000..2507c88a
--- /dev/null
+++ b/.github/workflows/dotnet.yml
@@ -0,0 +1,133 @@
+name: dotnet
+
+on:
+ push:
+ branches: [main, support/*]
+ tags: ["*"]
+ pull_request:
+ branches: [main, support/*]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ outputs:
+ fullSemVer: ${{ steps.gitversion.outputs.fullSemVer }}
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - uses: gittools/actions/gitversion/setup@v3.1.11
+ with:
+ versionSpec: 5.x
+
+ - id: gitversion
+ uses: gittools/actions/gitversion/execute@v3.1.11
+
+ - uses: actions/setup-dotnet@v4.3.1
+ with:
+ dotnet-version: 6.0.x
+
+ - uses: actions/cache@v4
+ env:
+ NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
+ with:
+ path: ~/.nuget/packages
+ key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
+ restore-keys: ${{ runner.os }}-nuget-
+
+ - run: dotnet restore
+
+ - run: dotnet build --configuration Release --no-restore
+
+ - run: |
+ dotnet test \
+ --configuration Release \
+ --no-build \
+ --no-restore \
+ -p:CollectCoverage=true \
+ -p:CoverletOutputFormat=opencover \
+ -p:Exclude="[JsonLD.Test*]*"
+
+ - name: Codecov
+ env:
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+ run: bash <(curl -s https://codecov.io/bash)
+
+ - run: |
+ dotnet pack \
+ --include-source \
+ --configuration Release \
+ --no-build \
+ --no-restore \
+ -p:PackageVersion="${{ steps.gitversion.outputs.fullSemVer }}" \
+ src/json-ld.net/json-ld.net.csproj \
+ --output ${{ github.workspace }}/nugets/
+
+ - uses: actions/upload-artifact@v4
+ with:
+ name: nugets
+ path: nugets
+
+ nuget-push-dev:
+ runs-on: ubuntu-latest
+ if: github.ref == 'refs/heads/main'
+ needs: build
+
+ steps:
+ - name: download artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: nugets
+
+ - name: setup dotnet
+ uses: actions/setup-dotnet@v4.3.1
+ with:
+ dotnet-version: 6.0.x
+ source-url: https://nuget.pkg.github.com/linked-data-dotnet/index.json
+ env:
+ NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: nuget push
+ run: dotnet nuget push "*.nupkg" --skip-duplicate --api-key ${{ secrets.GITHUB_TOKEN }}
+
+ nuget-push-prod:
+ runs-on: ubuntu-latest
+ if: startsWith(github.ref, 'refs/tags/')
+ needs: build
+
+ steps:
+ - uses: actions/download-artifact@v4
+ with:
+ name: nugets
+
+ - uses: actions/setup-dotnet@v4.3.1
+ with:
+ dotnet-version: 6.0.x
+ source-url: https://api.nuget.org/v3/index.json
+ env:
+ NUGET_AUTH_TOKEN: ${{ secrets.NUGET_API_KEY }}
+
+ - name: nuget push
+ run: dotnet nuget push "*.nupkg" --skip-duplicate --api-key ${{ secrets.NUGET_API_KEY }}
+
+ release-artifacts:
+ runs-on: ubuntu-latest
+ needs: build
+ if: startsWith(github.ref, 'refs/tags/')
+
+ steps:
+ - uses: actions/download-artifact@v4
+ with:
+ name: nugets
+
+ - name: Upload to stable release
+ uses: svenstaro/upload-release-action@2.9.0
+ with:
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+ file: nugets
+ asset_name: json-ld.net
+ tag: ${{ github.ref }}
+ overwrite: true
diff --git a/.gitignore b/.gitignore
index 764c8aa4..3f27ed22 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,6 +31,7 @@ build/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
+*opencover.xml
*_i.c
*_p.c
@@ -166,4 +167,7 @@ $RECYCLE.BIN/
.DS_Store
# NuGet package store
-packages/
\ No newline at end of file
+packages/
+
+# Jetbrains Rider things.
+.idea/**
\ No newline at end of file
diff --git a/CODEOWNERS b/CODEOWNERS
new file mode 100644
index 00000000..5629666f
--- /dev/null
+++ b/CODEOWNERS
@@ -0,0 +1 @@
+* @asbjornu @goofballlogic @tpluscode
diff --git a/Directory.Build.targets b/Directory.Build.targets
new file mode 100644
index 00000000..eca789cf
--- /dev/null
+++ b/Directory.Build.targets
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..5fcb9c66
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,20 @@
+# If you want to build and test under Linux using a docker container, here's how:
+#
+#> docker build -t json-ld.net .
+#> docker run --rm json-ld.net dotnet test -v normal
+
+# .NET Core 6.0 on Ubuntu 20.04 LTS
+FROM mcr.microsoft.com/dotnet/sdk:6.0-focal
+
+WORKDIR /App
+
+# First we ONLY copy sln and csproj files so that we don't have to re-cache
+# dotnet restore every time a .cs file changes
+COPY src/json-ld.net/json-ld.net.csproj src/json-ld.net/json-ld.net.csproj
+COPY test/json-ld.net.tests/json-ld.net.tests.csproj test/json-ld.net.tests/json-ld.net.tests.csproj
+COPY JsonLD.sln JsonLD.sln
+RUN dotnet restore
+
+# Then we copy everything and run dotnet build
+COPY . .
+RUN dotnet build
diff --git a/JsonLD.sln b/JsonLD.sln
index 05284e85..cdef61d5 100644
--- a/JsonLD.sln
+++ b/JsonLD.sln
@@ -1,11 +1,20 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.25123.0
+# Visual Studio 15
+VisualStudioVersion = 15.0.28010.2026
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "json-ld.net", "src\json-ld.net\json-ld.net.xproj", "{E1AB2A29-D1E4-45A1-9076-8255916F5693}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "json-ld.net", "src\json-ld.net\json-ld.net.csproj", "{E1AB2A29-D1E4-45A1-9076-8255916F5693}"
EndProject
-Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "json-ld.net.tests", "test\json-ld.net.tests\json-ld.net.tests.xproj", "{05CBE0E2-FBD2-40D1-BD9A-D30BD7ACF219}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "json-ld.net.tests", "test\json-ld.net.tests\json-ld.net.tests.csproj", "{05CBE0E2-FBD2-40D1-BD9A-D30BD7ACF219}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8D83EC18-10BB-4C6E-A34A-AC183AD6D814}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{8010CF28-BCB3-4DC2-901F-3118B2AAD142}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{4B8ED350-355A-4D30-9F63-B13FBFDBD9E8}"
+ProjectSection(SolutionItems) = preProject
+ .gitignore = .gitignore
+EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -25,4 +34,11 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {F10834B6-ACA3-4C86-892B-368D0B68ED83}
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {E1AB2A29-D1E4-45A1-9076-8255916F5693} = {8D83EC18-10BB-4C6E-A34A-AC183AD6D814}
+ {05CBE0E2-FBD2-40D1-BD9A-D30BD7ACF219} = {8010CF28-BCB3-4DC2-901F-3118B2AAD142}
+ EndGlobalSection
EndGlobal
diff --git a/README.md b/README.md
index a7b214db..05a63fb2 100644
--- a/README.md
+++ b/README.md
@@ -1,32 +1,712 @@
-json-ld.net
-==========
+# json-ld.net
-A JSON-LD processor for .NET.
+[![NuGet][nuget-badge]][nuget]
+[![Build Status][gha-badge]][build]
+[![codecov][codecov-badge]][codecov]
-v1.0.5 is available on NuGet.org at https://www.nuget.org/packages/json-ld.net/
+## Introduction
+This library is an implementation of the JSON-LD specification in C#.
-This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
+JSON, as specified in RFC7159, is a simple language for representing objects on
+the Web. Linked Data is a way of describing content across different documents
+or Web sites. Web resources are described using IRIs, and typically are
+dereferencable entities that may be used to find more information, creating a
+"Web of Knowledge". JSON-LD is intended to be a simple publishing method for
+expressing not only Linked Data in JSON, but for adding semantics to existing
+JSON.
-#### Supported frameworks
+JSON-LD is designed as a light-weight syntax that can be used to express Linked
+Data. It is primarily intended to be a way to express Linked Data in JavaScript
+and other Web-based programming environments. It is also useful when building
+interoperable Web Services and when storing Linked Data in JSON-based document
+storage engines. It is practical and designed to be as simple as possible,
+utilizing the large number of JSON parsers and existing code that is in use
+today. It is designed to be able to express key-value pairs, RDF data, RDFa
+data, Microformats data, and Microdata. That is, it supports every major
+Web-based structured data model in use today.
-* NET 4.0
-* NETStandard 1.3
-* Portable NET 4.5, Windows 8
+The syntax does not require many applications to change their JSON, but easily
+add meaning by adding context in a way that is either in-band or out-of-band.
+The syntax is designed to not disturb already deployed systems running on JSON,
+but provide a smooth migration path from plain JSON to semantically enhanced
+JSON. Finally, the format is intended to be fast to parse, fast to generate,
+stream-based and document-based processing compatible, and require a very small
+memory footprint in order to operate.
-Contributing
-=====
+You can read more about JSON-LD on the [JSON-LD website][jsonld].
-Pull requests for json-ld.net are welcome, to get started install the latest tools for .NET Core
+## Conformance
-* https://dot.net
-* https://www.microsoft.com/net/core
+This library aims to conform with the following:
-build.ps1 will create a nupkg and run tests for both .NET desktop and .NET Core.
+* [JSON-LD 1.0][json-ld-10], W3C Recommendation, 2014-01-16, and any
+[errata][errata]
+* [JSON-LD 1.0 Processing Algorithms and API][json-ld-10-api], W3C
+Recommendation, 2014-01-16, and any [errata][errata]
+* [JSON-LD 1.0 Framing][json-ld-10-framing], Unofficial Draft, 2012-08-30
+* Working Group [test suite][wg-test-suite]
+The [JSON-LD Working Group][json-ld-wg] is now developing JSON-LD 1.1. Library
+updates to conform with newer specifications will happen as features stabilize
+and development time and resources permit.
-Origin
-=====
-This project began life as a [Sharpen][sharpen]-based auto-port from [jsonld-java][jsonld-java].
+* [JSON-LD 1.1][json-ld-wg-11], W3C Working Draft, 2018-12-14 or
+[newer][json-ld-wg-latest]
+* [JSON-LD 1.1 Processing Algorithms and API][json-ld-wg-11-api], W3C Working
+Draft, 2018-12-14 or [newer][json-ld-wg-api-latest]
+* [JSON-LD 1.1 Framing][json-ld-wg-11-framing], W3C Working Draft, 2018-12-14
+or [newer][json-ld-wg-framing-latest]
- [sharpen]: http://community.versant.com/Projects/html/projectspaces/db4o_product_design/sharpen.html
- [jsonld-java]: https://github.com/jsonld-java/jsonld-java
+The [test runner][test-runner] is often updated to note or skip newer tests that
+are not yet supported.
+
+## Supported frameworks
+
+* .NET 4.0
+* .NET Standard 1.3 and 2.0
+* Portable .NET 4.5, Windows 8
+
+## Installation
+
+### dotnet CLI
+
+```sh
+dotnet new console
+dotnet add package json-ld.net
+```
+
+```csharp
+using JsonLD.Core;
+using Newtonsoft.Json.Linq;
+using System;
+
+namespace JsonLD.Demo
+{
+ internal class Program
+ {
+ private static void Main()
+ {
+ var json = "{'@context':{'test':'http://www.test.com/'},'test:hello':'world'}";
+ var document = JObject.Parse(json);
+ var expanded = JsonLdProcessor.Expand(document);
+ Console.WriteLine(expanded);
+ }
+ }
+}
+
+```
+
+## Examples
+--------
+
+Example data and context used throughout examples below:
+
+#### doc.json
+
+```json
+{
+ "@id": "http://example.org/ld-experts",
+ "http://schema.org/name": "LD Experts",
+ "http://schema.org/member": [{
+ "@type": "http://schema.org/Person",
+ "http://schema.org/name": "Manu Sporny",
+ "http://schema.org/url": {"@id": "http://manu.sporny.org/"},
+ "http://schema.org/image": {"@id": "http://manu.sporny.org/images/manu.png"}
+ }]
+}
+```
+
+#### context.json
+
+```json
+{
+ "name": "http://schema.org/name",
+ "member": "http://schema.org/member",
+ "homepage": {"@id": "http://schema.org/url", "@type": "@id"},
+ "image": {"@id": "http://schema.org/image", "@type": "@id"},
+ "Person": "http://schema.org/Person",
+ "@vocab": "http://example.org/",
+ "@base": "http://example.org/"
+}
+```
+
+### Compact
+
+[Compaction](http://json-ld.org/spec/latest/json-ld/#compacted-document-form) is
+the process of applying a developer-supplied context to shorten IRIs to terms or
+compact IRIs, and JSON-LD values expressed in expanded form to simple values
+such as strings or numbers. Often this makes it simpler to work with a document
+as the data is expressed in application-specific terms. Compacted documents are
+also typically easier to read for humans.
+
+```csharp
+var doc = JObject.Parse(_docJson);
+var context = JObject.Parse(_contextJson);
+var opts = new JsonLdOptions();
+var compacted = JsonLdProcessor.Compact(doc, context, opts);
+Console.WriteLine(compacted);
+
+/*
+
+Output:
+{
+ "@id": "ld-experts",
+ "member": {
+ "@type": "Person",
+ "image": "http://manu.sporny.org/images/manu.png",
+ "name": "Manu Sporny",
+ "homepage": "http://manu.sporny.org/"
+ },
+ "name": "LD Experts",
+ "@context": . . .
+}
+
+*/
+```
+
+### Expand
+
+
+[Exapansion](http://json-ld.org/spec/latest/json-ld/#expanded-document-form) is
+the process of taking a JSON-LD document and applying a @context such that all
+IRIs, types, and values are expanded so that the @context is no longer
+necessary.
+
+```csharp
+var expanded = JsonLdProcessor.Expand(compacted);
+Console.WriteLine(expanded);
+
+/*
+
+Output:
+[
+ {
+ "@id": "http://test.com/ld-experts",
+ "http://schema.org/member": [
+ {
+ "http://schema.org/url": [
+ {
+ "@id": "http://manu.sporny.org/"
+ }
+ ],
+ "http://schema.org/image": [
+ {
+ "@id": "http://manu.sporny.org/images/manu.png"
+ }
+ ],
+ "http://schema.org/name": [
+ {
+ "@value": "Manu Sporny"
+ }
+ ]
+ }
+ ],
+ "http://schema.org/name": [
+ {
+ "@value": "LD Experts"
+ }
+ ]
+ }
+]
+
+*/
+
+
+*/
+```
+
+### Flatten
+
+[Flattening](http://json-ld.org/spec/latest/json-ld/#flattened-document-form)
+collects all properties of a node in a single JSON object and labels all blank
+nodes with blank node identifiers. This ensures a shape of the data and
+consequently may drastically simplify the code required to process JSON-LD in
+certain applications.
+
+```csharp
+var doc = JObject.Parse(_docJson);
+var context = JObject.Parse(_contextJson);
+var opts = new JsonLdOptions();
+var flattened = JsonLdProcessor.Flatten(doc, context, opts);
+Console.WriteLine(flattened);
+
+/*
+
+Output:
+{
+ "@context": . . .,
+ "@graph": [
+ {
+ "@id": "_:b0",
+ "@type": "Person",
+ "image": "http://manu.sporny.org/images/manu.png",
+ "name": "Manu Sporny",
+ "homepage": "http://manu.sporny.org/"
+ },
+ {
+ "@id": "ld-experts",
+ "member": {
+ "@id": "_:b0"
+ },
+ "name": "LD Experts"
+ }
+ ]
+}
+
+*/
+
+```
+
+### Frame
+
+[Framing](http://json-ld.org/spec/latest/json-ld-framing/#introduction) is used
+to shape the data in a JSON-LD document, using an example frame document which
+is used to both match the flattened data and show an example of how the
+resulting data should be shaped. Matching is performed by using properties
+present in the frame to find objects in the data that share common values.
+Matching can be done either using all properties present in the frame, or any
+property in the frame. By chaining together objects using matched property
+values, objects can be embedded within one another.
+
+A frame also includes a context, which is used for compacting the resulting
+framed output.
+
+For the framing example below, the framing document is defined as follows:
+
+```json
+{
+ "@context": {
+ "name": "http://schema.org/name",
+ "member": {"@id": "http://schema.org/member", "@type": "@id"},
+ "homepage": {"@id": "http://schema.org/url", "@type": "@id"},
+ "image": {"@id": "http://schema.org/image", "@type": "@id"},
+ "Person": "http://schema.org/Person"
+ },
+ "@type": "Person"
+}
+```
+
+And we use it like this:
+
+```csharp
+var doc = JObject.Parse(_docJson);
+var frame = JObject.Parse(_frameJson);
+var opts = new JsonLdOptions();
+var flattened = JsonLdProcessor.Frame(doc, frame, opts);
+Console.WriteLine(flattened);
+
+/*
+
+Output:
+{
+ "@context": . . .,
+ "@graph": [
+ {
+ "@id": "_:b0",
+ "@type": "Person",
+ "image": "http://manu.sporny.org/images/manu.png",
+ "name": "Manu Sporny",
+ "homepage": "http://manu.sporny.org/"
+ }
+ ]
+}
+
+*/
+```
+
+### Normalize
+
+[Normalization](http://json-ld.github.io/normalization/spec/) (aka.
+canonicalization) converts the document into a graph of objects that is a
+canonical representation of the document that can be used for hashing,
+comparison, etc.
+
+```csharp
+var doc = JObject.Parse(_docJson);
+var opts = new JsonLdOptions();
+var normalized = (RDFDataset)JsonLdProcessor.Normalize(doc, opts);
+Console.WriteLine(normalized.Dump());
+
+/*
+
+Output:
+@default
+ subject
+ type blank node
+ value _:c14n0
+ predicate
+ type IRI
+ value http://schema.org/image
+ object
+ type IRI
+ value http://manu.sporny.org/images/manu.png
+ ---
+ subject
+ type blank node
+ value _:c14n0
+ predicate
+ type IRI
+ value http://schema.org/name
+ object
+ type literal
+ value Manu Sporny
+ datatype http://www.w3.org/2001/XMLSchema#string
+ ---
+ subject
+ type blank node
+ value _:c14n0
+ predicate
+ type IRI
+ value http://schema.org/url
+ object
+ type IRI
+ value http://manu.sporny.org/
+ ---
+ subject
+ type blank node
+ value _:c14n0
+ predicate
+ type IRI
+ value http://www.w3.org/1999/02/22-rdf-syntax-ns#type
+ object
+ type IRI
+ value http://schema.org/Person
+ ---
+ subject
+ type IRI
+ value http://example.org/ld-experts
+ predicate
+ type IRI
+ value http://schema.org/member
+ object
+ type blank node
+ value _:c14n0
+ ---
+ subject
+ type IRI
+ value http://example.org/ld-experts
+ predicate
+ type IRI
+ value http://schema.org/name
+ object
+ type literal
+ value LD Experts
+ datatype http://www.w3.org/2001/XMLSchema#string
+ ---
+
+*/
+```
+
+### ToRDF
+
+JSON-LD is a concrete RDF syntax as described in [RDF 1.1 Concepts and Abstract
+Syntax][rdf-11-concepts]. Hence, a JSON-LD document is both an RDF document and a
+JSON document and correspondingly represents an instance of an RDF data model.
+The procedure to deserialize a JSON-LD document to an
+[RDF dataset][rdf-11-dataset] (and, optionally, to [RDF N-Quads][n-quads])
+involves the following steps:
+
+1. Expand the JSON-LD document, removing any context; this ensures that
+properties, types, and values are given their full representation as IRIs and
+expanded values.
+1. Flatten the document, which turns the document into an array of node objects.
+1. Turn each node object into a series of RDF N-Quads.
+
+The processor's `ToRDF` method carries out these steps for you, like this:
+
+```csharp
+var doc = JObject.Parse(_docJson);
+var opts = new JsonLdOptions();
+var rdf = (RDFDataset)JsonLdProcessor.ToRDF(doc, opts);
+
+var serialized = RDFDatasetUtils.ToNQuads(rdf); // serialize RDF to string
+Console.WriteLine(serialized);
+
+/*
+
+Output:
+ _:b0 .
+ "LD Experts" .
+_:b0 .
+_:b0 "Manu Sporny" .
+_:b0 .
+_:b0 .
+
+*/
+```
+
+_or_ using a custom RDF renderer object, like this:
+
+```csharp
+private class JSONLDTripleCallback : IJSONLDTripleCallback
+{
+ public object Call(RDFDataset dataset) =>
+ RDFDatasetUtils.ToNQuads(dataset); // serialize the RDF dataset as NQuads
+}
+
+internal static void Run()
+{
+ var doc = JObject.Parse(_docJson);
+ var callback = new JSONLDTripleCallback();
+ var serialized = JsonLdProcessor.ToRDF(doc, callback);
+ Console.WriteLine(serialized);
+
+ /*
+
+ Output:
+ _:b0 .
+ "LD Experts" .
+ _:b0 .
+ _:b0 "Manu Sporny" .
+ _:b0 .
+ _:b0 .
+
+ */
+}
+```
+
+### FromRDF
+
+Serialization from RDF N-Quads into JSON-LD can be thought of as the inverse of
+the last of the three steps described in summary Deserialization described in
+the `ToRDF` method documentation above. Serialization creates an expanded
+JSON-LD document closely matching the N-Quads from RDF, using a single node
+object for all N-Quads having a common subject, and a single property for those
+N-Quads also having a common predicate.
+
+In practice, it looks like this:
+
+_the variable `serialized` is populated with RDF N-Quads values resulting from
+the code in the `ToRDF` example above_
+
+```csharp
+var opts = new JsonLdOptions();
+var jsonld = JsonLdProcessor.FromRDF(serialized, opts);
+Console.WriteLine(jsonld);
+
+/*
+
+Output:
+[
+ {
+ "@id": "_:b0",
+ "http://schema.org/image": [
+ {
+ "@id": "http://manu.sporny.org/images/manu.png"
+ }
+ ],
+ "http://schema.org/name": [
+ {
+ "@value": "Manu Sporny"
+ }
+ ],
+ "http://schema.org/url": [
+ {
+ "@id": "http://manu.sporny.org/"
+ }
+ ],
+ "@type": [
+ "http://schema.org/Person"
+ ]
+ },
+ {
+ "@id": "http://example.org/ld-experts",
+ "http://schema.org/member": [
+ {
+ "@id": "_:b0"
+ }
+ ],
+ "http://schema.org/name": [
+ {
+ "@value": "LD Experts"
+ }
+ ]
+ }
+]
+*/
+```
+
+_or_ using a custom RDF parser:
+
+```csharp
+private class CustomRDFParser : IRDFParser
+{
+ public RDFDataset Parse(JToken input)
+ {
+ // by public decree, references to example.org are normalized to https going forward...
+ var converted = ((string)input).Replace("http://example.org/", "https://example.org/");
+ return RDFDatasetUtils.ParseNQuads(converted);
+ }
+}
+
+internal static void Run()
+{
+ var parser = new CustomRDFParser();
+ var jsonld = JsonLdProcessor.FromRDF(_serialized, parser);
+ Console.WriteLine(jsonld);
+
+ /*
+
+ Output:
+ [
+ {
+ "@id": "_:b0",
+ "http://schema.org/image": [
+ {
+ "@id": "http://manu.sporny.org/images/manu.png"
+ }
+ ],
+ "http://schema.org/name": [
+ {
+ "@value": "Manu Sporny"
+ }
+ ],
+ "http://schema.org/url": [
+ {
+ "@id": "http://manu.sporny.org/"
+ }
+ ],
+ "@type": [
+ "http://schema.org/Person"
+ ]
+ },
+ {
+ "@id": "https://example.org/ld-experts",
+ "http://schema.org/member": [
+ {
+ "@id": "_:b0"
+ }
+ ],
+ "http://schema.org/name": [
+ {
+ "@value": "LD Experts"
+ }
+ ]
+ }
+ ]
+ */
+}
+```
+
+### Custom DocumentLoader
+
+By replacing the default `documentLoader` object placed on the JsonLdProcessor,
+it is possible to alter the behaviour when retrieving a remote document (e.g. a
+context document) required to execute a given algorithm (e.g. Expansion).
+
+```csharp
+public class CustomDocumentLoader : DocumentLoader
+{
+ private static readonly string _cachedExampleOrgContext = Res.ReadString("context.json");
+
+ public override RemoteDocument LoadDocument(string url)
+ {
+ if (url == "http://example.org/context.jsonld") // we have this cached locally
+ {
+ var doc = new JObject(new JProperty("@context", JObject.Parse(_cachedExampleOrgContext)));
+ return new RemoteDocument(url, doc);
+ }
+ else
+ {
+ return base.LoadDocument(url);
+ }
+ }
+}
+
+public static void Run()
+{
+ var doc = JObject.Parse(_docJson);
+ var remoteContext = JObject.Parse("{'@context':'http://example.org/context.jsonld'}");
+ var opts = new JsonLdOptions { documentLoader = new CustomDocumentLoader() };
+ var compacted = JsonLdProcessor.Compact(doc, remoteContext, opts);
+ Console.WriteLine(compacted);
+
+ /*
+
+ Output:
+ {
+ "@id": "http://example.org/ld-experts",
+ "member": {
+ "@type": "Person",
+ "image": "http://manu.sporny.org/images/manu.png",
+ "name": "Manu Sporny",
+ "homepage": "http://manu.sporny.org/"
+ },
+ "name": "LD Experts"
+ }
+
+ */
+}
+```
+
+
+## Contributing
+
+This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For
+more information see the [Code of Conduct FAQ][coc-faq] or contact
+[opencode@microsoft.com][ms-mail] with any additional questions or comments.
+
+Pull requests for json-ld.net are welcome, to get started install the latest
+tools for .NET Core:
+
+* [.NET Core][dnc]
+* [.NET Core tutorial][dnc-tutorial]
+
+
+### Build and Tests
+
+On Windows, you can execute `build.ps1`, which will create a nupkg and run
+tests for both .NET desktop and .NET Core.
+
+On both Windows and all other supported operating systems, you can run
+`dotnet build` to build and `dotnet test` to run the tests.
+
+
+## Origin
+
+This project began life as a [Sharpen][sharpen]-based auto-port from
+[jsonld-java][jsonld-java].
+
+Documentation for this library is in part drawn from
+https://github.com/linked-data-dotnet/json-ld.net
+
+ [coc]: https://opensource.microsoft.com/codeofconduct/
+ [coc-faq]: https://opensource.microsoft.com/codeofconduct/faq/
+ [codecov]: https://codecov.io/gh/linked-data-dotnet/json-ld.net
+ [codecov-badge]: https://img.shields.io/codecov/c/github/linked-data-dotnet/json-ld.net/main.svg
+
+ [errata]: http://www.w3.org/2014/json-ld-errata
+
+ [ms-mail]: mailto:opencode@microsoft.com
+ [dnc]: https://dot.net
+ [dnc-tutorial]: https://www.microsoft.com/net/core
+
+ [gha-badge]: https://github.com/linked-data-dotnet/json-ld.net/actions/workflows/dotnet.yml/badge.svg
+ [build]: https://github.com/linked-data-dotnet/json-ld.net/actions/workflows/dotnet.yml
+
+ [jsonld]: https://json-ld.org/
+ [jsonld-java]: https://github.com/jsonld-java/jsonld-java
+
+ [json-ld-10]: http://www.w3.org/TR/2014/REC-json-ld-20140116/
+ [json-ld-10-api]: http://www.w3.org/TR/2014/REC-json-ld-api-20140116/
+ [json-ld-10-framing]: https://json-ld.org/spec/ED/json-ld-framing/20120830/
+
+ [json-ld-wg-11]: https://www.w3.org/TR/json-ld11/
+ [json-ld-wg-11-api]: https://www.w3.org/TR/json-ld11-api/
+ [json-ld-wg-11-framing]: https://www.w3.org/TR/json-ld11-framing/
+
+ [json-ld-wg-latest]: https://w3c.github.io/json-ld-syntax/
+ [json-ld-wg-api-latest]: https://w3c.github.io/json-ld-api/
+ [json-ld-wg-framing-latest]: https://w3c.github.io/json-ld-framing/
+
+ [n-quads]: https://www.w3.org/TR/n-quads/
+ [rdf-11-concepts]: https://www.w3.org/TR/rdf11-concepts/
+ [rdf-11-dataset]: https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset
+
+ [nuget]: https://www.nuget.org/packages/json-ld.net/
+ [nuget-badge]: https://img.shields.io/nuget/v/json-ld.net.svg
+
+ [sharpen]: http://community.versant.com/Projects/html/projectspaces/db4o_product_design/sharpen.html
+
+ [test-runner]: https://github.com/linked-data-dotnet/json-ld.net/tree/main/test/json-ld.net.tests
+ [wg-test-suite]: https://github.com/w3c/json-ld-api/tree/master/tests
diff --git a/after.JsonLD.sln.targets b/after.JsonLD.sln.targets
new file mode 100644
index 00000000..cf28d823
--- /dev/null
+++ b/after.JsonLD.sln.targets
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/contributors.md b/contributors.md
new file mode 100644
index 00000000..9392a960
--- /dev/null
+++ b/contributors.md
@@ -0,0 +1,21 @@
+# Contributors and maintainers
+
+We maintain this list as a way to track who's currently involved in the project.
+Please use PRs to this document to propose adding new maintainers /
+contributors.
+
+[](https://github.com/linked-data-dotnet/json-ld.net/graphs/contributors)
+
+Made with [contributors-img](https://contributors-img.web.app).
+
+**N.B.** please propose names ordered alphabetically by username.
+
+## Admins
+
+ - [Asbjørn Ulsberg](https://github.com/asbjornu )
+ - [Andrew Stewart Gibson](https://github.com/goofballLogic)
+
+## Contributors
+
+ - [Tomasz Pluskiewicz](https://github.com/tpluscode)
+ - [Ben Abelshausen](https://github.com/xivk)
diff --git a/doc/linqpad-samples/Compact.linq b/doc/linqpad-samples/Compact.linq
new file mode 100644
index 00000000..f90973e0
--- /dev/null
+++ b/doc/linqpad-samples/Compact.linq
@@ -0,0 +1,14 @@
+
+ json-ld.net.dll
+ Newtonsoft.Json
+ JsonLD.Core
+ Newtonsoft.Json.Linq
+
+
+#load "Utils/Resources"
+
+var opts = new JsonLdOptions();
+var compacted = JsonLdProcessor.Compact(Resources.Doc, Resources.Context, opts);
+
+compacted.ToString().Dump("string");
+compacted.Dump("JSON DOM");
\ No newline at end of file
diff --git a/doc/linqpad-samples/CustomRDFParser.linq b/doc/linqpad-samples/CustomRDFParser.linq
new file mode 100644
index 00000000..bb485198
--- /dev/null
+++ b/doc/linqpad-samples/CustomRDFParser.linq
@@ -0,0 +1,31 @@
+
+ json-ld.net.dll
+ Newtonsoft.Json
+ JsonLD.Core
+ Newtonsoft.Json.Linq
+
+
+#load "Utils/Resources"
+
+void Main()
+{
+ var opts = new JsonLdOptions();
+
+ var rdf = (RDFDataset)JsonLdProcessor.ToRDF(Resources.Doc, opts);
+ var serialized = RDFDatasetUtils.ToNQuads(rdf); // serialize RDF to string
+
+ var parser = new CustomRDFParser();
+ var jsonld = JsonLdProcessor.FromRDF(serialized, parser);
+ jsonld.ToString().Dump("string");
+ jsonld.Dump("JSON DOM");
+}
+
+public class CustomRDFParser : IRDFParser
+{
+ public RDFDataset Parse(JToken input)
+ {
+ // by public decree, references to example.org are normalized to https going forward...
+ var converted = ((string)input).Replace("http://example.org/", "https://example.org/");
+ return RDFDatasetUtils.ParseNQuads(converted);
+ }
+}
diff --git a/doc/linqpad-samples/CustomRDFRender.linq b/doc/linqpad-samples/CustomRDFRender.linq
new file mode 100644
index 00000000..8bdf6734
--- /dev/null
+++ b/doc/linqpad-samples/CustomRDFRender.linq
@@ -0,0 +1,23 @@
+
+ json-ld.net.dll
+ Newtonsoft.Json
+ JsonLD.Core
+ Newtonsoft.Json.Linq
+
+
+#load "Utils/Resources"
+
+void Main()
+{
+ var opts = new JsonLdOptions();
+
+ var callback = new JSONLDTripleCallback();
+ var serialized = JsonLdProcessor.ToRDF(Resources.Doc, callback);
+ serialized.Dump("RDF");
+}
+
+public class JSONLDTripleCallback : IJSONLDTripleCallback
+{
+ public object Call(RDFDataset dataset) =>
+ RDFDatasetUtils.ToNQuads(dataset); // serialize the RDF dataset as NQuads
+}
diff --git a/doc/linqpad-samples/Expand.linq b/doc/linqpad-samples/Expand.linq
new file mode 100644
index 00000000..5569b4b2
--- /dev/null
+++ b/doc/linqpad-samples/Expand.linq
@@ -0,0 +1,16 @@
+
+ json-ld.net.dll
+ Newtonsoft.Json
+ JsonLD.Core
+ Newtonsoft.Json.Linq
+
+
+#load "Utils/Resources"
+
+var opts = new JsonLdOptions();
+var compacted = JsonLdProcessor.Compact(Resources.Doc, Resources.Context, opts);
+
+var expanded = JsonLdProcessor.Expand(compacted);
+
+expanded.ToString().Dump("string");
+expanded.Dump("JSON DOM");
\ No newline at end of file
diff --git a/doc/linqpad-samples/FileOrder.txt b/doc/linqpad-samples/FileOrder.txt
new file mode 100644
index 00000000..7cbe0854
--- /dev/null
+++ b/doc/linqpad-samples/FileOrder.txt
@@ -0,0 +1,11 @@
+Installation.linq
+Compact.linq
+Expand.linq
+Flatten.linq
+Frame.linq
+Normalize.linq
+ToRDF.linq
+FromRDF.linq
+CustomRDFRender.linq
+CustomRDFParser.linq
+RemoteDocumentLoader.linq
\ No newline at end of file
diff --git a/doc/linqpad-samples/Flatten.linq b/doc/linqpad-samples/Flatten.linq
new file mode 100644
index 00000000..2016de63
--- /dev/null
+++ b/doc/linqpad-samples/Flatten.linq
@@ -0,0 +1,14 @@
+
+ json-ld.net.dll
+ Newtonsoft.Json
+ JsonLD.Core
+ Newtonsoft.Json.Linq
+
+
+#load "Utils/Resources"
+
+var opts = new JsonLdOptions();
+var flattened = JsonLdProcessor.Flatten(Resources.Doc, Resources.Context, opts);
+
+flattened.ToString().Dump("string");
+flattened.Dump("JSON DOM");
\ No newline at end of file
diff --git a/doc/linqpad-samples/Frame.linq b/doc/linqpad-samples/Frame.linq
new file mode 100644
index 00000000..750f1bf3
--- /dev/null
+++ b/doc/linqpad-samples/Frame.linq
@@ -0,0 +1,14 @@
+
+ json-ld.net.dll
+ Newtonsoft.Json
+ JsonLD.Core
+ Newtonsoft.Json.Linq
+
+
+#load "Utils/Resources"
+
+var opts = new JsonLdOptions();
+var framed = JsonLdProcessor.Frame(Resources.Doc, Resources.Frame, opts);
+
+framed.ToString().Dump("string");
+framed.Dump("JSON DOM");
\ No newline at end of file
diff --git a/doc/linqpad-samples/FromRDF.linq b/doc/linqpad-samples/FromRDF.linq
new file mode 100644
index 00000000..193cd77e
--- /dev/null
+++ b/doc/linqpad-samples/FromRDF.linq
@@ -0,0 +1,17 @@
+
+ json-ld.net.dll
+ Newtonsoft.Json
+ JsonLD.Core
+ Newtonsoft.Json.Linq
+
+
+#load "Utils/Resources"
+
+var opts = new JsonLdOptions();
+var rdf = (RDFDataset)JsonLdProcessor.ToRDF(Resources.Doc, opts);
+var serialized = RDFDatasetUtils.ToNQuads(rdf); // serialize RDF to string
+
+var jsonld = JsonLdProcessor.FromRDF(serialized, opts);
+
+jsonld.ToString().Dump("string");
+jsonld.Dump("JSON DOM");
diff --git a/doc/linqpad-samples/Installation.linq b/doc/linqpad-samples/Installation.linq
new file mode 100644
index 00000000..b0edf4c3
--- /dev/null
+++ b/doc/linqpad-samples/Installation.linq
@@ -0,0 +1,13 @@
+
+ json-ld.net.dll
+ Newtonsoft.Json
+ Newtonsoft.Json.Linq
+ JsonLD.Core
+
+
+var json = "{'@context':{'test':'http://www.example.org/'},'test:hello':'world'}";
+var document = JObject.Parse(json);
+var expanded = JsonLdProcessor.Expand(document);
+
+expanded.ToString().Dump("string");
+expanded.Dump("JSON DOM");
\ No newline at end of file
diff --git a/doc/linqpad-samples/Normalize.linq b/doc/linqpad-samples/Normalize.linq
new file mode 100644
index 00000000..ab252e66
--- /dev/null
+++ b/doc/linqpad-samples/Normalize.linq
@@ -0,0 +1,15 @@
+
+ json-ld.net.dll
+ Newtonsoft.Json
+ JsonLD.Core
+ Newtonsoft.Json.Linq
+
+
+#load "Utils/Resources"
+#load "Utils/ObjectDumper"
+
+var opts = new JsonLdOptions();
+var normalized = (RDFDataset)JsonLdProcessor.Normalize(Resources.Doc, opts);
+
+normalized.JsonLDDump().Dump("string");
+normalized.Dump("JSON DOM");
\ No newline at end of file
diff --git a/doc/linqpad-samples/RemoteDocumentLoader.linq b/doc/linqpad-samples/RemoteDocumentLoader.linq
new file mode 100644
index 00000000..4590bd50
--- /dev/null
+++ b/doc/linqpad-samples/RemoteDocumentLoader.linq
@@ -0,0 +1,37 @@
+
+ json-ld.net.dll
+ Newtonsoft.Json
+ JsonLD.Core
+ Newtonsoft.Json.Linq
+
+
+#load "Utils/Resources"
+
+void Main()
+{
+ var doc = Resources.Doc;
+ var remoteContext = JObject.Parse("{'@context':'http://example.org/context.jsonld'}");
+ var opts = new JsonLdOptions { documentLoader = new CustomDocumentLoader() };
+ var compacted = JsonLdProcessor.Compact(doc, remoteContext, opts);
+
+ compacted.ToString().Dump("string");
+ compacted.Dump("JSON DOM");
+}
+
+public class CustomDocumentLoader : DocumentLoader
+{
+ private static readonly string _cachedExampleOrgContext = Resources.Context.ToString();
+
+ public override RemoteDocument LoadDocument(string url)
+ {
+ if (url == "http://example.org/context.jsonld") // we have this cached locally
+ {
+ var doc = new JObject(new JProperty("@context", JObject.Parse(_cachedExampleOrgContext)));
+ return new RemoteDocument(url, doc);
+ }
+ else
+ {
+ return base.LoadDocument(url);
+ }
+ }
+}
diff --git a/doc/linqpad-samples/ToRDF.linq b/doc/linqpad-samples/ToRDF.linq
new file mode 100644
index 00000000..9ee8ca38
--- /dev/null
+++ b/doc/linqpad-samples/ToRDF.linq
@@ -0,0 +1,16 @@
+
+ json-ld.net.dll
+ Newtonsoft.Json
+ JsonLD.Core
+ Newtonsoft.Json.Linq
+
+
+#load "Utils/Resources"
+
+var opts = new JsonLdOptions();
+var rdf = (RDFDataset)JsonLdProcessor.ToRDF(Resources.Doc, opts);
+
+var serialized = RDFDatasetUtils.ToNQuads(rdf); // serialize RDF to string
+
+serialized.Dump("string");
+serialized.Dump("JSON DOM");
\ No newline at end of file
diff --git a/doc/linqpad-samples/Utils/ObjectDumper.linq b/doc/linqpad-samples/Utils/ObjectDumper.linq
new file mode 100644
index 00000000..a60473c6
--- /dev/null
+++ b/doc/linqpad-samples/Utils/ObjectDumper.linq
@@ -0,0 +1,80 @@
+
+ System.ComponentModel
+
+
+void Main()
+{
+
+}
+
+public static class ObjectDumperExtensions
+{
+ public static string JsonLDDump(this object obj) => ObjectDumper.Dump(obj);
+}
+
+// thanks: https://stackoverflow.com/a/42264037
+public class ObjectDumper
+{
+ public static string Dump(object obj)
+ {
+ return new ObjectDumper().DumpObject(obj);
+ }
+
+ private readonly StringBuilder _dumpBuilder = new StringBuilder();
+
+ private string DumpObject(object obj)
+ {
+ DumpObject(obj, 0);
+ return _dumpBuilder.ToString();
+ }
+
+ private void DumpObject(object obj, int nestingLevel = 0)
+ {
+ var nestingSpaces = new String('\t', nestingLevel); //"".PadLeft(nestingLevel * 4);
+
+ if (obj == null)
+ {
+ _dumpBuilder.AppendFormat("null", nestingSpaces);
+ }
+ else if (obj is string || obj.GetType().IsPrimitive)
+ {
+ _dumpBuilder.AppendFormat("{1}", nestingSpaces, obj.ToString().PadRight(8));
+ }
+ else if (ImplementsDictionary(obj.GetType()))
+ {
+ using var e = ((dynamic)obj).GetEnumerator();
+ var enumerator = (IEnumerator)e;
+ while (enumerator.MoveNext())
+ {
+ dynamic p = enumerator.Current;
+
+ var key = p.Key;
+ var value = p.Value;
+ _dumpBuilder.AppendFormat("\n{0}{1}", nestingSpaces, key.PadRight(10), value != null ? value.GetType().ToString() : "");
+ DumpObject(value, nestingLevel + 1);
+ }
+ }
+ else if (obj is IEnumerable)
+ {
+ foreach (dynamic p in obj as IEnumerable)
+ {
+ DumpObject(p, nestingLevel);
+ DumpObject("\n", nestingLevel);
+ DumpObject("---", nestingLevel);
+ }
+ }
+ else
+ {
+ foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(obj))
+ {
+ string name = descriptor.Name;
+ object value = descriptor.GetValue(obj);
+
+ _dumpBuilder.AppendFormat("{0}{1}\n", nestingSpaces, name.PadRight(10), value != null ? value.GetType().ToString() : "");
+ DumpObject(value, nestingLevel + 1);
+ }
+ }
+ }
+
+ private bool ImplementsDictionary(Type t) => t.GetInterfaces().Any(i => i.Name.Contains("IDictionary"));
+}
diff --git a/doc/linqpad-samples/Utils/Resources.linq b/doc/linqpad-samples/Utils/Resources.linq
new file mode 100644
index 00000000..9dddc067
--- /dev/null
+++ b/doc/linqpad-samples/Utils/Resources.linq
@@ -0,0 +1,19 @@
+
+ context.json
+ doc.json
+ frame.json
+ Newtonsoft.Json
+ Newtonsoft.Json.Linq
+
+
+void Main()
+{
+
+}
+
+public static class Resources
+{
+ public static readonly JObject Doc = JObject.Parse(File.ReadAllText(Util.GetFullPath("doc.json")));
+ public static readonly JObject Context = JObject.Parse(File.ReadAllText(Util.GetFullPath("context.json")));
+ public static readonly JObject Frame = JObject.Parse(File.ReadAllText(Util.GetFullPath("frame.json")));
+}
diff --git a/doc/linqpad-samples/context.json b/doc/linqpad-samples/context.json
new file mode 100644
index 00000000..0b1d6477
--- /dev/null
+++ b/doc/linqpad-samples/context.json
@@ -0,0 +1,15 @@
+{
+ "name": "http://schema.org/name",
+ "member": "http://schema.org/member",
+ "homepage": {
+ "@id": "http://schema.org/url",
+ "@type": "@id"
+ },
+ "image": {
+ "@id": "http://schema.org/image",
+ "@type": "@id"
+ },
+ "Person": "http://schema.org/Person",
+ "@vocab": "http://example.org/",
+ "@base": "http://example.org/"
+}
\ No newline at end of file
diff --git a/doc/linqpad-samples/doc.json b/doc/linqpad-samples/doc.json
new file mode 100644
index 00000000..5d6bd3d2
--- /dev/null
+++ b/doc/linqpad-samples/doc.json
@@ -0,0 +1,16 @@
+{
+ "@id": "http://example.org/ld-experts",
+ "http://schema.org/name": "LD Experts",
+ "http://schema.org/member": [
+ {
+ "@type": "http://schema.org/Person",
+ "http://schema.org/name": "Manu Sporny",
+ "http://schema.org/url": {
+ "@id": "http://manu.sporny.org/"
+ },
+ "http://schema.org/image": {
+ "@id": "http://manu.sporny.org/images/manu.png"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/doc/linqpad-samples/frame.json b/doc/linqpad-samples/frame.json
new file mode 100644
index 00000000..b274c875
--- /dev/null
+++ b/doc/linqpad-samples/frame.json
@@ -0,0 +1,19 @@
+{
+ "@context": {
+ "name": "http://schema.org/name",
+ "member": {
+ "@id": "http://schema.org/member",
+ "@type": "@id"
+ },
+ "homepage": {
+ "@id": "http://schema.org/url",
+ "@type": "@id"
+ },
+ "image": {
+ "@id": "http://schema.org/image",
+ "@type": "@id"
+ },
+ "Person": "http://schema.org/Person"
+ },
+ "@type": "Person"
+}
\ No newline at end of file
diff --git a/src/json-ld.net/Core/Context.cs b/src/json-ld.net/Core/Context.cs
index ee3fb67d..48bd4471 100644
--- a/src/json-ld.net/Core/Context.cs
+++ b/src/json-ld.net/Core/Context.cs
@@ -14,15 +14,12 @@ namespace JsonLD.Core
/// tristan
//[System.Serializable]
public class Context : JObject
-#if !PORTABLE && !IS_CORECLR
- , ICloneable
-#endif
{
private JsonLdOptions options;
JObject termDefinitions;
- public JObject inverse = null;
+ internal JObject inverse = null;
public Context() : this(new JsonLdOptions())
{
@@ -68,7 +65,7 @@ private void Init(JsonLdOptions options)
///
///
///
- public virtual JToken CompactValue(string activeProperty, JObject value)
+ internal virtual JToken CompactValue(string activeProperty, JObject value)
{
var dict = (IDictionary)value;
// 1)
@@ -137,7 +134,7 @@ public virtual JToken CompactValue(string activeProperty, JObject value)
///
/// JsonLdError
///
- public virtual JsonLD.Core.Context Parse(JToken localContext, List remoteContexts)
+ internal virtual JsonLD.Core.Context Parse(JToken localContext, List remoteContexts)
{
if (remoteContexts == null)
{
@@ -318,7 +315,7 @@ public virtual JsonLD.Core.Context Parse(JToken localContext, List remot
}
///
- public virtual JsonLD.Core.Context Parse(JToken localContext)
+ internal virtual JsonLD.Core.Context Parse(JToken localContext)
{
return this.Parse(localContext, new List());
}
@@ -952,7 +949,7 @@ public object Clone()
/// already generated for the given active context.
///
/// the inverse context.
- public virtual JObject GetInverse()
+ internal virtual JObject GetInverse()
{
// lazily create inverse
if (inverse != null)
@@ -1144,7 +1141,7 @@ private string SelectTerm(string iri, JArray containers, string typeLanguage
/// Retrieve container mapping.
///
///
- public virtual string GetContainer(string property)
+ internal virtual string GetContainer(string property)
{
// TODO(sblom): Do java semantics of get() on a Map return null if property is null?
if (property == null)
@@ -1169,7 +1166,7 @@ public virtual string GetContainer(string property)
return (string)td["@container"];
}
- public virtual bool IsReverseProperty(string property)
+ internal virtual bool IsReverseProperty(string property)
{
if (property == null)
{
@@ -1218,7 +1215,7 @@ internal virtual JObject GetTermDefinition(string key)
}
///
- public virtual JToken ExpandValue(string activeProperty, JToken value)
+ internal virtual JToken ExpandValue(string activeProperty, JToken value)
{
JObject rval = new JObject();
JObject td = GetTermDefinition(activeProperty);
@@ -1272,7 +1269,7 @@ public virtual JToken ExpandValue(string activeProperty, JToken value)
}
///
- public virtual JObject GetContextValue(string activeProperty, string @string)
+ internal virtual JObject GetContextValue(string activeProperty, string @string)
{
throw new JsonLdError(JsonLdError.Error.NotImplemented, "getContextValue is only used by old code so far and thus isn't implemented"
);
diff --git a/src/json-ld.net/Core/DocumentLoader.cs b/src/json-ld.net/Core/DocumentLoader.cs
index bb7d5f79..9c759ae9 100644
--- a/src/json-ld.net/Core/DocumentLoader.cs
+++ b/src/json-ld.net/Core/DocumentLoader.cs
@@ -6,199 +6,108 @@
using JsonLD.Util;
using System.Net;
using System.Collections.Generic;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Threading.Tasks;
+using System.Runtime.InteropServices;
namespace JsonLD.Core
{
public class DocumentLoader
{
+ enum JsonLDContentType
+ {
+ JsonLD,
+ PlainJson,
+ Other
+ }
+
+ JsonLDContentType GetJsonLDContentType(string contentTypeStr)
+ {
+ JsonLDContentType contentType;
+
+ switch (contentTypeStr)
+ {
+ case "application/ld+json":
+ contentType = JsonLDContentType.JsonLD;
+ break;
+ // From RFC 6839, it looks like plain JSON is content type application/json and any MediaType ending in "+json".
+ case "application/json":
+ case string type when type.EndsWith("+json"):
+ contentType = JsonLDContentType.PlainJson;
+ break;
+ default:
+ contentType = JsonLDContentType.Other;
+ break;
+ }
+
+ return contentType;
+ }
+
///
public virtual RemoteDocument LoadDocument(string url)
{
-#if !PORTABLE && !IS_CORECLR
+ return LoadDocumentAsync(url).ConfigureAwait(false).GetAwaiter().GetResult();
+ }
+
+ ///
+ public virtual async Task LoadDocumentAsync(string url)
+ {
RemoteDocument doc = new RemoteDocument(url, null);
try
{
- HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);
- req.Accept = AcceptHeader;
- WebResponse resp = req.GetResponse();
- bool isJsonld = resp.Headers[HttpResponseHeader.ContentType] == "application/ld+json";
- if (!resp.Headers[HttpResponseHeader.ContentType].Contains("json"))
+ using (HttpResponseMessage response = await JsonLD.Util.LDHttpClient.FetchAsync(url).ConfigureAwait(false))
{
- throw new JsonLdError(JsonLdError.Error.LoadingDocumentFailed, url);
- }
- string[] linkHeaders = resp.Headers.GetValues("Link");
- if (!isJsonld && linkHeaders != null)
- {
- linkHeaders = linkHeaders.SelectMany((h) => h.Split(",".ToCharArray()))
- .Select(h => h.Trim()).ToArray();
- IEnumerable linkedContexts = linkHeaders.Where(v => v.EndsWith("rel=\"http://www.w3.org/ns/json-ld#context\""));
- if (linkedContexts.Count() > 1)
+ var code = (int)response.StatusCode;
+
+ if (code >= 400)
{
- throw new JsonLdError(JsonLdError.Error.MultipleContextLinkHeaders);
+ throw new JsonLdError(JsonLdError.Error.LoadingDocumentFailed, $"HTTP {code} {url}");
+ }
+
+ var finalUrl = response.RequestMessage.RequestUri.ToString();
+
+ var contentType = GetJsonLDContentType(response.Content.Headers.ContentType.MediaType);
+
+ if (contentType == JsonLDContentType.Other)
+ {
+ throw new JsonLdError(JsonLdError.Error.LoadingDocumentFailed, url);
+ }
+
+ // For plain JSON, see if there's a context document linked in the HTTP response headers.
+ if (contentType == JsonLDContentType.PlainJson && response.Headers.TryGetValues("Link", out var linkHeaders))
+ {
+ linkHeaders = linkHeaders.SelectMany((h) => h.Split(",".ToCharArray()))
+ .Select(h => h.Trim()).ToArray();
+ IEnumerable linkedContexts = linkHeaders.Where(v => v.EndsWith("rel=\"http://www.w3.org/ns/json-ld#context\""));
+ if (linkedContexts.Count() > 1)
+ {
+ throw new JsonLdError(JsonLdError.Error.MultipleContextLinkHeaders);
+ }
+ string header = linkedContexts.First();
+ string linkedUrl = header.Substring(1, header.IndexOf(">") - 1);
+ string resolvedUrl = URL.Resolve(finalUrl, linkedUrl);
+ var remoteContext = await this.LoadDocumentAsync(resolvedUrl).ConfigureAwait(false);
+ doc.contextUrl = remoteContext.documentUrl;
+ doc.context = remoteContext.document;
}
- string header = linkedContexts.First();
- string linkedUrl = header.Substring(1, header.IndexOf(">") - 1);
- string resolvedUrl = URL.Resolve(url, linkedUrl);
- var remoteContext = this.LoadDocument(resolvedUrl);
- doc.contextUrl = remoteContext.documentUrl;
- doc.context = remoteContext.document;
- }
- Stream stream = resp.GetResponseStream();
+ Stream stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
- doc.DocumentUrl = req.Address.ToString();
- doc.Document = JSONUtils.FromInputStream(stream);
+ doc.DocumentUrl = finalUrl;
+ doc.Document = JSONUtils.FromInputStream(stream);
+ }
}
catch (JsonLdError)
{
throw;
}
- catch (Exception)
+ catch (Exception exception)
{
- throw new JsonLdError(JsonLdError.Error.LoadingDocumentFailed, url);
+ throw new JsonLdError(JsonLdError.Error.LoadingDocumentFailed, url, exception);
}
return doc;
-#else
- throw new PlatformNotSupportedException();
-#endif
}
-
- /// An HTTP Accept header that prefers JSONLD.
- /// An HTTP Accept header that prefers JSONLD.
- public const string AcceptHeader = "application/ld+json, application/json;q=0.9, application/javascript;q=0.5, text/javascript;q=0.5, text/plain;q=0.2, */*;q=0.1";
-
-// private static volatile IHttpClient httpClient;
-
-// ///
-// /// Returns a Map, List, or String containing the contents of the JSON
-// /// resource resolved from the URL.
-// ///
-// ///
-// /// Returns a Map, List, or String containing the contents of the JSON
-// /// resource resolved from the URL.
-// ///
-// /// The URL to resolve
-// ///
-// /// The Map, List, or String that represent the JSON resource
-// /// resolved from the URL
-// ///
-// /// If the JSON was not valid.
-// ///
-// /// If there was an error resolving the resource.
-// ///
-// public static object FromURL(URL url)
-// {
-// MappingJsonFactory jsonFactory = new MappingJsonFactory();
-// InputStream @in = OpenStreamFromURL(url);
-// try
-// {
-// JsonParser parser = jsonFactory.CreateParser(@in);
-// try
-// {
-// JsonToken token = parser.NextToken();
-// Type type;
-// if (token == JsonToken.StartObject)
-// {
-// type = typeof(IDictionary);
-// }
-// else
-// {
-// if (token == JsonToken.StartArray)
-// {
-// type = typeof(IList);
-// }
-// else
-// {
-// type = typeof(string);
-// }
-// }
-// return parser.ReadValueAs(type);
-// }
-// finally
-// {
-// parser.Close();
-// }
-// }
-// finally
-// {
-// @in.Close();
-// }
-// }
-
-// ///
-// /// Opens an
-// /// Java.IO.InputStream
-// /// for the given
-// /// Java.Net.URL
-// /// , including support
-// /// for http and https URLs that are requested using Content Negotiation with
-// /// application/ld+json as the preferred content type.
-// ///
-// /// The URL identifying the source.
-// /// An InputStream containing the contents of the source.
-// /// If there was an error resolving the URL.
-// public static InputStream OpenStreamFromURL(URL url)
-// {
-// string protocol = url.GetProtocol();
-// if (!JsonLDNet.Shims.EqualsIgnoreCase(protocol, "http") && !JsonLDNet.Shims.EqualsIgnoreCase
-// (protocol, "https"))
-// {
-// // Can't use the HTTP client for those!
-// // Fallback to Java's built-in URL handler. No need for
-// // Accept headers as it's likely to be file: or jar:
-// return url.OpenStream();
-// }
-// IHttpUriRequest request = new HttpGet(url.ToExternalForm());
-// // We prefer application/ld+json, but fallback to application/json
-// // or whatever is available
-// request.AddHeader("Accept", AcceptHeader);
-// IHttpResponse response = GetHttpClient().Execute(request);
-// int status = response.GetStatusLine().GetStatusCode();
-// if (status != 200 && status != 203)
-// {
-// throw new IOException("Can't retrieve " + url + ", status code: " + status);
-// }
-// return response.GetEntity().GetContent();
-// }
-
-// public static IHttpClient GetHttpClient()
-// {
-// IHttpClient result = httpClient;
-// if (result == null)
-// {
-// lock (typeof(JSONUtils))
-// {
-// result = httpClient;
-// if (result == null)
-// {
-// // Uses Apache SystemDefaultHttpClient rather than
-// // DefaultHttpClient, thus the normal proxy settings for the
-// // JVM will be used
-// DefaultHttpClient client = new SystemDefaultHttpClient();
-// // Support compressed data
-// // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/httpagent.html#d5e1238
-// client.AddRequestInterceptor(new RequestAcceptEncoding());
-// client.AddResponseInterceptor(new ResponseContentEncoding());
-// CacheConfig cacheConfig = new CacheConfig();
-// cacheConfig.SetMaxObjectSize(1024 * 128);
-// // 128 kB
-// cacheConfig.SetMaxCacheEntries(1000);
-// // and allow caching
-// httpClient = new CachingHttpClient(client, cacheConfig);
-// result = httpClient;
-// }
-// }
-// }
-// return result;
-// }
-
-// public static void SetHttpClient(IHttpClient nextHttpClient)
-// {
-// lock (typeof(JSONUtils))
-// {
-// httpClient = nextHttpClient;
-// }
-// }
}
}
diff --git a/src/json-ld.net/Core/JSONLDConsts.cs b/src/json-ld.net/Core/JSONLDConsts.cs
index 916d8852..802a9023 100644
--- a/src/json-ld.net/Core/JSONLDConsts.cs
+++ b/src/json-ld.net/Core/JSONLDConsts.cs
@@ -4,7 +4,7 @@ namespace JsonLD.Core
{
/// URI Constants used in the JSON-LD parser.
/// URI Constants used in the JSON-LD parser.
- public sealed class JSONLDConsts
+ internal sealed class JSONLDConsts
{
public const string RdfSyntaxNs = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
diff --git a/src/json-ld.net/Core/JsonLdApi.cs b/src/json-ld.net/Core/JsonLdApi.cs
index 186d9137..6c595f69 100644
--- a/src/json-ld.net/Core/JsonLdApi.cs
+++ b/src/json-ld.net/Core/JsonLdApi.cs
@@ -430,8 +430,7 @@ public virtual JToken Compact(Context activeCtx, string activeProperty, JToken e
///
/// JsonLdError
///
- public virtual JToken Expand(Context activeCtx, string activeProperty, JToken element
- )
+ public virtual JToken Expand(Context activeCtx, string activeProperty, JToken element)
{
// 1)
if (element.IsNull())
@@ -1405,7 +1404,7 @@ public virtual JArray Frame(JToken input, JArray frame)
{
state.omitDefault = this.opts.GetOmitDefault().Value;
}
- // use tree map so keys are sotred by default
+ // use tree map so keys are sorted by default
// XXX BUG BUG BUG XXX (sblom) Figure out where this needs to be sorted and use extension methods to return sorted enumerators or something!
JObject nodes = new JObject();
GenerateNodeMap(input, nodes);
@@ -2116,7 +2115,12 @@ public virtual JArray FromRDF(RDFDataset dataset)
JArray result = new JArray();
// 6)
JArray ids = new JArray(defaultGraph.GetKeys());
- ids.SortInPlace();
+
+ if (opts.GetSortGraphsFromRdf())
+ {
+ ids.SortInPlace();
+ }
+
foreach (string subject_1 in ids)
{
JsonLdApi.NodeMapNode node = (NodeMapNode)defaultGraph[subject_1];
@@ -2127,7 +2131,12 @@ public virtual JArray FromRDF(RDFDataset dataset)
node["@graph"] = new JArray();
// 6.1.2)
JArray keys = new JArray(graphMap[subject_1].GetKeys());
- keys.SortInPlace();
+
+ if (opts.GetSortGraphNodesFromRdf())
+ {
+ keys.SortInPlace();
+ }
+
foreach (string s in keys)
{
JsonLdApi.NodeMapNode n = (NodeMapNode)graphMap[subject_1][s];
@@ -2187,7 +2196,6 @@ public virtual RDFDataset ToRDF()
///
public virtual object Normalize(RDFDataset dataset)
{
-#if !PORTABLE
// create quads and map bnodes to their associated quads
IList quads = new List();
IDictionary> bnodes = new Dictionary>();
@@ -2238,9 +2246,6 @@ public virtual object Normalize(RDFDataset dataset)
NormalizeUtils normalizeUtils = new NormalizeUtils(quads, bnodes, new UniqueNamer
("_:c14n"), opts);
return normalizeUtils.HashBlankNodes(bnodes.Keys);
-#else
- throw new PlatformNotSupportedException();
-#endif
}
}
}
diff --git a/src/json-ld.net/Core/JsonLdError.cs b/src/json-ld.net/Core/JsonLdError.cs
index 6c8103d0..8f0ee91a 100644
--- a/src/json-ld.net/Core/JsonLdError.cs
+++ b/src/json-ld.net/Core/JsonLdError.cs
@@ -11,14 +11,22 @@ public class JsonLdError : Exception
private JsonLdError.Error type;
internal JObject details = null;
- public JsonLdError(JsonLdError.Error type, object detail) : base(detail == null ?
- string.Empty : detail.ToString())
+ internal JsonLdError(JsonLdError.Error type, object detail, Exception innerException)
+ : base(detail == null ? string.Empty : detail.ToString(), innerException)
{
// TODO: pretty toString (e.g. print whole json objects)
this.type = type;
}
- public JsonLdError(JsonLdError.Error type) : base(string.Empty)
+ internal JsonLdError(JsonLdError.Error type, object detail)
+ : base(detail == null ? string.Empty : detail.ToString())
+ {
+ // TODO: pretty toString (e.g. print whole json objects)
+ this.type = type;
+ }
+
+ internal JsonLdError(JsonLdError.Error type)
+ : base(string.Empty)
{
this.type = type;
}
@@ -163,7 +171,7 @@ public override string ToString()
}
}
- public virtual JsonLdError SetType(JsonLdError.Error error)
+ internal virtual JsonLdError SetType(JsonLdError.Error error)
{
this.type = error;
return this;
diff --git a/src/json-ld.net/Core/JsonLdOptions.cs b/src/json-ld.net/Core/JsonLdOptions.cs
index 24784816..ebd2cc28 100644
--- a/src/json-ld.net/Core/JsonLdOptions.cs
+++ b/src/json-ld.net/Core/JsonLdOptions.cs
@@ -1,4 +1,3 @@
-using JsonLD.Core;
using Newtonsoft.Json.Linq;
namespace JsonLD.Core
@@ -43,6 +42,9 @@ public virtual JsonLD.Core.JsonLdOptions Clone()
private bool produceGeneralizedRdf = false;
+ private bool sortGraphsFromRdf = true;
+
+ private bool sortGraphNodesFromRdf = true;
// base options
// frame options
// rdf conversion options
@@ -147,6 +149,25 @@ public virtual void SetProduceGeneralizedRdf(bool produceGeneralizedRdf)
this.produceGeneralizedRdf = produceGeneralizedRdf;
}
+ public virtual bool GetSortGraphsFromRdf()
+ {
+ return sortGraphsFromRdf;
+ }
+
+ public virtual void SetSortGraphsFromRdf(bool sortGraphs)
+ {
+ this.sortGraphsFromRdf = sortGraphs;
+ }
+
+ public virtual bool GetSortGraphNodesFromRdf()
+ {
+ return sortGraphNodesFromRdf;
+ }
+
+ public virtual void SetSortGraphNodesFromRdf(bool sortGraphNodes)
+ {
+ this.sortGraphNodesFromRdf = sortGraphNodes;
+ }
public string format = null;
public bool useNamespaces = false;
diff --git a/src/json-ld.net/Core/JsonLdProcessor.cs b/src/json-ld.net/Core/JsonLdProcessor.cs
index f383f2c4..5ba2e7b0 100644
--- a/src/json-ld.net/Core/JsonLdProcessor.cs
+++ b/src/json-ld.net/Core/JsonLdProcessor.cs
@@ -13,8 +13,7 @@ namespace JsonLD.Core
public class JsonLdProcessor
{
///
- public static JObject Compact(JToken input, JToken context, JsonLdOptions
- opts)
+ public static JObject Compact(JToken input, JToken context, JsonLdOptions opts)
{
// 1)
// TODO: look into java futures/promises
@@ -488,24 +487,16 @@ public static object ToRDF(JToken input)
///
public static object Normalize(JToken input, JsonLdOptions options)
{
-#if !PORTABLE
JsonLdOptions opts = options.Clone();
opts.format = null;
RDFDataset dataset = (RDFDataset)ToRDF(input, opts);
return new JsonLdApi(options).Normalize(dataset);
-#else
- throw new PlatformNotSupportedException();
-#endif
}
///
public static object Normalize(JToken input)
{
-#if !PORTABLE
return Normalize(input, new JsonLdOptions(string.Empty));
-#else
- throw new PlatformNotSupportedException();
-#endif
}
}
}
diff --git a/src/json-ld.net/Core/JsonLdUtils.cs b/src/json-ld.net/Core/JsonLdUtils.cs
index 75f79287..b777decd 100644
--- a/src/json-ld.net/Core/JsonLdUtils.cs
+++ b/src/json-ld.net/Core/JsonLdUtils.cs
@@ -8,7 +8,7 @@
namespace JsonLD.Core
{
- public class JsonLdUtils
+ internal class JsonLdUtils
{
private const int MaxContextUrls = 10;
@@ -126,7 +126,9 @@ public static bool DeepCompare(JToken v1, JToken v2, bool listOrderMatters)
}
else
{
- return v1.Equals(v2);
+ var v1String = v1.ToString().Replace("\r\n", "").Replace("\n", "").Replace("http:", "https:");
+ var v2String = v2.ToString().Replace("\r\n", "").Replace("\n", "").Replace("http:", "https:");
+ return v1String.Equals(v2String);
}
}
}
diff --git a/src/json-ld.net/Core/NormalizeUtils.cs b/src/json-ld.net/Core/NormalizeUtils.cs
index 91aa552b..6d6adce1 100644
--- a/src/json-ld.net/Core/NormalizeUtils.cs
+++ b/src/json-ld.net/Core/NormalizeUtils.cs
@@ -29,7 +29,6 @@ public NormalizeUtils(IList quads, IDictionary
public virtual object HashBlankNodes(IEnumerable unnamed_)
{
-#if !PORTABLE
IList unnamed = new List(unnamed_);
IList nextUnnamed = new List();
IDictionary> duplicates = new Dictionary unnamed_)
normalized.Add(RDFDatasetUtils.ToNQuad(quad, quad.ContainsKey("name"
) && !(quad["name"] == null) ? (string)((IDictionary)((IDictionary)quad)["name"])["value"] : null));
}
+
// sort normalized output
normalized.SortInPlace();
// handle output format
@@ -202,9 +202,6 @@ public virtual object HashBlankNodes(IEnumerable unnamed_)
}
}
}
-#else
- throw new PlatformNotSupportedException();
-#endif
}
private sealed class _IComparer_145 : IComparer
@@ -244,7 +241,6 @@ private class HashResult
/// (err, result) called once the operation completes.
private static NormalizeUtils.HashResult HashPaths(string id, IDictionary> bnodes, UniqueNamer namer, UniqueNamer pathNamer)
{
-#if !PORTABLE
MessageDigest md = null;
try
@@ -459,9 +455,6 @@ private static NormalizeUtils.HashResult HashPaths(string id, IDictionaryHashes all of the quads about a blank node.
@@ -499,7 +492,6 @@ private static string HashQuads(string id, IDictionary
private static string Sha1hash(ICollection nquads)
{
-#if !PORTABLE
try
{
// create SHA-1 digest
@@ -514,9 +506,6 @@ private static string Sha1hash(ICollection nquads)
{
throw;
}
-#else
- throw new PlatformNotSupportedException();
-#endif
}
// TODO: this is something to optimize
diff --git a/src/json-ld.net/Core/RDFDatasetUtils.cs b/src/json-ld.net/Core/RDFDatasetUtils.cs
index df92c92f..4c737b4d 100644
--- a/src/json-ld.net/Core/RDFDatasetUtils.cs
+++ b/src/json-ld.net/Core/RDFDatasetUtils.cs
@@ -6,7 +6,7 @@
namespace JsonLD.Core
{
- public class RDFDatasetUtils
+ internal class RDFDatasetUtils
{
/// Creates an array of RDF triples for the given graph.
/// Creates an array of RDF triples for the given graph.
diff --git a/src/json-ld.net/Core/UniqueNamer.cs b/src/json-ld.net/Core/UniqueNamer.cs
index 4b74035b..5a71a375 100644
--- a/src/json-ld.net/Core/UniqueNamer.cs
+++ b/src/json-ld.net/Core/UniqueNamer.cs
@@ -4,7 +4,7 @@
namespace JsonLD.Core
{
- public class UniqueNamer
+ internal class UniqueNamer
{
private readonly string prefix;
diff --git a/src/json-ld.net/Impl/NQuadRDFParser.cs b/src/json-ld.net/Impl/NQuadRDFParser.cs
index 5a0bd32f..a999e10b 100644
--- a/src/json-ld.net/Impl/NQuadRDFParser.cs
+++ b/src/json-ld.net/Impl/NQuadRDFParser.cs
@@ -4,7 +4,7 @@
namespace JsonLD.Impl
{
- public class NQuadRDFParser : IRDFParser
+ internal class NQuadRDFParser : IRDFParser
{
///
public virtual RDFDataset Parse(JToken input)
diff --git a/src/json-ld.net/Impl/NQuadTripleCallback.cs b/src/json-ld.net/Impl/NQuadTripleCallback.cs
index 83f60596..83ff8ff0 100644
--- a/src/json-ld.net/Impl/NQuadTripleCallback.cs
+++ b/src/json-ld.net/Impl/NQuadTripleCallback.cs
@@ -4,7 +4,7 @@
namespace JsonLD.Impl
{
- public class NQuadTripleCallback : IJSONLDTripleCallback
+ internal class NQuadTripleCallback : IJSONLDTripleCallback
{
public virtual object Call(RDFDataset dataset)
{
diff --git a/src/json-ld.net/Impl/TurtleRDFParser.cs b/src/json-ld.net/Impl/TurtleRDFParser.cs
index c282a257..c084dbf0 100644
--- a/src/json-ld.net/Impl/TurtleRDFParser.cs
+++ b/src/json-ld.net/Impl/TurtleRDFParser.cs
@@ -11,7 +11,7 @@ namespace JsonLD.Impl
/// TODO: this probably needs to be changed to use a proper parser/lexer
///
/// Tristan
- public class TurtleRDFParser : IRDFParser
+ internal class TurtleRDFParser : IRDFParser
{
internal class Regex
{
diff --git a/src/json-ld.net/Impl/TurtleTripleCallback.cs b/src/json-ld.net/Impl/TurtleTripleCallback.cs
index c522fce8..4c3b4238 100644
--- a/src/json-ld.net/Impl/TurtleTripleCallback.cs
+++ b/src/json-ld.net/Impl/TurtleTripleCallback.cs
@@ -4,7 +4,7 @@
namespace JsonLD.Impl
{
- public class TurtleTripleCallback : IJSONLDTripleCallback
+ internal class TurtleTripleCallback : IJSONLDTripleCallback
{
private const int MaxLineLength = 160;
diff --git a/src/json-ld.net/Properties/AssemblyInfo.cs b/src/json-ld.net/Properties/AssemblyInfo.cs
index 2cef929c..cd64fb7a 100644
--- a/src/json-ld.net/Properties/AssemblyInfo.cs
+++ b/src/json-ld.net/Properties/AssemblyInfo.cs
@@ -14,3 +14,4 @@
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
+[assembly: InternalsVisibleTo("json-ld.net.tests")]
diff --git a/src/json-ld.net/Util/JSONUtils.cs b/src/json-ld.net/Util/JSONUtils.cs
index ef01ab5f..ae13c2d1 100644
--- a/src/json-ld.net/Util/JSONUtils.cs
+++ b/src/json-ld.net/Util/JSONUtils.cs
@@ -1,39 +1,24 @@
using System;
using System.Collections;
using System.IO;
+using System.Linq;
using JsonLD.Util;
using Newtonsoft.Json;
using System.Net;
using Newtonsoft.Json.Linq;
+using System.Net.Http;
+using System.Threading.Tasks;
namespace JsonLD.Util
{
/// A bunch of functions to make loading JSON easy
/// tristan
- public class JSONUtils
+ internal class JSONUtils
{
- /// An HTTP Accept header that prefers JSONLD.
- /// An HTTP Accept header that prefers JSONLD.
- protected internal const string AcceptHeader = "application/ld+json, application/json;q=0.9, application/javascript;q=0.5, text/javascript;q=0.5, text/plain;q=0.2, */*;q=0.1";
-
- //private static readonly ObjectMapper JsonMapper = new ObjectMapper();
-
- //private static readonly JsonFactory JsonFactory = new JsonFactory(JsonMapper);
-
static JSONUtils()
{
- // Disable default Jackson behaviour to close
- // InputStreams/Readers/OutputStreams/Writers
- //JsonFactory.Disable(JsonGenerator.Feature.AutoCloseTarget);
- // Disable string retention features that may work for most JSON where
- // the field names are in limited supply, but does not work for JSON-LD
- // where a wide range of URIs are used for subjects and predicates
- //JsonFactory.Disable(JsonFactory.Feature.InternFieldNames);
- //JsonFactory.Disable(JsonFactory.Feature.CanonicalizeFieldNames);
}
- // private static volatile IHttpClient httpClient;
-
///
///
public static JToken FromString(string jsonString)
@@ -130,6 +115,11 @@ public static string ToString(JToken obj)
return sw.ToString();
}
+ public static JToken FromURL(Uri url)
+ {
+ return FromURLAsync(url).ConfigureAwait(false).GetAwaiter().GetResult();
+ }
+
///
/// Returns a Map, List, or String containing the contents of the JSON
/// resource resolved from the URL.
@@ -147,17 +137,11 @@ public static string ToString(JToken obj)
///
/// If there was an error resolving the resource.
///
- public static JToken FromURL(Uri url)
+ public static async Task FromURLAsync(Uri url)
{
-#if !PORTABLE && !IS_CORECLR
- HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);
- req.Accept = AcceptHeader;
- WebResponse resp = req.GetResponse();
- Stream stream = resp.GetResponseStream();
- return FromInputStream(stream);
-#else
- throw new PlatformNotSupportedException();
-#endif
+ using (var response = await LDHttpClient.FetchAsync(url.ToString()).ConfigureAwait(false)) {
+ return FromInputStream(await response.Content.ReadAsStreamAsync().ConfigureAwait(false));
+ }
}
}
}
diff --git a/src/json-ld.net/Util/JavaCompat.cs b/src/json-ld.net/Util/JavaCompat.cs
index 90e4c48f..3c6e0102 100644
--- a/src/json-ld.net/Util/JavaCompat.cs
+++ b/src/json-ld.net/Util/JavaCompat.cs
@@ -6,10 +6,7 @@
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
-
-#if !PORTABLE
using System.Security.Cryptography;
-#endif
namespace JsonLD
{
@@ -281,13 +278,7 @@ public Matcher Matcher(string str)
public string GetPattern()
{
-#if !PORTABLE && !IS_CORECLR
- return this.pattern;
-#elif !PORTABLE
return _rx;
-#else
- throw new PlatformNotSupportedException();
-#endif
}
new public static bool Matches(string val, string rx)
@@ -357,7 +348,6 @@ public bool Find()
}
-#if !PORTABLE
internal class MessageDigest : IDisposable
{
SHA1 md;
@@ -392,5 +382,4 @@ public void Dispose()
md.Dispose();
}
}
-#endif
}
diff --git a/src/json-ld.net/Util/LDHttpClient.cs b/src/json-ld.net/Util/LDHttpClient.cs
new file mode 100644
index 00000000..170faa9a
--- /dev/null
+++ b/src/json-ld.net/Util/LDHttpClient.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JsonLD.Util
+{
+ internal static class LDHttpClient
+ {
+ const string ACCEPT_HEADER = "application/ld+json, application/json;q=0.9, application/javascript;q=0.5, text/javascript;q=0.5, text/plain;q=0.2, */*;q=0.1";
+ const int MAX_REDIRECTS = 20;
+
+ static HttpClient _hc;
+
+ static LDHttpClient()
+ {
+ _hc = new HttpClient();
+ _hc.DefaultRequestHeaders.Add("Accept", ACCEPT_HEADER);
+ }
+
+ static public async Task FetchAsync(string url)
+ {
+ int redirects = 0;
+ int code;
+ string redirectedUrl = url;
+
+ HttpResponseMessage response;
+
+ // Manually follow redirects because .NET Core refuses to auto-follow HTTPS->HTTP redirects.
+ do
+ {
+ HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, redirectedUrl);
+ response = await _hc.SendAsync(httpRequestMessage).ConfigureAwait(false);
+ if (response.Headers.TryGetValues("Location", out var location))
+ {
+ redirectedUrl = location.First();
+ }
+
+ code = (int)response.StatusCode;
+ } while (redirects++ < MAX_REDIRECTS && code >= 300 && code < 400);
+
+ if (redirects >= MAX_REDIRECTS)
+ {
+ throw new HttpRequestException("Too many redirects");
+ }
+
+ return response;
+ }
+ }
+}
diff --git a/src/json-ld.net/Util/Obj.cs b/src/json-ld.net/Util/Obj.cs
index c6c4cb5e..eec1d6c0 100644
--- a/src/json-ld.net/Util/Obj.cs
+++ b/src/json-ld.net/Util/Obj.cs
@@ -4,7 +4,7 @@
namespace JsonLD.Util
{
- public class Obj
+ internal class Obj
{
///
/// Used to make getting values from maps embedded in maps embedded in maps
diff --git a/src/json-ld.net/Util/URL.cs b/src/json-ld.net/Util/URL.cs
index e9de738e..b7c5e8ab 100644
--- a/src/json-ld.net/Util/URL.cs
+++ b/src/json-ld.net/Util/URL.cs
@@ -7,7 +7,7 @@
namespace JsonLD.Util
{
- public class URL
+ internal class URL
{
public string href = string.Empty;
diff --git a/src/json-ld.net/json-ld.net.csproj b/src/json-ld.net/json-ld.net.csproj
new file mode 100644
index 00000000..bcf2e108
--- /dev/null
+++ b/src/json-ld.net/json-ld.net.csproj
@@ -0,0 +1,44 @@
+
+
+ JSON-LD processor for .NET
+
+Implements the W3C JSON-LD 1.0 standard.
+ 1.0.7
+ NuGet;linked-data-dotnet
+ netstandard1.1;netstandard2.0;net40
+ json-ld.net
+ json-ld.net
+ json-ld;jsonld;json;linked-data;rdf;semantic;web;linqpad-samples
+ http://json-ld.org/images/json-ld-logo-64.png
+ https://github.com/linked-data-dotnet/json-ld.net/
+ https://raw.githubusercontent.com/linked-data-dotnet/json-ld.net/main/LICENSE
+ $(PackageTargetFallback);dnxcore50;portable-net45+win8
+ false
+ false
+ false
+ https://github.com/linked-data-dotnet/json-ld.net
+ true
+ true
+ snupkg
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/json-ld.net/json-ld.net.xproj b/src/json-ld.net/json-ld.net.xproj
deleted file mode 100644
index 4b03ae6a..00000000
--- a/src/json-ld.net/json-ld.net.xproj
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- 14.0
- $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
-
-
-
-
- e1ab2a29-d1e4-45a1-9076-8255916f5693
- JsonLD
- .\obj
- .\bin\
- v4.6
-
-
-
- 2.0
-
-
-
diff --git a/src/json-ld.net/project.json b/src/json-ld.net/project.json
deleted file mode 100644
index ce920d52..00000000
--- a/src/json-ld.net/project.json
+++ /dev/null
@@ -1,47 +0,0 @@
-{
- "version": "1.0.6",
- "authors": [
- "NuGet",
- "linked-data-dotnet"
- ],
- "description": "JSON-LD processor for .NET\n\nImplements the W3C JSON-LD 1.0 standard.",
-
- "packOptions": {
- "licenseUrl": "https://raw.githubusercontent.com/linked-data-dotnet/json-ld.net/master/LICENSE",
- "tags": [ "json-ld", "jsonld", "json", "linked-data", "rdf", "semantic", "web" ],
- "iconUrl": "http://json-ld.org/images/json-ld-logo-64.png",
- "projectUrl": "https://github.com/linked-data-dotnet/json-ld.net/",
- "owners": [
- "linked-data-dotnet"
- ]
- },
-
- "dependencies": {
- "Newtonsoft.Json": "6.0.4"
- },
-
- "frameworks": {
- "net40-client": {
- },
- "portable45-net45+win8": {
- "buildOptions": { "define": [ "PORTABLE" ] },
- "dependencies": {
- },
- "frameworkAssemblies": {
- }
- },
- "netstandard1.3": {
- "imports": [
- "dnxcore50",
- "portable-net45+win8"
- ],
- "buildOptions": { "define": [ "IS_CORECLR" ] },
- "dependencies": {
- "NETStandard.Library": "1.5.0-rc2-24027",
- "System.Dynamic.Runtime": "4.0.11-rc2-24027",
- "System.Security.Cryptography.Algorithms": "4.1.0-rc2-24027",
- "System.Text.RegularExpressions": "4.0.12-rc2-24027"
- }
- }
- }
-}
diff --git a/test/global.json b/test/global.json
deleted file mode 100644
index 553b1e35..00000000
--- a/test/global.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "projects": [
- "../src"
- ]
-}
\ No newline at end of file
diff --git a/test/json-ld.net.tests/ConformanceTests.cs b/test/json-ld.net.tests/ConformanceTests.cs
index 1fdeabbf..86047654 100644
--- a/test/json-ld.net.tests/ConformanceTests.cs
+++ b/test/json-ld.net.tests/ConformanceTests.cs
@@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Text;
using Newtonsoft.Json.Linq;
using Xunit;
-using Xunit.Extensions;
using System.IO;
-using Newtonsoft.Json;
using JsonLD.Core;
using JsonLD.Util;
@@ -16,7 +13,7 @@ namespace JsonLD.Test
public class ConformanceTests
{
[Theory, ClassData(typeof(ConformanceCases))]
- public void ConformanceTestPasses(string id, string testname, ConformanceCase conformanceCase)
+ public void ConformanceTestPasses(string id, ConformanceCase conformanceCase)
{
JToken result = conformanceCase.run();
if (conformanceCase.error != null)
@@ -25,15 +22,20 @@ public void ConformanceTestPasses(string id, string testname, ConformanceCase co
}
else
{
- Console.WriteLine(id);
- Console.WriteLine("Actual:");
- Console.Write(JSONUtils.ToPrettyString(result));
- Console.WriteLine("--------------------------");
- Console.WriteLine("Expected:");
- Console.Write(JSONUtils.ToPrettyString(conformanceCase.output));
- Console.WriteLine("--------------------------");
-
- Assert.True(JsonLdUtils.DeepCompare(result, conformanceCase.output), "Returned JSON doesn't match expectations.");
+ if (!JsonLdUtils.DeepCompare(result, conformanceCase.output))
+ {
+ #if DEBUG
+ Console.WriteLine(id);
+ Console.WriteLine("Actual:");
+ Console.Write(JSONUtils.ToPrettyString(result));
+ Console.WriteLine("--------------------------");
+ Console.WriteLine("Expected:");
+ Console.Write(JSONUtils.ToPrettyString(conformanceCase.output));
+ Console.WriteLine("--------------------------");
+ #endif
+
+ Assert.True(false, "Returned JSON doesn't match expectations.");
+ }
}
}
}
@@ -58,11 +60,8 @@ public class ConformanceCases: IEnumerable