Skip to content
Scott McDonald edited this page Feb 5, 2019 · 4 revisions

The purpose of this section is to present a high-level overview of the JsonApiFramework.

The following diagram demonstrates a standard HTTP GET request of a JSON API document between a client application and hypermedia API server and how JsonApiFramework could be utilized on both the client-side and server-side to make this interaction effortless with further details on the following top-level JsonApiFramework concepts:

  • Service Model
  • Document
  • Document Context
  • Reading JSON API Documents
  • Building/Writing JSON API Documents

Service Model

The ServiceModel represents the domain model of the resources produced and consumed by the hypermedia API server or client application. The ServiceModel contains the metadata of individual resources and the schema of how resources are related to each other through relationships (to-one or to-many).

The term resource is used here to represent and mean both the JSON API resource and .NET CLR resource.

The ServiceModel is used by a DocumentContext in the realization of the following services:

  • Conversion between JSON API resources and CLR resources
  • Generation of JSON API standard hypermedia for JSON API links and relationships

See XXX to learn more.

Example

The following is an example Blogging domain model of CLR resources that could be represented by a ServiceModel.

namespace Blogging
{
    public class Blog
    {
        public long BlogId { get; set; }
        public string Name { get; set; }
    }
    
    public class Article
    {
        public long ArticleId { get; set; }
        public string Title { get; set; }
        public string Text { get; set; }
    }
    
    public class Comment
    {
        public long CommentId { get; set; }
        public string Body { get; set; }
    }
    
    public class Person
    {
        public long PersonId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Twitter { get; set; }
    }
}

Service Model Configuration

The ServiceModel is configured by developers for the respective domain model for the system under consideration. A ServiceModel is configured in any of the following ways:

  • Built inside a derived DocumentContext by overriding the OnServiceModelCreating method.
  • Built outside a derived DocumentContext and configured for use by overriding the OnConfiguring method.
  • Built outside a DocumentContext and passed in the constructor of a DocumentContext.

ServiceModel configuration can be supplemented by the use of conventions thus making the configuration of the ServiceModel convention-based as desired by the developer.

Any developer explicit configuration will always take precedence over any convention-based configuration.

Example (Explicit)

The following is an example of a ServiceModel configuration with explicit configuration of the resource types for the previous Blogging domain model of resources.

namespace Blogging
{
    public class BlogConfiguration : ResourceTypeBuilder<Blog>
    {
        public BlogConfiguration()
        {
            // Hypermedia
            this.Hypermedia()
                .SetApiCollectionPathSegment("blogs");

            // ResourceIdentity
            this.ResourceIdentity(x => x.BlogId)
                .SetApiType("blogs");

            // Attributes
            this.Attribute(x => x.Name)
                .SetApiPropertyName("name");

            // Relationships
            this.ToManyRelationship<Article>("articles")
                .SetApiRelPathSegment("articles");
        }
    }

    public class ArticleConfiguration : ResourceTypeBuilder<Article>
    {
        public ArticleConfiguration()
        {
            // Hypermedia
            this.Hypermedia()
                .SetApiCollectionPathSegment("articles");

            // ResourceIdentity
            this.ResourceIdentity(x => x.ArticleId)
                .SetApiType("articles");

            // Attributes
            this.Attribute(x => x.Title)
                .SetApiPropertyName("title");

            this.Attribute(x => x.Text)
                .SetApiPropertyName("text");

            // Relationships
            this.ToOneRelationship<Person>("author")
                .SetApiRelPathSegment("author");

            this.ToManyRelationship<Comment>("comments")
                .SetApiRelPathSegment("comments");
        }
    }

    public class CommentConfiguration : ResourceTypeBuilder<Comment>
    {
        public CommentConfiguration()
        {
            // Hypermedia
            this.Hypermedia()
                .SetApiCollectionPathSegment("comments");

            // ResourceIdentity
            this.ResourceIdentity(x => x.Id)
                .SetApiType("comments");

            // Attributes
            this.Attribute(x => x.Body)
                .SetApiPropertyName("body");

            // Relationships
            this.ToOneRelationship<Person>("author")
                .SetApiRelPathSegment("author");
        }
    }

