Coder Social home page Coder Social logo

jonnii / jsonapiserializer Goto Github PK

View Code? Open in Web Editor NEW

This project forked from codecutout/jsonapiserializer

0.0 2.0 0.0 1.45 MB

JsonApiSerializer supports configurationless serializing and deserializing objects into the json:api format (http://jsonapi.org).

License: MIT License

C# 100.00%

jsonapiserializer's Introduction

JsonApiSerializer

The JsonApiSerializer provides configurationless serializing and deserializing objects into the json:api format.

It is implemented as an JsonSerializerSettings for Json.Net with usage being the standard Json.Net methods passing in a JsonApiSerializerSettings

//To serialize a POCO in json:api format
string json = JsonConvert.SerializeObject(articles, new JsonApiSerializerSettings());

//To deserialize to a POCO from json:api format
Article[] articles = JsonConvert.DeserializeObject<Article[]>(json, new JsonApiSerializerSettings());

NuGet version

Example

Given an object model like:

public class Article
{
    public string Id { get; set; }

    public string Title { get; set; }

    public Person Author { get; set; }

    public List<Comment> Comments { get; set; }
}

public class Comment
{
    public string Id { get; set; }

    public string Body { get; set; }

    public Person Author { get; set; }
}

public class Person
{
    public string Id { get; set; }

    [JsonProperty(propertyName: "first-name")] //uses standard Json.NET attributes to control serialization
    public string FirstName { get; set; }

    [JsonProperty(propertyName: "last-name")]
    public string LastName { get; set; }

    public string Twitter { get; set; }
}

Deserialization

and json:api content that look something like

{
  "data": [{
    "type": "articles",
    "id": "1",
    "attributes": {
        "title": "JSON API paints my bikeshed!"
    },
    "relationships": {
      "author": {
        "data": { "type": "people", "id": "9" }
      },
      "comments": {
        "data": [
          { "type": "comments", "id": "5" },
          { "type": "comments", "id": "12" }
        ]
      }
    }
  }],
  "included": [{
    "type": "people",
    "id": "9",
    "attributes": {
      "first-name": "Dan",
      "last-name": "Gebhardt",
      "twitter": "dgeb"
    },
  }, {
    "type": "comments",
    "id": "5",
    "attributes": {
      "body": "First!"
    },
    "relationships": {
      "author": {
        "data": { "type": "people", "id": "2" }
      }
    },
  }, {
    "type": "comments",
    "id": "12",
    "attributes": {
      "body": "I like XML better"
    },
    "relationships": {
      "author": {
        "data": { "type": "people", "id": "9" }
      }
    },
  }]
}

We can deserialize with

Article[] articles = JsonConvert.DeserializeObject<Article[]>(json, new JsonApiSerializerSettings());

Serialization

We can also generate the JSON from our object model

var author = new Person
{
    Id = "9",
    FirstName = "Dan",
    LastName = "Gebhardt",
    Twitter = "dgeb",
};

var articles = new[] {
    new Article
    {
        Id = "1",
        Title = "JSON API paints my bikeshed!",
        Author = author,
        Comments = new List<Comment>
        {
            new Comment
            {
                Id = "5",
                Body = "First!",
                Author = new Person
                {
                    Id = "2"
                },
            },
            new Comment
            {
                Id = "12",
                Body = "I like XML better",
                Author = author,
            }
        }
    }
};

//will produce the same json:api json value
string json = JsonConvert.SerializeObject(articles, new JsonApiSerializerSettings());

Extracting more properties

json:api allows for additional information to be stored at objects and relationships, We provide some helper classes that allows you to access these properties.

DocumentRoot

DocumentRoot<TData> allows you to get and set json:api values at the root document level

{
	"jsonapi": {
		"version":"1.0"
	},
	"links": {
		"self": "http://example.com/articles",
	},
	"meta": {
		"created": "2017-04-02T23:28:35"
	},
	"data": [{
  		"id" : "1",
		"type": "articles",
		"attributes": {
	  		"title": "document root example"
		}
	}]
}
DocumentRoot<Article[]> articlesRoot = JsonConvert.DeserializeObject<DocumentRoot<Article[]>>(json, new JsonApiSerializerSettings());
Assert.Equal("1.0" articlesRoot.JsonApi.Version);
Assert.Equal("http://example.com/articles", articlesRoot.Links["self"].Href);
Assert.Equal("2017-04-02T23:28:35", articlesRoot.Meta["created"]);

Relationships

Relationship<TData> can be used in an object to get and set additional json:api around relationships such as links or meta

{
	"data": [{
		"type": "articles",
		"id": "1",
		"attributes": {
			"title": "JSON API paints my bikeshed!"
		},
		"relationships": {
			"author": {
				"links": {
				  "self": "http://example.com/articles/1/relationships/author",
				  "related": "http://example.com/articles/1/author"
				},
				"data": { "type": "people", "id": "9" }
			}
		}
	}]
}
public class Article
{
    public string Id { get; set; }

    public string Title { get; set; }

    public Relationship<Person> Author { get; set; }
}
Article[] articles = JsonConvert.DeserializeObject<Article[]>(json, new JsonApiSerializerSettings());
Assert.Equal("http://example.com/articles/1/relationships/author", articles[0].links["self"].Href);
Assert.Equal("http://example.com/articles/1/author", articles[0].links["related"].Href);

Links

Link can be used to store link values. json:api supports links as either a string or as an object. JsonApiSerializer normalizes this behaviour so in the object model they are always an object.

A Links class is also provided to store a dictionary of links that is typically stored on json:api objects

Types

If you dont specify a type property on your object model JsonApiSerializer will use the class name. If you want to modify this behaviour it is as simple as putting a Type property on a class

public class Person
{
	public string Type { get; set; } = "people"; //sets type to "people"

	public string Id { get; set; }

	[JsonProperty(propertyName: "first-name")]
	public string FirstName { get; set; }

	[JsonProperty(propertyName: "last-name")]
	public string LastName { get; set; }

	public string Twitter { get; set; }
}

Determining relationship objects

By default any class with an "Id" is considered a Resource Object, and it will be treated as a relationship during serialization and deserialization.

This can be overrided during initialization by providing your own JsonConverter (it is strongly recommneded you extend ResourceObjectConverter) when you create the JsonApiSerializerSettings. Your custom 'JsonConverter can override the `CanConvert(Type type)' method.

var settings = new JsonApiSerializerSettings(new MyOwnJsonSerializer())
Article[] articles = JsonConvert.DeserializeObject<Article[]>(json, settings);

Determine object type during deserialization

The default behaviour is to assume that the object type declared on your property is the type that will be created during deserialization. However there are times where you may want the property to be declared as an interface or abstract class and then determine the actual type to instantiate during deserialization.

This can be achieved by creating a custom JsonConvertor extending from ResoruceObjectConvertor and overriding CreateObject. You can then provide custom logic in creating the object, typically this would be done by examing hte json:api type.

    public class MyTypeDeterminingResourceObjectConvertor : ResourceObjectConverter
    {
        protected override object CreateObject(Type objectType, string jsonapiType, JsonSerializer serializer)
        {
            switch (jsonapiType)
            {
                case "vip-person":
                    return new PersonVIP();
                case "person":
                    return new Person();
                default:
                    return base.CreateObject(objectType, jsonapiType, serializer);
            }
        }
    }

This convertor can be added into the system in a number of ways

  1. Adding it globally to all resource objects by specifiying during the creation of the JsonApiSerializerSettings
var settings = new JsonApiSerializerSettings(new MyTypeDeterminingResourceObjectConvertor())
Article[] articles = JsonConvert.DeserializeObject<Article[]>(json, settings);
  1. Adding to everything that matches the CanConvert method on your customer converter. If doing this you would want to override the CanConvert method so it only targets object types you want to use the new convertor.
var settings = new JsonApiSerializerSettings()
settings.Converters.Add(new MyTypeDeterminingResourceObjectConvertor())
Article[] articles = JsonConvert.DeserializeObject<Article[]>(json, settings);
  1. Add to your model by annotating it with a JsonConvertorAttribute. This behaviour is identical to usage by json.net
[JsonConvertor(typeof(MyTypeDeterminingResourceObjectConvertor))]
public IPerson Author {get; set;}

Integrating with Microsoft.AspNetCore.Mvc

You can configure json:api to be the default serialization for your MVC site by reconfiguring the JsonInputFormatter and JsonOutputFormatter to use the JsonApiSerializerSettings

public class Startup
{

	// ...

    public void ConfigureServices(IServiceCollection services)
    {
        // ...

        var sp = services.BuildServiceProvider();
        var logger = sp.GetService<ILoggerFactory>();
        var objectPoolProvider = sp.GetService<ObjectPoolProvider>();

        services.AddMvc(opt => {
            var serializerSettings = new JsonApiSerializerSettings();

            var jsonApiFormatter = new JsonOutputFormatter(serializerSettings, ArrayPool<Char>.Shared);
            opt.OutputFormatters.RemoveType<JsonOutputFormatter>();
            opt.OutputFormatters.Insert(0, jsonApiFormatter);

            var jsonApiInputFormatter = new JsonInputFormatter(logger.CreateLogger<JsonInputFormatter>(), serializerSettings, ArrayPool<Char>.Shared, objectPoolProvider);
            opt.InputFormatters.RemoveType<JsonInputFormatter>();
            opt.InputFormatters.Insert(0, jsonApiInputFormatter);

        });
    }
}

jsonapiserializer's People

Contributors

alex-davies avatar jonnii avatar alarkvell avatar erikpaperik avatar faabergr avatar

Watchers

 avatar James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.