    public class PersonConfiguration : ResourceTypeBuilder<Person>
    {
        public PersonConfiguration()
        {
            // Hypermedia
            this.Hypermedia()
                .SetApiCollectionPathSegment("people");

            // ResourceIdentity
            this.ResourceIdentity(x => x.Id)
                .SetApiType("people");

            // Attributes
            this.Attribute(x => x.FirstName)
                .SetApiPropertyName("first-name");

            this.Attribute(x => x.LastName)
                .SetApiPropertyName("last-name");

            this.Attribute(x => x.Twitter)
                .SetApiPropertyName("twitter");

            // Relationships
            this.ToManyRelationship<Article>("articles")
                .SetApiRelPathSegment("articles");

            this.ToManyRelationship<Comment>("comments")
                .SetApiRelPathSegment("comments");
        }
    }
}

Example (Convention-Based)

The following is an example of a ServiceModel convention-based configuration for the previous Blogging domain model of CLR resources with the help of the following conventions:

  • Convention for JSON API type to be pluralized from the C# type name and use the JSON API standard for member names (lower-cased and hyphenated)
  • Convention for JSON API attributes to use the JSON API standard for member names (lower-cased and hyphenated)
  • Convention for auto-discovery of all public properties as JSON API attributes

With these conventions the only thing left to configure is the relationships between resources.

namespace Blogging
{
    public class BlogConfiguration : ResourceTypeBuilder<Blog>
    {
        public BlogConfiguration()
        {
            // Relationships
            this.ToManyRelationship<Article>("articles");
        }
    }

    public class ArticleConfiguration : ResourceTypeBuilder<Article>
    {
        public ArticleConfiguration()
        {
            // Relationships
            this.ToOneRelationship<Person>("author");
            this.ToManyRelationship<Comment>("comments");
        }
    }

    public class CommentConfiguration : ResourceTypeBuilder<Comment>
    {
        public CommentConfiguration()
        {
            // Relationships
            this.ToOneRelationship<Person>("author");
        }
    }

    public class PersonConfiguration : ResourceTypeBuilder<Person>
    {
        public PersonConfiguration()
        {
            // Relationships
            this.ToManyRelationship<Article>("articles");
            this.ToManyRelationship<Comment>("comments");
        }
    }
}

Document

The Document represents a JSON API document from the JSON API specification. The Document is the top-level class for a package of classes that represent the domain model of the JSON API specification.

The JsonApiFramework abstracts any object that can serialize/deserialize itself to/from JSON respectively with the JsonObject class. Document uses the services JsonObject provides to serialize/deserialize itself to/from JSON respectively.

JsonApiFramework uses the JSON.NET library for the actual implementation of serialization and deserialization between JSON and C# objects.

Example JSON Serialization

var json = document.ToJson();

Example JSON Deserialization

var document = JsonObject.Parse<Document>(json);

Document Context

The DocumentContext represents a session with a JSON API document for reading and writing of CLR resources. The DocumentContext uses a ServiceModel that represents the domain model of the resources produced and consumed by the hypermedia API server or client application for the realization of the following services:

  • Conversion between JSON API resources and CLR resources
  • Generation of JSON API standard hypermedia for JSON API links and relationships

Example

This is an example of a derived DocumentContext class for the Blogging domain model of CLR resources where the ServiceModel is configured internal to the derived DocumentContext class with conventions and explicit configurations defined earlier.

namespace Blogging
{
    public class BloggingDocumentContext : DocumentContext
    {
        protected override void OnConventionSetCreating(IConventionSetBuilder conventionSetBuilder)
        {
            Contract.Requires(conventionSetBuilder != null);

            conventionSetBuilder.ApiTypeNamingConventions()
                                .AddPluralNamingConvention()
                                .AddStandardMemberNamingConvention();

            conventionSetBuilder.ApiAttributeNamingConventions()
                                .AddStandardMemberNamingConvention();

            conventionSetBuilder.ResourceTypeConventions()
                                .AddPropertyDiscoveryConvention();
        }

        protected override void OnServiceModelCreating(IServiceModelBuilder serviceModelBuilder)
        {
            Contract.Requires(serviceModelBuilder != null);

            serviceModelBuilder.Configurations.Add(new ArticleConfiguration());
            serviceModelBuilder.Configurations.Add(new BlogConfiguration());
            serviceModelBuilder.Configurations.Add(new CommentConfiguration());
            serviceModelBuilder.Configurations.Add(new PersonConfiguration());
        }
    }
}

Reading JSON API Documents

CLR resources are read from the JSON API document using a DocumentContext object. Reading CLR resources from JSON API documents is the same for both client-side and server-side development.

See XXX to learn more.

Example

This is an example of using the previous Blogging DocumentContext object to read both document-level and resource-level things about the JSON API document.

// Deserialize JSON into JSON API document. 
var document = JsonObject.Parse<Document>(json);

// Read JSON API document.
using (var context = new BloggingContext(document))
{
    // Read document-level things.
    var documentType = context.GetDocumentType();
    var documentJsonApi = context.GetDocumentJsonApi();
    var documentMeta = context.GetDocumentMeta();
    var documentLinks = context.GetDocumentLinks();

    // Read resource-level things.

    // .. blog
    var blog = context.GetResourceCollection<Blog>()
                      .SingleOrDefault(blog => blog.Name == "JsonApiFramework");
    Assert(blog != null);

    var blogLinks = context.GetResourceLinks(blog);
    var blogRelationships = context.GetResourceRelationships(blog);

    // .. blog related articles
    Assert(blogRelationships != null);

    var blogToArticlesRelationship = blogRelationships.GetRelationship("articles");
    Assert(blogToArticlesRelationship != null);

    var blogToArticles = context.GetRelatedToManyResourceCollection<Article>(blogToArticlesRelationship)
                                .ToList();
}

Building/Writing JSON API Documents

JSON API documents are created with CLR resources using a DocumentContext object. Writing JSON API documents with CLR resources is different for both client-side and server-side development in the context of the JSON API specification.

See XXX to learn more.

Example Server-Side

This is an example of using the previous Blogging DocumentContext object to build and write JSON with framework generated hypermedia for a hypermedia API server request for the handling of a HTTP GET request.

var currentRequestUrl = HttpContext.Current.Request.Url;
using (var context = new BloggingContext())
{
    // Build/Write JSON API document.
    var document = context
        .NewDocument(currentRequestUrl)
            // Document links
            .Links()
                .AddSelfLink()
            .LinksEnd()

            // Convert CLR Blog resource to JSON API resource
            .Resource(blog)
                // Blog relationships
                .Relationships()
                    .AddRelationship("articles", Keywords.Self, Keywords.Related)
                .RelationshipsEnd()
                
                // Blog links
                .Links()
                    .AddSelfLink()
                .LinksEnd()
            .ResourceEnd()

            .Included()
                // Convert related "to-many" CLR Article resources to JSON API resources
                // Automatically generate "to-many" resource linkage in blog to related articles
                .ToMany(blog, "articles", articles)
                    // Article relationships
                    .Relationships()
                        .AddRelationship("author", Keywords.Self, Keywords.Related)
                    .RelationshipsEnd()

                    // Article links
                    .Links()
                        .AddSelfLink()
                    .LinksEnd()
                .ToManyEnd()
            .IncludedEnd()

        .WriteDocument();

    // Serialize JSON API document into JSON. 
    var json = document.ToJson();
    return json;
}

Example Client-Side

This is an example of using the previous Blogging DocumentContext object to build and write JSON for a client application request to create a resource with a HTTP POST request.

using (var context = new BloggingContext())
{
    // Build/Write JSON API document.
    var document = context
        .NewDocument()
            Resource<Article>()
                .Attributes()
                    .AddAttribute(article => article.Title, "JsonApiFramework")
                    .AddAttribute(article => article.Text,
"JsonApiFramework is a fast, extensible, and portable C#/.NET framework for the reading and writing "
"of client-side and server-side JSON API documents based on the domain model of the hypermedia API resources.")
                .AttributesEnd()
            .ResourceEnd()
        .WriteDocument();

    // Serialize JSON API document into JSON. 
    var json = document.ToJson();
    return json;
}