Git Product home page Git Product logo

jsonapiserializer's People

Contributors

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

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsonapiserializer's Issues

Unable to cast object of type 'System.Int32' to type 'System.String'

Always get following error after upgrade from 1.4.0 to >=1.5.0

System.InvalidCastException: Unable to cast object of type 'System.Int32' to type 'System.String'.
   at JsonApiSerializer.JsonConverters.ResourceObjectConverter.WriteJson(JsonWriter writer, Object value, JsonSerializer serializer)
   at JsonApiSerializer.JsonConverters.DocumentRootConverter.WriteJson(JsonWriter writer, Object value, JsonSerializer serializer)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeConvertable(JsonWriter writer, JsonConverter converter, Object value, JsonContract contract, JsonContainerContract collectionContract, JsonProperty containerProperty) in /_/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs:line 652
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriterjsonWriter, Object value, Type objectType) in /_/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs:line 80

Controller method that is called and producing above error:

public async Task<string> GetSampleSheet(string runName)
    {
        SomeObject someObject= new SomeObject();

        var json = await Task.Factory.StartNew(() => JsonConvert.SerializeObject(someObject, Formatting.Indented, new JsonApiSerializerSettings()));
        return json;
    }

If i remove the new JsonApiSerializerSettings() than it works but i need the data object to appear in response.

.csproj (.net core 2.2.0)

<PackageReference Include="Microsoft.AspNetCore.App" />
...
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="JsonApiSerializer" Version="1.6.1" />
...

I checked the code on line 652 of JsonSerializerInternalWriter.cs but it is not clear to me why it would fail there.

Can you help me figure out where the problem lies in?

List<ISomeInterface> will not serialize if `ID` is in `ISomeInterface` inheritance chain

with the model

   public interface IWithId
    {
        string Id { get; set; }
    }

    public interface ILocationWithId : IWithId
    {
    }

    public class LocationWithId : ILocationWithId
    {
        public string Id { get; set; }
        public IEnumerable<ILocationWithId> Parents { get; set; }
    }

run with

            var root = new LocationWithId()
            {
                Id = "Willesdon Green",
                Parents = new ILocationWithId[]
                {
                    new LocationWithId()
                    {
                        Id = "Brent",
                        Parents = new ILocationWithId[]
                        {
                            new LocationWithId()
                            {
                                Id = "London"
                            }
                        }
                    }
                }
            };


            var json = JsonConvert.SerializeObject(root, settings);

produces the following incorrect result (note the array is located at "parents" rather than "data"

{
  "data": {
    "type": "locationwithid",
    "id": "Willesdon Green",
    "relationships": {
      "parents": [
        {
          "data": {
            "id": "Brent",
            "type": "locationwithid"
          }
        }
      ]
    }
  },
  "included": [
    {
      "type": "locationwithid",
      "id": "Brent",
      "relationships": {
        "parents": [
          {
            "data": {
              "id": "London",
              "type": "locationwithid"
            }
          }
        ]
      }
    }
  ]
}

Does not handle null data field

I experience an exception when deserialize an object.

Example:
{"data": null}

Exception:
JsonApiSerializer.Exceptions.JsonApiFormatException: 'Unable to find closing element element'.

Deserializing resource with no relationships included

I have an example json api resource that looks like this:

{ "data": [ { "attributes": { "name": "Test", "description": "Unassigned" }, "relationships": { "areas": { "links": { "self": "https://localhost:44317/api/types/1/relationships/areas", "related": "https://localhost:44317/api/types/1/areas" } } }, "type": "types", "id": "1" } ] }

Note that there is no 'data' in the relationship - this is intentional because we don't want to include them in this request to the server.

I get an exception: Expected to find nested object within data[0].relationships.areas that matches \data\

If I'm reading this right, it sounds like the deserializer is expecting "data". The JSON API spec (https://jsonapi.org/format/#document-resource-object-relationships) says it must contain at least one of the following: links, data, meta.

So I believe in this case data should not be required, because 'links' are present. In which case the relationships should just be empty. Could be totally wrong though ¯_(ツ)_/¯

Usage vs normal WebAPI

Hi,

This is really a question not an issue. I've kicked off my API explorations with .NET Core and the type of approach here (https://docs.microsoft.com/en-us/ef/core/get-started/aspnetcore/existing-db). This project looks perfectly aligned to that, with the same structure for defining the objects etc.

I was wondering when this project is recommended versus using the deserialisation provided by WebAPI when you tell it a particular object is in the request body and should be deserialised from there.

Many thanks!

Aspnet Core Integration Uses Obsolete Constructor

Issue refers to the following documentation: https://github.com/codecutout/JsonApiSerializer#integrating-with-microsoftaspnetcoremvc

Documentation recommends newing up JsonInputFormatter with JsonInputFormatter(ILogger, JsonSerializerSettings, ArrayPool<char>, ObjectPoolProvider) constructor (JsonInputFormatter`4). Per Microsoft, this constructor is now obsoleted: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.formatters.jsoninputformatter.-ctor?view=aspnetcore-2.2#Microsoft_AspNetCore_Mvc_Formatters_JsonInputFormatter__ctor_Microsoft_Extensions_Logging_ILogger_Newtonsoft_Json_JsonSerializerSettings_System_Buffers_ArrayPool_System_Char__Microsoft_Extensions_ObjectPool_ObjectPoolProvider_

It is still possible to use this constructor, but due to likelihood that it will be removed from future versions of of dotnet, I am looking for a better/more future-proof way of integrating. The only remaining non-obsolete constructor requires passing both MvcOptions (available in IServiceCollection.AddMvc`1) and MvcJsonOptions (available in IMvcBuilder.AddJsonOptions`1)), but I've not yet found a way to access both MvcOptions and MvcJsonOptions in the same place in order to use the preferred constructor.

Though this is not definitive, it appears that Microsoft intends that custom serialization settings be achieved via IMvcBuilder.AddJsonOptions`1: aspnet/Mvc#4339 (comment)

I will follow up if I find a good solution for integrating with Aspnet Core WebApi that doesn't go against current "best practices", but wanted to post here as well in case it is relevant to others.

Thanks.
~Jordan

Empty Relationships Being Serialized as Attributes

Hey there,

We have a "Match" resource which we are serializing and sending to a client, within this match resource we have a relationship to a set of "Spectator" resources (Both have an Id property and are setup correctly) however there may not be any spectators watching a match so there is a possibility that this list can be empty.

When serializing the empty list it seems to put it as a attribute rather than a relationship. Whereas it seems to be setup correctly when there is at least one spectator in the list.

Is there any way around this?

Example Output:

    "data": [
        {
            "type": "match",
            "id": "32737895-7e44-408e-914b-e70a578dde88",
            "links": {
                "self": "ommitted",
                "schema": "ommitted"
            },
            "attributes": {
                "shardId": "pc-eu",
                "createdAt": "2018-03-16T17:52:25.5652711Z",
                "duration": 1000,
                "stats": {},
                "tags": {},
                "spectators": {
                    "data": []
                },
                "patchVersion": "master"
            },
            "relationships": {
                "rosters": {
                    "data": [
                        //Many Rosters omitted for brevity
                    ]
                },
                "assets": {
                    "data": [
                        {
                            "id": "3f21af6f-6368-407a-bd59-cbf29bd0dca2",
                            "type": "asset"
                        }
                    ]
                }
            }
        }
    ]

Stack overflow when serializing recursive F# records

The following code produces a stack overflow (regardless of whether it's the parent or child that's serialized at the end):

type Parent = 
  { Id: string
    Data: string
    Child: Child option }
and Child = 
  { Id: string
    Data: string
    mutable Parent: Parent option }

let child = {Id = "b"; Data = "dataB"; Parent = None}
let parent = {Id = "a"; Data = "dataA"; Child = Some child}
child.Parent <- Some parent

JsonConvert.SerializeObject(child, JsonApiSerializerSettings())

On the contrary, it works fine when the types are normal classes:

type Parent() = 
  member val Id: string = "" with get, set
  member val Data: string = "" with get, set
  member val Child: Child option = None with get, set
and Child() = 
  member val Id: string = "" with get, set
  member val Data: string = "" with get, set
  member val Parent: Parent option = None with get, set

let child = Child(Id = "b", Data = "dataB", Parent = None)
let parent = Parent(Id = "a", Data = "dataA", Child = Some child)
child.Parent <- Some parent

JsonConvert.SerializeObject(child, JsonApiSerializerSettings())

I'd really like to use records for my API models though, due to several other benefits. Would it be possible for you to investigate this?

Perhaps it would help you to see what the two approaches compile to. Here's the record types and here's the classes from SharpLab.io.

Record types decompiled to C#
using Microsoft.FSharp.Core;
using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;

[assembly: AssemblyVersion("0.0.0.0")]
[assembly: FSharpInterfaceDataVersion(2, 0, 0)]
[CompilationMapping(SourceConstructFlags.Module)]
public static class _
{
    [CompilationMapping(SourceConstructFlags.RecordType)]
    [Serializable]
    public sealed class Parent : IEquatable<_.Parent>, IStructuralEquatable, IComparable<_.Parent>, IComparable, IStructuralComparable
    {
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        internal string Id@;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        internal string Data@;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        internal FSharpOption<_.Child> Child@;

        [CompilationMapping(SourceConstructFlags.Field, 0)]
        public string Id
        {
            get
            {
                return this.Id@;
            }
        }

        [CompilationMapping(SourceConstructFlags.Field, 1)]
        public string Data
        {
            get
            {
                return this.Data@;
            }
        }

        [CompilationMapping(SourceConstructFlags.Field, 2)]
        public FSharpOption<_.Child> Child
        {
            get
            {
                return this.Child@;
            }
        }

        public Parent(string id, string data, FSharpOption<_.Child> child)
        {
            this.Id@ = id;
            this.Data@ = data;
            this.Child@ = child;
        }

        [CompilerGenerated]
        public override string ToString()
        {
            return ExtraTopLevelOperators.PrintFormatToString<FSharpFunc<_.Parent, string>>(new PrintfFormat<FSharpFunc<_.Parent, string>, Unit, string, string, _.Parent>("%+A")).Invoke(this);
        }

        [CompilerGenerated]
        public sealed override int CompareTo(_.Parent obj)
        {
            if (this != null)
            {
                if (obj == null)
                {
                    return 1;
                }
                IComparer genericComparer = LanguagePrimitives.GenericComparer;
                int num = string.CompareOrdinal(this.Id@, obj.Id@);
                if (num < 0)
                {
                    return num;
                }
                if (num > 0)
                {
                    return num;
                }
                genericComparer = LanguagePrimitives.GenericComparer;
                int num2 = string.CompareOrdinal(this.Data@, obj.Data@);
                if (num2 < 0)
                {
                    return num2;
                }
                if (num2 > 0)
                {
                    return num2;
                }
                return LanguagePrimitives.HashCompare.GenericComparisonWithComparerIntrinsic<FSharpOption<_.Child>>(LanguagePrimitives.GenericComparer, this.Child@, obj.Child@);
            }
            else
            {
                if (obj != null)
                {
                    return -1;
                }
                return 0;
            }
        }

        [CompilerGenerated]
        public sealed override int CompareTo(object obj)
        {
            return this.CompareTo((_.Parent)obj);
        }

        [CompilerGenerated]
        public sealed override int CompareTo(object obj, IComparer comp)
        {
            _.Parent parent = (_.Parent)obj;
            if (this != null)
            {
                if ((_.Parent)obj == null)
                {
                    return 1;
                }
                int num = string.CompareOrdinal(this.Id@, parent.Id@);
                if (num < 0)
                {
                    return num;
                }
                if (num > 0)
                {
                    return num;
                }
                int num2 = string.CompareOrdinal(this.Data@, parent.Data@);
                if (num2 < 0)
                {
                    return num2;
                }
                if (num2 > 0)
                {
                    return num2;
                }
                return LanguagePrimitives.HashCompare.GenericComparisonWithComparerIntrinsic<FSharpOption<_.Child>>(comp, this.Child@, parent.Child@);
            }
            else
            {
                if ((_.Parent)obj != null)
                {
                    return -1;
                }
                return 0;
            }
        }

        [CompilerGenerated]
        public sealed override int GetHashCode(IEqualityComparer comp)
        {
            if (this != null)
            {
                int num = 0;
                num = -1640531527 + (LanguagePrimitives.HashCompare.GenericHashWithComparerIntrinsic<FSharpOption<_.Child>>(comp, this.Child@) + ((num << 6) + (num >> 2)));
                int arg_43_0 = -1640531527;
                string text = this.Data@;
                num = arg_43_0 + (((text == null) ? 0 : text.GetHashCode()) + ((num << 6) + (num >> 2)));
                int arg_65_0 = -1640531527;
                text = this.Id@;
                return arg_65_0 + (((text == null) ? 0 : text.GetHashCode()) + ((num << 6) + (num >> 2)));
            }
            return 0;
        }

        [CompilerGenerated]
        public sealed override int GetHashCode()
        {
            return this.GetHashCode(LanguagePrimitives.GenericEqualityComparer);
        }

        [CompilerGenerated]
        public sealed override bool Equals(object obj, IEqualityComparer comp)
        {
            if (this != null)
            {
                _.Parent parent = obj as _.Parent;
                return parent != null && string.Equals(this.Id@, parent.Id@) && string.Equals(this.Data@, parent.Data@) && LanguagePrimitives.HashCompare.GenericEqualityWithComparerIntrinsic<FSharpOption<_.Child>>(comp, this.Child@, parent.Child@);
            }
            return obj == null;
        }

        [CompilerGenerated]
        public sealed override bool Equals(_.Parent obj)
        {
            if (this != null)
            {
                return obj != null && string.Equals(this.Id@, obj.Id@) && string.Equals(this.Data@, obj.Data@) && LanguagePrimitives.HashCompare.GenericEqualityERIntrinsic<FSharpOption<_.Child>>(this.Child@, obj.Child@);
            }
            return obj == null;
        }

        [CompilerGenerated]
        public sealed override bool Equals(object obj)
        {
            _.Parent parent = obj as _.Parent;
            return parent != null && this.Equals(parent);
        }
    }

    [CompilationMapping(SourceConstructFlags.RecordType)]
    [Serializable]
    public sealed class Child : IEquatable<_.Child>, IStructuralEquatable, IComparable<_.Child>, IComparable, IStructuralComparable
    {
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        internal string Id@;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        internal string Data@;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        public FSharpOption<_.Parent> Parent@;

        [CompilationMapping(SourceConstructFlags.Field, 0)]
        public string Id
        {
            get
            {
                return this.Id@;
            }
        }

        [CompilationMapping(SourceConstructFlags.Field, 1)]
        public string Data
        {
            get
            {
                return this.Data@;
            }
        }

        [CompilationMapping(SourceConstructFlags.Field, 2)]
        public FSharpOption<_.Parent> Parent
        {
            get
            {
                return this.Parent@;
            }
            set
            {
                this.Parent@ = value;
            }
        }

        public Child(string id, string data, FSharpOption<_.Parent> parent)
        {
            this.Id@ = id;
            this.Data@ = data;
            this.Parent@ = parent;
        }

        [CompilerGenerated]
        public override string ToString()
        {
            return ExtraTopLevelOperators.PrintFormatToString<FSharpFunc<_.Child, string>>(new PrintfFormat<FSharpFunc<_.Child, string>, Unit, string, string, _.Child>("%+A")).Invoke(this);
        }

        [CompilerGenerated]
        public sealed override int CompareTo(_.Child obj)
        {
            if (this != null)
            {
                if (obj == null)
                {
                    return 1;
                }
                IComparer genericComparer = LanguagePrimitives.GenericComparer;
                int num = string.CompareOrdinal(this.Id@, obj.Id@);
                if (num < 0)
                {
                    return num;
                }
                if (num > 0)
                {
                    return num;
                }
                genericComparer = LanguagePrimitives.GenericComparer;
                int num2 = string.CompareOrdinal(this.Data@, obj.Data@);
                if (num2 < 0)
                {
                    return num2;
                }
                if (num2 > 0)
                {
                    return num2;
                }
                return LanguagePrimitives.HashCompare.GenericComparisonWithComparerIntrinsic<FSharpOption<_.Parent>>(LanguagePrimitives.GenericComparer, this.Parent@, obj.Parent@);
            }
            else
            {
                if (obj != null)
                {
                    return -1;
                }
                return 0;
            }
        }

        [CompilerGenerated]
        public sealed override int CompareTo(object obj)
        {
            return this.CompareTo((_.Child)obj);
        }

        [CompilerGenerated]
        public sealed override int CompareTo(object obj, IComparer comp)
        {
            _.Child child = (_.Child)obj;
            if (this != null)
            {
                if ((_.Child)obj == null)
                {
                    return 1;
                }
                int num = string.CompareOrdinal(this.Id@, child.Id@);
                if (num < 0)
                {
                    return num;
                }
                if (num > 0)
                {
                    return num;
                }
                int num2 = string.CompareOrdinal(this.Data@, child.Data@);
                if (num2 < 0)
                {
                    return num2;
                }
                if (num2 > 0)
                {
                    return num2;
                }
                return LanguagePrimitives.HashCompare.GenericComparisonWithComparerIntrinsic<FSharpOption<_.Parent>>(comp, this.Parent@, child.Parent@);
            }
            else
            {
                if ((_.Child)obj != null)
                {
                    return -1;
                }
                return 0;
            }
        }

        [CompilerGenerated]
        public sealed override int GetHashCode(IEqualityComparer comp)
        {
            if (this != null)
            {
                int num = 0;
                num = -1640531527 + (LanguagePrimitives.HashCompare.GenericHashWithComparerIntrinsic<FSharpOption<_.Parent>>(comp, this.Parent@) + ((num << 6) + (num >> 2)));
                int arg_43_0 = -1640531527;
                string text = this.Data@;
                num = arg_43_0 + (((text == null) ? 0 : text.GetHashCode()) + ((num << 6) + (num >> 2)));
                int arg_65_0 = -1640531527;
                text = this.Id@;
                return arg_65_0 + (((text == null) ? 0 : text.GetHashCode()) + ((num << 6) + (num >> 2)));
            }
            return 0;
        }

        [CompilerGenerated]
        public sealed override int GetHashCode()
        {
            return this.GetHashCode(LanguagePrimitives.GenericEqualityComparer);
        }

        [CompilerGenerated]
        public sealed override bool Equals(object obj, IEqualityComparer comp)
        {
            if (this != null)
            {
                _.Child child = obj as _.Child;
                return child != null && string.Equals(this.Id@, child.Id@) && string.Equals(this.Data@, child.Data@) && LanguagePrimitives.HashCompare.GenericEqualityWithComparerIntrinsic<FSharpOption<_.Parent>>(comp, this.Parent@, child.Parent@);
            }
            return obj == null;
        }

        [CompilerGenerated]
        public sealed override bool Equals(_.Child obj)
        {
            if (this != null)
            {
                return obj != null && string.Equals(this.Id@, obj.Id@) && string.Equals(this.Data@, obj.Data@) && LanguagePrimitives.HashCompare.GenericEqualityERIntrinsic<FSharpOption<_.Parent>>(this.Parent@, obj.Parent@);
            }
            return obj == null;
        }

        [CompilerGenerated]
        public sealed override bool Equals(object obj)
        {
            _.Child child = obj as _.Child;
            return child != null && this.Equals(child);
        }
    }
}
namespace <StartupCode$_>
{
    internal static class $_
    {
    }
}
Classes decompiled to C#
using Microsoft.FSharp.Core;
using System;
using System.Reflection;

[assembly: AssemblyVersion("0.0.0.0")]
[assembly: FSharpInterfaceDataVersion(2, 0, 0)]
[CompilationMapping(SourceConstructFlags.Module)]
public static class _
{
    [CompilationMapping(SourceConstructFlags.ObjectType)]
    [Serializable]
    public class Parent
    {
        internal string Id@;

        internal string Data@;

        internal FSharpOption<_.Child> Child@;

        public string Id
        {
            get
            {
                return this.Id@;
            }
            set
            {
                this.Id@ = value;
            }
        }

        public string Data
        {
            get
            {
                return this.Data@;
            }
            set
            {
                this.Data@ = value;
            }
        }

        public FSharpOption<_.Child> Child
        {
            get
            {
                return this.Child@;
            }
            set
            {
                this.Child@ = value;
            }
        }

        public Parent() : this()
        {
            this.Id@ = "";
            this.Data@ = "";
            this.Child@ = null;
        }
    }

    [CompilationMapping(SourceConstructFlags.ObjectType)]
    [Serializable]
    public class Child
    {
        internal string Id@;

        internal string Data@;

        internal FSharpOption<_.Parent> Parent@;

        public string Id
        {
            get
            {
                return this.Id@;
            }
            set
            {
                this.Id@ = value;
            }
        }

        public string Data
        {
            get
            {
                return this.Data@;
            }
            set
            {
                this.Data@ = value;
            }
        }

        public FSharpOption<_.Parent> Parent
        {
            get
            {
                return this.Parent@;
            }
            set
            {
                this.Parent@ = value;
            }
        }

        public Child() : this()
        {
            this.Id@ = "";
            this.Data@ = "";
            this.Parent@ = null;
        }
    }
}
namespace <StartupCode$_>
{
    internal static class $_
    {
    }
}

Select'd IEnumerable lists do not serialize properly

An Item similiar to the following

            var root = new LocationWithId()
            {
                Id = "Willesdon Green",
                Parents = Enumerable.Range(0, 3).Select(i => new LocationWithId()
                {
                    Id = $"London_{i}_"
                })
            };

Does not serialize the list correct (array is in the wrong location)

Does JsonApiSerializer supports JsonConverters

Hello, (it's me again!)

I'm encountering a problem with a custom JsonConverter that converts longs to TimeSpans.

It seems to work fine with another Json.NET deserializer I have in the app (the one created by Akavache for offline caching), but fails with JsonApiSerializer (it's not called at all).


The exception:

Newtonsoft.Json.JsonSerializationException: Error converting value 5581 to type 'System.TimeSpan'. Path 'data[0].attributes.duration'.
 |-> System.ArgumentException: Could not cast or convert from System.Int64 to System.TimeSpan.
Full stack trace
01-02 20:14:01.971 E/mono    ( 5029): ReactiveUI.UnhandledErrorException: An object implementing IHandleObservableErrors (often a ReactiveCommand or ObservableAsPropertyHelper) has errored, thereby breaking its observable pipeline. To prevent this, ensure the pipeline does not error, or Subscribe to the ThrownExceptions property of the object in question to handle the erroneous case. ---> Newtonsoft.Json.JsonSerializationException: Error converting value 5581 to type 'System.TimeSpan'. Path 'data[0].attributes.duration'. ---> System.ArgumentException: Could not cast or convert from System.Int64 to System.TimeSpan.
01-02 20:14:01.971 E/mono    ( 5029):   at Newtonsoft.Json.Utilities.ConvertUtils.EnsureTypeAssignable (System.Object value, System.Type initialType, System.Type targetType) [0x00067] in <c19705166c7c4a608e182e859c4de6d2>:0 
01-02 20:14:01.971 E/mono    ( 5029):   at Newtonsoft.Json.Utilities.ConvertUtils.ConvertOrCast (System.Object initialValue, System.Globalization.CultureInfo culture, System.Type targetType) [0x00031] in <c19705166c7c4a608e182e859c4de6d2>:0 
01-02 20:14:01.971 E/mono    ( 5029):   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType (Newtonsoft.Json.JsonReader reader, System.Object value, System.Globalization.CultureInfo culture, Newtonsoft.Json.Serialization.JsonContract contract, System.Type targetType) [0x000b0] in <c19705166c7c4a608e182e859c4de6d2>:0 
01-02 20:14:01.971 E/mono    ( 5029):    --- End of inner exception stack trace ---
01-02 20:14:01.971 E/mono    ( 5029):   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType (Newtonsoft.Json.JsonReader reader, System.Object value, System.Globalization.CultureInfo culture, Newtonsoft.Json.Serialization.JsonContract contract, System.Type targetType) [0x000e0] in <c19705166c7c4a608e182e859c4de6d2>:0 
01-02 20:14:01.971 E/mono    ( 5029):   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x0009b] in <c19705166c7c4a608e182e859c4de6d2>:0 
01-02 20:14:01.971 E/mono    ( 5029):   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Boolean checkAdditionalContent) [0x000db] in <c19705166c7c4a608e182e859c4de6d2>:0 
01-02 20:14:01.971 E/mono    ( 5029):   at Newtonsoft.Json.Serialization.JsonSerializerProxy.DeserializeInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00008] in <c19705166c7c4a608e182e859c4de6d2>:0 
01-02 20:14:01.971 E/mono    ( 5029):   at Newtonsoft.Json.JsonSerializer.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000] in <c19705166c7c4a608e182e859c4de6d2>:0 
01-02 20:14:01.971 E/mono    ( 5029):   at JsonApiSerializer.Util.ReaderUtil.TryPopulateProperty (Newtonsoft.Json.JsonSerializer serializer, System.Object obj, Newtonsoft.Json.Serialization.JsonProperty property, Newtonsoft.Json.JsonReader value) [0x00012] in <114082c9b9634d14a07a89c38a135e48>:0 
01-02 20:14:01.971 E/mono    ( 5029):   at JsonApiSerializer.JsonConverters.ResourceObjectConverter.PopulateProperties (Newtonsoft.Json.JsonSerializer serializer, System.Object obj, Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonObjectContract contract) [0x00064] in <114082c9b9634d14a07a89c38a135e48>:0 
01-02 20:14:01.971 E/mono    ( 5029):   at JsonApiSerializer.JsonConverters.ResourceObjectConverter+<>c__DisplayClass3_0.<ReadJson>b__0 (JsonApiSerializer.Util.ForkableJsonReader dataReader) [0x000fb] in <114082c9b9634d14a07a89c38a135e48>:0 
01-02 20:14:01.971 E/mono    ( 5029):   at JsonApiSerializer.Util.ReaderUtil.ReadInto[TReader,TResult] (TReader reader, System.Text.RegularExpressions.Regex pathRegex, System.Func`2[T,TResult] action) [0x0000d] in <114082c9b9634d14a07a89c38a135e48>:0 
01-02 20:14:01.971 E/mono    ( 5029):   at JsonApiSerializer.JsonConverters.ResourceObjectConverter.ReadJson (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Object existingValue, Newtonsoft.Json.JsonSerializer serializer) [0x0004b] in <114082c9b9634d14a07a89c38a135e48>:0 
01-02 20:14:01.971 E/mono    ( 5029):   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable (Newtonsoft.Json.JsonConverter converter, Newtonsoft.Json.JsonReader reader,
01-02 20:14:01.973 E/mono-rt ( 5029): [ERROR] FATAL UNHANDLED EXCEPTION: ReactiveUI.UnhandledErrorException: An object implementing IHandleObservableErrors (often a ReactiveCommand or ObservableAsPropertyHelper) has errored, thereby breaking its observable pipeline. To prevent this, ensure the pipeline does not error, or Subscribe to the ThrownExceptions property of the object in question to handle the erroneous case. ---> Newtonsoft.Json.JsonSerializationException: Error converting value 5581 to type 'System.TimeSpan'. Path 'data[0].attributes.duration'. ---> System.ArgumentException: Could not cast or convert from System.Int64 to System.TimeSpan.
01-02 20:14:01.973 E/mono-rt ( 5029):   at Newtonsoft.Json.Utilities.ConvertUtils.EnsureTypeAssignable (System.Object value, System.Type initialType, System.Type targetType) [0x00067] in <c19705166c7c4a608e182e859c4de6d2>:0 
01-02 20:14:01.973 E/mono-rt ( 5029):   at Newtonsoft.Json.Utilities.ConvertUtils.ConvertOrCast (System.Object initialValue, System.Globalization.CultureInfo culture, System.Type targetType) [0x00031] in <c19705166c7c4a608e182e859c4de6d2>:0 
01-02 20:14:01.973 E/mono-rt ( 5029):   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType (Newtonsoft.Json.JsonReader reader, System.Object value, System.Globalization.CultureInfo culture, Newtonsoft.Json.Serialization.JsonContract contract, System.Type targetType) [0x000b0] in <c19705166c7c4a608e182e859c4de6d2>:0 
01-02 20:14:01.973 E/mono-rt ( 5029):    --- End of inner exception stack trace ---
01-02 20:14:01.973 E/mono-rt ( 5029):   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType (Newtonsoft.Json.JsonReader reader, System.Object value, System.Globalization.CultureInfo culture, Newtonsoft.Json.Serialization.JsonContract contract, System.Type targetType) [0x000e0] in <c19705166c7c4a608e182e859c4de6d2>:0 
01-02 20:14:01.973 E/mono-rt ( 5029):   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x0009b] in <c19705166c7c4a608e182e859c4de6d2>:0 
01-02 20:14:01.973 E/mono-rt ( 5029):   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Boolean checkAdditionalContent) [0x000db] in <c19705166c7c4a608e182e859c4de6d2>:0 
01-02 20:14:01.973 E/mono-rt ( 5029):   at Newtonsoft.Json.Serialization.JsonSerializerProxy.DeserializeInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00008] in <c19705166c7c4a608e182e859c4de6d2>:0 
01-02 20:14:01.973 E/mono-rt ( 5029):   at Newtonsoft.Json.JsonSerializer.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000] in <c19705166c7c4a608e182e859c4de6d2>:0 
01-02 20:14:01.973 E/mono-rt ( 5029):   at JsonApiSerializer.Util.ReaderUtil.TryPopulateProperty (Newtonsoft.Json.JsonSerializer serializer, System.Object obj, Newtonsoft.Json.Serialization.JsonProperty property, Newtonsoft.Json.JsonReader value) [0x00012] in <114082c9b9634d14a07a89c38a135e48>:0 
01-02 20:14:01.973 E/mono-rt ( 5029):   at JsonApiSerializer.JsonConverters.ResourceObjectConverter.PopulateProperties (Newtonsoft.Json.JsonSerializer serializer, System.Object obj, Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonObjectContract contract) [0x00064] in <114082c9b9634d14a07a89c38a135e48>:0 
01-02 20:14:01.973 E/mono-rt ( 5029):   at JsonApiSerializer.JsonConverters.ResourceObjectConverter+<>c__DisplayClass3_0.<ReadJson>b__0 (JsonApiSerializer.Util.ForkableJsonReader dataReader) [0x000fb] in <114082c9b9634d14a07a89c38a135e48>:0 
01-02 20:14:01.973 E/mono-rt ( 5029):   at JsonApiSerializer.Util.ReaderUtil.ReadInto[TReader,TResult] (TReader reader, System.Text.RegularExpressions.Regex pathRegex, System.Func`2[T,TResult] action) [0x0000d] in <114082c9b9634d14a07a89c38a135e48>:0 
01-02 20:14:01.973 E/mono-rt ( 5029):   at JsonApiSerializer.JsonConverters.ResourceObjectConverter.ReadJson (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Object existingValue, Newtonsoft.Json.JsonSerializer serializer) [0x0004b] in <114082c9b9634d14a07a89c38a135e48>:0 
01-02 20:14:01.973 E/mono-rt ( 5029):   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable (Newtonsoft.Json.JsonConverter converter, Newtonsoft.Json.Js

The property in question:

[JsonConverter(typeof(TimeSpanInSecondsConverter))]
public TimeSpan Duration { get; set; }

TimeSpanInSecondsConverter class (should not be relevant, just in case):
namespace WeBSurg.Core.JsonConverters
{
    using System;

    using Newtonsoft.Json;

    public class TimeSpanInSecondsConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType) => objectType == typeof(TimeSpan);

        public override bool CanRead => true;

        public override bool CanWrite => true;
        
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (objectType != typeof(TimeSpan))
                throw new ArgumentException();

            if (!(reader.Value is long spanSeconds))
                return null;

            return TimeSpan.FromSeconds(spanSeconds);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var duration = (TimeSpan) value;

            writer.WriteValue((int) duration.TotalSeconds);
        }
    }
}

Does JsonApiSerializer supports JsonConverters?

Anyway, thanks for this piece of software, it helped me a lot these last times :)

List of items that reference each other should not have included

 var root = new[]
            {
                new LocationWithId() { Id = "London", Description ="Capital" },
                new LocationWithId() {
                    Id = "Kings-Cross",
                    Parents = new[] { new LocationWithId() { Id = "London", Description= "Capital" } }
                },
                new LocationWithId() {
                    Id = "Farringdon",
                    Parents = new[] { new LocationWithId() { Id = "London", Description = "Capital" } }
                },
                
            };

has an included element, however as London is already in the data the included element is redundant.

Class with JObject as a property is being serialized as a relationship

I have the following classes:

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

        public NestedClass MyObject { get; set; }
    }

    public class NestedClass
    {
        public JObject Data { get; set; } 
    }

and if i do the following:

            var doc = new DocumentRoot<MyClass>()
                    {
                        Data = new MyClass()
                               {
                                   MyObject = new NestedClass()
                                              {
                     
                                              }
                               }
                    };

            
            var json = JsonConvert.SerializeObject(doc, new JsonApiSerializerSettings());

this will produce:

{
  "data": {
    "type": "myclass",
    "relationships": {
      "myObject": {
        
      }
    }
  }
}

where i'm needing myObject to be an attribute, not a relationship. Is there something i can do to mitigate this? I assume it's because a JObject has a Type property....

Conditional Properties with JsonApiSerializer

Hi,

In my API I want to be able dynamically serialize properties on my model. For example the user only requests certain fields - http://myurl.com?fields[myobject]=fieldA,fieldB,fieldC

I want to be able to read the fields[myobject] property on my model and serialize only the properties that have been asked for by the user.

Hence in my controller I want to set the Conditional Properties on my model depending on fields user requests -
https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm

Unfortunately this doesn't seem to apply/work with JsonApiSerializer. As soon as the JsonApiSerializer is disabled ( and reverted to default Json.NET) Conditional Properties work.

Any suggestions how to get Conditional Properties working with JsonApiSerializer ?

Thanks

Check if "type" value matches type before deserializing

I have a sign-in endpoint that should accept two different resources: A User resource with username and password, and a ConfirmationCode resource containing an SMS confirmation code.

For example, it should accept both

{
  "data": {
    "type": "user",
    "attributes": {
      "username": "someUser",
      "password": "somePwd"
    }
  }
}

and

{
  "data": {
    "type": "confirmation-code",
    "id": "6081f88f-8a7d-44ef-902b-76fc66461808",
    "attributes": {
      "code": "1234"
    }
  }
}

I would expect that when I try to deserialize the first json above to a ConfirmationCode object, it should fail since the JSON API type does not match. What actually happens is that I simply get an empty User object.

Would it be possible to check the "type" parameter before deserializing, and failing in some way (e.g. throwing an exception) if the type does not match?

{"Expected to find json object at"}

Serialized an object with:

        var t = JsonConvert.SerializeObject(allMethod.Data, new JsonApiSerializerSettings());
        var tt = JsonConvert.DeserializeObject<Compound[]>(t, new JsonApiSerializerSettings());

where allMethod.Data is Compound[]. During deserialize I get the following Exception

JsonApiSerializer.Exceptions.JsonApiFormatException was unhandled
HResult=-2146233088
Message=Expected to find json object at
Path=""
Source=JsonApiSerializer
StackTrace:
at JsonApiSerializer.Util.ReaderUtil.d__1.MoveNext()
at JsonApiSerializer.Util.ReaderUtil.ReadAheadToIdentifyObject(ForkableJsonReader reader)
at JsonApiSerializer.JsonConverters.IncludedConverter.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)
at JsonApiSerializer.JsonConverters.DocumentRootConverter.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.Serialization.JsonSerializerProxy.DeserializeInternal(JsonReader reader, Type objectType)
at JsonApiSerializer.JsonConverters.DocumentRootConverter.TryResolveAsRootData(JsonReader reader, Type objectType, JsonSerializer serializer, Object& obj)
at JsonApiSerializer.JsonConverters.ResourceObjectListConverter.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
at TestBed.Program.Main(String[] args) in C:_\Thanos\TestBed\Program.cs:line 30
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:

It would be useful to get the property of the object that causes this exception instead of getting an unspecified "Expected to find json object at" message.
By The way when I use the JsonConvert.DeserializeObject<Compound[]>(t); with defaults it seems to work. Might this be an issue with the already mentioned null handling?

Meta

How to add Total Pages and Total Count in Meta?

Cannot Serialize

I tried the serialization example and it works correctly like documented.
But when I try to serialize one of my objects I don't understand why it is not serialized with the JSON:API structure.

Example

My class is:

public class VLSignUpRequestDTO
{
	[JsonProperty("email")]
	public string Email { get; set; }

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

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

	[JsonProperty("password")]
	public string Password { get; set; }

	[JsonProperty("password-confirmation")]
	[JsonIgnore]
	public string ConfirmPassword { get; set; }

	[JsonProperty("referral-code")]
	public string ReferralCode { get; set; }

	[JsonProperty("accept_terms")]
	public bool AreTermsAccepted { get; set; }

	[JsonProperty("shop")]
	public bool? Shop { get; set; }

	[JsonProperty("company-name")]
	public string CompanyName { get; set; }
}

Then I try to serialize it like this:

var signUp = new VLSignUpRequestDTO
{
	AreTermsAccepted = true,
	CompanyName = "My Company",
	Email = "[email protected]",
	FirstName = "John",
	LastName = "Doe",
	Password = "qwerty"
};

string json = JsonConvert.SerializeObject(signUp, new JsonApiSerializerSettings());

And the json result string is this:

[
  {
    "email": "[email protected]",
    "first-name": "John",
    "last-name": "Doe",
    "password": "qwerty",
    "accept_terms": true,
    "company-name": "My Company"
  }
]

How can I get it to format as

{
  "data": {
    "type": "VLSignUpRequestDTO",
    "attributes": {
      "email": "[email protected]",
      "first-name": "John",
      "last-name": "Doe",
      "password": "qwerty",
      "shop": false,
      "company-name": "My Company",
      "referral-code": ""
    }
  }
}

etc?

Possible to override default JSON property naming?

I would like all properties to be kebab-case by default, e.g. MyProperty becomes my-property. This is dead simple with the help of Humanizer's Underscore() and Hyphenate() string extensions by defining a simple contract resolver (shown in F# below):

type KebabCaseContractResolver() =
  inherit DefaultContractResolver()

  override __.ResolvePropertyName(propName: string) =
    propName.Underscore().Hyphenate()

However, JsonApiSerializer comes with its own contract resolver, so I can't use the one I created above. I have also tried subclassing your contract resolver and overriding ResolvePropertyName as above, but that had no effect and ResolvePropertyName wasn't even called.

I currently use JsonPropertyAttribute for all properties to explicitly specify the JSON property names manually, but that's far from ideal since the transformation is trivially automatable as shown above.

Is there a way I can enforce my own JSON property naming convention while using JsonApiSerializer?

Deserialization error: Expected to find json object at path ''

Hello,

I get an error when trying to deserialize lists of resources:

JsonApiSerializer.Exceptions.JsonApiFormatException: Expected to find json object at path ''
  at JsonApiSerializer.Util.ReaderUtil+<IterateProperties>d__1.MoveNext () [0x000ad] in <114082c9b9634d14a07a89c38a135e48>:0 
Full relevant stack trace
JsonApiSerializer.Exceptions.JsonApiFormatException: Expected to find json object at path ''
  at JsonApiSerializer.Util.ReaderUtil+<IterateProperties>d__1.MoveNext () [0x000ad] in <114082c9b9634d14a07a89c38a135e48>:0 
  at JsonApiSerializer.Util.ReaderUtil.ReadAheadToIdentifyObject (JsonApiSerializer.Util.ForkableJsonReader reader) [0x000cf] in <114082c9b9634d14a07a89c38a135e48>:0 
  at JsonApiSerializer.JsonConverters.ResourceObjectConverter+<>c__DisplayClass3_0.<ReadJson>b__0 (JsonApiSerializer.Util.ForkableJsonReader dataReader) [0x00033] in <114082c9b9634d14a07a89c38a135e48>:0 
  at JsonApiSerializer.Util.ReaderUtil.ReadInto[TReader,TResult] (TReader reader, System.Text.RegularExpressions.Regex pathRegex, System.Func`2[T,TResult] action) [0x0000d] in <114082c9b9634d14a07a89c38a135e48>:0 
  at JsonApiSerializer.JsonConverters.ResourceObjectConverter.ReadJson (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Object existingValue, Newtonsoft.Json.JsonSerializer serializer) [0x0004b] in <114082c9b9634d14a07a89c38a135e48>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable (Newtonsoft.Json.JsonConverter converter, Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Object existingValue) [0x00055] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Boolean checkAdditionalContent) [0x000db] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerProxy.DeserializeInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00008] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.JsonSerializer.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at JsonApiSerializer.Util.ReaderUtil.TryPopulateProperty (Newtonsoft.Json.JsonSerializer serializer, System.Object obj, Newtonsoft.Json.Serialization.JsonProperty property, Newtonsoft.Json.JsonReader value) [0x00012] in <114082c9b9634d14a07a89c38a135e48>:0 
  at JsonApiSerializer.JsonConverters.ResourceObjectConverter.PopulateProperties (Newtonsoft.Json.JsonSerializer serializer, System.Object obj, Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonObjectContract contract) [0x00027] in <114082c9b9634d14a07a89c38a135e48>:0 
  at JsonApiSerializer.JsonConverters.ResourceObjectConverter+<>c__DisplayClass3_0.<ReadJson>b__0 (JsonApiSerializer.Util.ForkableJsonReader dataReader) [0x000fb] in <114082c9b9634d14a07a89c38a135e48>:0 
  at JsonApiSerializer.Util.ReaderUtil.ReadInto[TReader,TResult] (TReader reader, System.Text.RegularExpressions.Regex pathRegex, System.Func`2[T,TResult] action) [0x0000d] in <114082c9b9634d14a07a89c38a135e48>:0 
  at JsonApiSerializer.JsonConverters.ResourceObjectConverter.ReadJson (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Object existingValue, Newtonsoft.Json.JsonSerializer serializer) [0x0004b] in <114082c9b9634d14a07a89c38a135e48>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable (Newtonsoft.Json.JsonConverter converter, Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Object existingValue) [0x00055] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Boolean checkAdditionalContent) [0x000db] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerProxy.DeserializeInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00008] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.JsonSerializer.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at JsonApiSerializer.JsonConverters.ResourceObjectListConverter+<>c__DisplayClass4_0.<ReadJson>b__0 (System.Object x) [0x00000] in <114082c9b9634d14a07a89c38a135e48>:0 
  at System.Linq.Enumerable+SelectEnumerableIterator`2[TSource,TResult].ToArray () [0x0001d] in <b5bd9d990a0b4733885e90ca5ec6c0fb>:0 
  at System.Linq.Enumerable.ToArray[TSource] (System.Collections.Generic.IEnumerable`1[T] source) [0x0001f] in <b5bd9d990a0b4733885e90ca5ec6c0fb>:0 
  at JsonApiSerializer.Util.ListUtil.CreateList (System.Type listType, System.Collections.Generic.IEnumerable`1[T] elements) [0x00032] in <114082c9b9634d14a07a89c38a135e48>:0 
  at JsonApiSerializer.JsonConverters.ResourceObjectListConverter.ReadJson (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Object existingValue, Newtonsoft.Json.JsonSerializer serializer) [0x00088] in <114082c9b9634d14a07a89c38a135e48>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable (Newtonsoft.Json.JsonConverter converter, Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Object existingValue) [0x00055] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Boolean checkAdditionalContent) [0x000db] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerProxy.DeserializeInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00008] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.JsonSerializer.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at JsonApiSerializer.JsonConverters.DocumentRootConverter.ReadJson (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Object existingValue, Newtonsoft.Json.JsonSerializer serializer) [0x000d9] in <114082c9b9634d14a07a89c38a135e48>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable (Newtonsoft.Json.JsonConverter converter, Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Object existingValue) [0x00055] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Boolean checkAdditionalContent) [0x000db] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerProxy.DeserializeInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00008] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.JsonSerializer.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at JsonApiSerializer.JsonConverters.DocumentRootConverter.TryResolveAsRootData (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.JsonSerializer serializer, System.Object& obj) [0x00060] in <114082c9b9634d14a07a89c38a135e48>:0 
  at JsonApiSerializer.JsonConverters.ResourceObjectListConverter.ReadJson (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Object existingValue, Newtonsoft.Json.JsonSerializer serializer) [0x00015] in <114082c9b9634d14a07a89c38a135e48>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable (Newtonsoft.Json.JsonConverter converter, Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Object existingValue) [0x00055] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Boolean checkAdditionalContent) [0x000db] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.JsonSerializer.DeserializeInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00053] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.JsonSerializer.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.JsonConvert.DeserializeObject (System.String value, System.Type type, Newtonsoft.Json.JsonSerializerSettings settings) [0x0002d] in <c19705166c7c4a608e182e859c4de6d2>:0 
  at Newtonsoft.Json.JsonConvert.DeserializeObject[T] (System.String value, Newtonsoft.Json.JsonSerializerSettings settings) [0x00000] in <c19705166c7c4a608e182e859c4de6d2>:0

The minimal amount of JSON to reproduce seems to be:

{
   "data": [
      {
         "type": "media",
         "id": "42170",
         "attributes": {
         }
      }
   ],
   "included": [
   ]
}

The type I try to deserialize from that is Media[] or List<Media> (tried both).

JsonSerializationException.SpecificationInformation is null.

The JSON posted above seems valid though (and I get no error if the array is empty). Moreover, I'm pretty sure this worked before on another endpoint, unfortunately it's broken today and I don't have an hand on the API I get JSON from, so I can't diff the payloads.

Thanks for your help (and thanks for the package, that saved me a lot of time, but I'm a bit stuck here 😄).

Cannot deserialize relationship without include

I have following model

 public class LocationDto      
 {      
     public string Id { get; set; }
     public string Type => "location"; 
     public string Name => { get; set; } 
     public LocationDto Region { get; set; }
 }

I am trying to deserialize it from following json:

 {
  "data": {
	  "id": "1",
	  "type": "location",
	  "attributes": {
		  "name": "test"
	  },
	  "relationships": {
		  "region": {
			  "data": {
				  "type": "location",
				  "id": "2"
			  }
		  }
	  }
  }

But I got error - {System.Object b__0(JsonApiSerializer.Util.ForkableJsonReader)} System.Reflection.MethodBase {System.Reflection.RuntimeMethodInfo} - "Object reference not set to an instance of an object."

Problem is in deserialization region relationship. I guess it expects to find region relation attributes in include and deserialize it, becouse when region.id is same as location.id exception won't occurred . According to http://jsonapi.org/format/#crud-updating-resource-relationships json that I use is valid. Maybe I missed something?

Erro deserialize

error while trying to deserialize the following json
"{"data":[{"type":"assets","id":7,"attributes":{"asset_allocation_id":6,"asset_subtype_id":14,"code":"a18j09","name":"a18j09","active":true,"custom_asset_subtype":false,"custom_asset_allocation":false,"investment_variation":"ACV","integer_min_investment":false}}]}"
Error : Exception has occurred: CLR/JsonApiSerializer.Exceptions.JsonApiFormatException
An exception of type 'JsonApiSerializer.Exceptions.JsonApiFormatException' occurred in Newtonsoft.Json.dll but was not handled in user code: 'Expected to find json object at path '''
Model:
using System;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
namespace App.ViewModel {
public class assets {
public int id { get; set; }
public int asset_allocation_id { get; set; }
public int asset_subtype_id { get; set; }
public string code { get; set; }
public string name { get; set; }
public bool active { get; set; }
public bool custom_asset_subtype { get; set; }
public bool custom_asset_allocation { get; set; }
public string isin { get; set; }
public string trading_name { get; set; }
public string description { get; set; }
public string investment_variation { get; set; }
public string custom_investment_variation { get; set; }
public string issuer_name { get; set; }
public byte? liquidity_days { get; set; }
public decimal? min_investment { get; set; }
public bool integer_min_investment { get; set; }
public string indexer_name { get; set; }
public asset_allocations asset_allocations { get; set; }
public asset_subtypes asset_subtypes { get; set; }
}
}

How to work with embedded objects?

From the JSON API specification on attributes:

Attributes may contain any valid JSON value.

Complex data structures involving JSON objects and arrays are allowed as attribute values. However, any object that constitutes or is contained in an attribute MUST NOT contain a relationships or links member, as those members are reserved by this specification for future use.

For example, I'd like to be able to (de)serialize the following JSON (especially the history bit):

{
  "data": {
    "type": "user",
    "id": 5,
    "attributes": {
      "name": "David",
      "history": [
        { "url": "http://google.com", "time": "2015-10-01T20:12:53Z" },
        { "url": "http://apple.com",  "time": "2014-10-01T20:12:53Z" },
        { "url": "http://yahoo.com",  "time": "2013-10-01T20:12:53Z" }
      ]
    }
  }
}

Some documentation from ember about embedded records.

Can this be done using this serializer already, or if not, what would be needed to achieve this?

Covariant lists do not serialize property

public interface ILineItem {}

public class LineItem : ILineItem
{
   public string Id {get; set;}
}

public class Order
{
   public string Id { get; set; }
   public IEnumerable<ILineItem> Lines {get; set;}
}

serializing

new Order{
   Id = "order",
   Lines = new LineItem[]{
      new LineItem {Id="123"}
   }
}

results in the following (notice the lines element should have a data element with an array, not be an array)

{
  "data": {
    "type": "order",
    "id": "order",
    "relationships": {
      "lines": [
        {
          "data": {
            "id": "123",
            "type": "lineitem"
          }
        }
      ]
    }
  }
}

Dynamically create Relationship Items

At the moment if I want a relationship I need to do this..

public Relationship<List> Authors { get; set; }

And this gives me an author inside of the Relationships of the resource object. However, I dont know Authors until run time. I want something more like....

public Relationship<IDictionary<string, List>> Relationships { get; set; }

Now I could have anything added and all would be good. Any ideas how I could do this?

JsonApiSerializer always returns null or default for attributes after DeserializeObject

I have used the JsonApiDotNetCore library to build an Api to return a valid JsonApi reponse. Here is an example response from our foos endpoint:

{
  "data": [
    {
      "attributes": {
        "emailAddress": "[email protected]",
        "firstName": "John"
      },
      "type": "foos",
      "id": "1"
    }
  ],
  "links": {
    "last": "http://localhost:60000/v1/foos?page[size]=10&page[number]=1"
  },
  "meta": {
    "total-records": 1
  }
}

To deserialize this I have a corresponding client side model in C#:

public class FooModel
{
     [JsonProperty(propertyName: "emailAddress")]
     public string EmailAddress { get; set; }

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

Here is how we try to deserialize the json:

var result = await _httpClient.SendAsync(request);
var responseBody = await result.Content.ReadAsStringAsync();

var myFoos = JsonConvert.DeserializeObject<DocumentRoot<FooModel[]>>(responseBody, new JsonApiSerializerSettings());

But in the Data object within the myFoos variable, both EmailAddress and FirstName are null. Some of the meta data has been populated.

Is there anything that I am doing wrong here?


Package versions:

Newtonsoft.Json: v12.0.1
JsonApiSerializer: v1.6.2

.Net Core - Set JsonApiSerializer as Default Setting

Hello Everyone!!

I am really appreciating this library. Great Job. Maybe this is not the right place to ask, but I really dont know.

I am trying to get a global configuration using .NET CORE 2 MVC API.

I can easily convert an object using this code. This is a controller and I am converting and respond using Ok(Json).

        public IActionResult Get([FromRoute]string company)
        {
            var parts = _partService.GetAll(company);

            string json = JsonConvert.SerializeObject(parts, new JsonApiSerializerSettings());

            return Ok(json);
        }

. It Works, but add some backslashes to the JSON due to the duplicate serialization, from the JsonConvert and then from the MVC.

"{\"data\":[{\"type\":\"partdefinitionapimodel\",\"id\":\"2026001632590498180\",\"attributes\":{\"name\":\"Tax

I Tried to look over the internet and could not find any place where I could change globally the JsonSerializerSettings with NET CORE.

I am thinking there is something related to services.AddMvc().AddJsonOption inside Startup class but I really dont know where to look or how to do it.

Thanks in advance for any help!!

Intercept "type" before deserializing

Hi,
First off thanks for creating this library it works great!.
Recently came across a requirement where a resource could be of any type and we need to deserialize into concrete type based on "type" property

For example we have sample json as

  "data": {
    "type": "products",
    "id": "123456",
    "attributes": {},
    "relationships": {
      "content": {
        "links": {
          "self": "/v1/products/123456/relationships/content",
          "related": "/v1/products/123456/content"
        },
        "data": {
          "type": "contentA",
          "id": "999999"
        }
      }
    }
  }
}

content.data.type could be "contentA", "contentB" and so on.

And have defined below classes to deserialize into

using System.IO;
using System.Reflection;
using JsonApiSerializer;
using JsonApiSerializer.JsonApi;
using Newtonsoft.Json;

namespace MyJsonApiSerializer
{
    interface IContent
    {
        string Type { get; }
        string Id { get; }
    }

    class ContentA : IContent
    {
        public string Type { get; } = "contentA";
        public string Id { get; set; }
    }

    class ContentB : IContent
    {
        public string Type { get; } = "contentB";
        public string Id { get; set; }
    }

    class Product
    {
        public string Type { get; } = "products";
        public string Id { get; set; }

        [JsonProperty("content")] public Relationship<IContent> Content { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var thisAssembly = Assembly.GetExecutingAssembly();
            using (var stream = thisAssembly.GetManifestResourceStream("MyJsonApiSerializer.product.json"))
            {
                using (var reader = new StreamReader(stream))
                {
                    var json = reader.ReadToEnd();
                    var product = JsonConvert.DeserializeObject<Product>(json, new JsonApiSerializerSettings());
                }
            }
        }
    }
}

What we need is to deserialize the json into Product class but Content propery should get deserialized into Relationship or Relationship as per "type". Don't think JsonApiSerializer is smart enough to infer the type and deserialize accordingly but any help on how to extend the current behavior to support this would be appreciated.

Links empty when deserialized

Input data for testing:

{
  "data": {
    "id": "2",
    "type": "user",
    "attributes": {
      "username": "Education Designer",
      "name": "Education Designer",
      "email": null,
      "phone_number": "+79261623199",
      "address": null,
      "birthday": null,
      "referral_code": "kc8afc",
      "last_sign_in_ip": null,
      "last_sign_in_at": null,
      "sign_in_count": 0
    },
    "relationships": {
      "user_entry": {
        "data": {
          "id": "2",
          "type": "user_entry"
        }
      },
      "referred_user": {
        "data": {
          "id": "1",
          "type": "referred_user"
        }
      }
    },
    "links": {
      "referral_code": "https://best.url/ref/kc8afc"
    }
  },
  "meta": {
    "user_coupons": {
      "active": {
        "count": 0
      }
    }
  },
  "included": [
    {
      "id": "1",
      "type": "referred_user",
      "attributes": {
        "username": "Marketing Agent",
        "referral_code": "dcwypm"
      }
    },
    {
      "id": "2",
      "type": "user_entry",
      "attributes": {
        "name": "BNS",
        "balance": 0
      }
    }
  ]
}

screen shot 2018-09-23 at 12 28 45


[JsonConverter] seems to be ignored as of 1.6.0

I have a class with a property marked with [JsonConverter] attribute. The class has been deserialized correctly using this converter. However, starting with JsonApiSerializer 1.6.0, the converter is no longer called, rendering deserialization incorrect.

Configure include options

Hi,

I'm currently searching for a possibility to specify which related resources should be serialized as included resources in an serialized result.

E.g.: We have an Article with an one-to-one relationship to a Person
When receiving the request /articles/1?include=author we want to return:

{
  "data":{
    "type": "article",
    "id": "1",
    "relationships": {
      "author": {
         "data": { 
           "type": "people", 
           "id": "99" }
      }
    }
  },
  "included": [{
    "type": "people",
    "id": "99",
    "attributes": {
      "first-name": "Patrick",
      "last-name": "Gebhardt",
    }
  }]
}

On the other hand when receiving /articles/1 our response should look like

{
  "data":{
    "type": "article",
    "id": "1",
    "relationships": {
      "author": {
         "data": { 
           "type": "people", 
           "id": "9" }
      }
    }
  }
}

From the code in ResourceObjectConverter listed below it looks like a resource is always included if there are properties w/ data (excluding several fields).

var willWriteObjectToIncluded = contract.Properties.Any(prop =>
{
//ignore id, type, meta and ignored properties
if (prop.PropertyName == PropertyNames.Id
|| prop.PropertyName == PropertyNames.Type
|| prop.PropertyName == PropertyNames.Meta
|| prop.Ignored)
return false;

Whats the best starting point for this problem?
Should I implement an custom ResourceObjectConverter which is able to deal with the specific include option handling?

Thanks in advance!

Required fields on related objects failing model validation

First of all, thank you for this nice library! 👍 So far, implementing the server-side for an ember-data application I'm working on, has been great using this library. However I've started using model validation in combination with relationships, and now I'm running into the following issue.

Say I have a model like this (tree structure):

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

    [Required]
    public string Name { get; set; }

    public Category Parent { get; set; }

    public IEnumerable<Category> Children { get; set; }
}

And I want to update an existing record:

[Route("{id}")]
public Category Patch(string id, [FromBody]Category category)
{
    category.Id = id;

    if (!ModelState.IsValid)
    {
        throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
    }

    // save in database & return
}

I want to be able to send the following JSON (generated by ember-data):

{
    "data": {
        "attributes": {
            "name": "Apple TV"
        },
        "id": "5bb0bae631ee771950a56f27",
        "relationships": {
            "parent": {
                "data": {
                    "id": "5bb0b50f31ee752168a5b2b0",
                    "type": "categories"
                }
            }
        },
        "type": "categories"
    }
}

However, this JSON will fail ModelValidation for the relationship "parent":

{
    "Message": "The request is invalid.",
    "ModelState": {
        "category.Parent.Name": [
            "The Name field is required."
        ]
    }
}

So my expectation is that while the "name" field is required, it shouldn't be required for referenced objects. For referenced objects, only "id" and "type" should suffice.

I've also seen this comment which might also work here. That would however require two models if I want to include full related objects for get queries and only light relationships for post/patch queries.

Convert type names in the same way as property names

Property names are, by default, converted from e.g. SomePropertyName to somePropertyName. However, type names are converted from SomeTypeName to sometypename. I know the type names can be overridden using a Type property, but I think the serializer by default should convert type names in the same way as property names. (In particular, since I use a custom NamingStrategy, see #41, I would expect the type name to be converted using the same stragety.)

Double serialisation

Correct me if I am wrong or I am using your library wrongly. When I try to serialise my DTO object it serialise two that make sense because in web api by default it will serialise json.net. but the thing is why I cant register JsonApiSerializerSettings as formatter in wep api config, then I believe this double serialisation will prevent.

any way this is awesome project. thanks for your sharing.

How to map a relationship to the id property

Imagine a simple relationship where a Foo has a Boo:

 "data": [
        {
            "id": "1",
            "type": "foos",
            "relationships": {
                  "bar" :  {
                        data: {
                          "type": "bars",
                           "id": "1"
                       }
               }
            }
       }

What I'd like is to be able to deserialize Foo's "bar" relationship to the id, not to the full object.
See the comment for the Bar property below:

class Foo
{
         public int Id { get; set; }
         public Bar Bar { get; set; }    // instead, I would like to have this: public int BarId { get; set; }
}

class Bar 
{
          public int Id { get; set; }
}

How can I do this?

Deserializing invalid json:api works w/o an exception

I encountered an unexpected behavior when deserializing an invalid json:api string(at least unexpected for me).

Below you can find a console application which illustrates the behavior.

  • I definied two models Message and Attachment. The Message contains one Attachment.
  • Within the Main method I definied an invalid json:api representation of the Message model (see invalidJson variable)
    • In this example the included tag contains invalid data since it's not a proper resource object for the related Attachment.

Expected behavior: An exception is thrown while deserializing the invalid json:api string.
Actual behavior: The invalid json:api string gets deserialized and even the Attachment attributes encrypted and size are properly set although they are not defined in a json:api compliant way within the included tag.

using System;
using System.ComponentModel.DataAnnotations;
using JsonApiSerializer;
using JsonApiSerializer.JsonApi;
using Newtonsoft.Json;

namespace DeserializeInvalidJsonApi
{
  class Program
  {
    public class Message
    {
      [Required]
      public string Id { get; set; }

      [Required]
      [RegularExpression("^message$")]
      public string Type { get; set; }

      [Required]
      public Attachment Attachment { get; set; }

    }

    public class Attachment
    {
      [Required]
      public string Id { get; set; }

      [Required]
      [RegularExpression("^attachment$")]
      public string Type { get; set; }

      [Required]
      public bool Encrypted { get; set; }

      [Required]
      public int Size { get; set; }
    }

    static void Main(string[] args)
    {
      string invalidJson = @"{
        ""data"":{  
          ""id"":""3e69af94-ed06-4544-bcdf-36117d351bfe"",
          ""type"":""message"",
          ""relationships"":{  
            ""attachment"":{  
                ""data"":{  
                  ""type"":""attachment"",
                  ""id"":""123123124""
                }
            }
          }
        },
        ""included"":[  
            {  
              ""type"":""attachment"",
              ""id"":""123123124"",
              ""encrypted"": false,
              ""size"": 12345
            }
        ]
      }";

      Console.WriteLine("Start deserialization");

      Message deserializedJsonApi = JsonConvert.DeserializeObject<Message>(invalidJson,
                                                                           new JsonApiSerializerSettings());

      Console.WriteLine(deserializedJsonApi.Attachment.Size.ToString());
      Console.WriteLine(deserializedJsonApi.Attachment.Encrypted.ToString());

      Console.WriteLine("Deserialization done");
    }
  }
}

How to support JSON API Error Objects?

JSON API specifies a format for so-called "error objects". While I could create such error documents myself, I would be interested to see what would be needed to support creating such documents based on ModelState validation.

An example from the JSON API site:

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/vnd.api+json

{
  "jsonapi": { "version": "1.0" },
  "errors": [
    {
      "code":   "123",
      "source": { "pointer": "/data/attributes/firstName" },
      "title":  "Value is too short",
      "detail": "First name must contain at least three characters."
    },
    {
      "code":   "225",
      "source": { "pointer": "/data/attributes/password" },
      "title": "Passwords must contain a letter, number, and punctuation character.",
      "detail": "The password provided is missing a punctuation character."
    },
    {
      "code":   "226",
      "source": { "pointer": "/data/attributes/password" },
      "title": "Password and password confirmation do not match."
    }
  ]
}

For reference, the default output of ModelState looks like this:

if (!ModelState.IsValid)
    throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
{
    "Message": "The request is invalid.",
    "ModelState": {
        "category.Name": [
            "The field Name must be a string or array type with a minimum length of '20'."
        ]
    }
}

Nested classes deserialization

I have an issue where i'm trying to serialise an object, that has a top level class with an Id property, and a list of nested classes which also have an Id property.

    public class MyClass1
    {
        public string Id { get; set; }
        public string MyTopProperty { get; set; }

        public IEnumerable<MyClass2> MyClass2 { get; set; }
    }

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

        public string MyProperty { get; set; }
    }

and if i do:

            var myClass = new MyClass1()
                          {
                              MyTopProperty = "hello",
                              MyClass2 = new List<MyClass2>()
                                         {
                                         }
                          };

            var doc = new DocumentRoot<MyClass1>()
                      {
                          Data = myClass
                      };

            var json = JsonConvert.SerializeObject(doc, new JsonApiSerializerSettings());

this will give me:

{
  "data": {
    "type": "myclass1",
    "attributes": {
      "myTopProperty": "hello"
    },
    "relationships": {
      "myClass2": {
        "data": [
          
        ]
      }
    }
  }
}

when really i'm expecting to see:

{
  "data": {
    "type": "myclass1",
    "attributes": {
      "myTopProperty": "hello"
    },
    "relationships": {
      "myClass2": [    
        ]
    }
  }
}

this becomes an issue when i receive some json and i try deserialising it back into MyClass1 and i get an exception that says Expected to find nested object within data.attributes.myClass2 that matches \data$\

Is there a way around this?

Question: JsonInputFormatter and JsonOutputFormatter

Hi!

First off thanks for creating this library it works great!

I got a question about the implementation; Correct me if I'm wrong but from the examples I understand that, in order to use the JsonApi serialization, I need to overwrite the existing dotnet JsonInput- and JsonOutput formatters.

If I'm correct (I tested it) this means that I loose the option to do content negotiation (https://docs.microsoft.com/en-us/aspnet/core/web-api/advanced/formatting?view=aspnetcore-2.0#content-negotiation).

Is this intentionally or could I do a PR to reintroduce content negotiation?

Multiple objects with same Id have the same reference

I would like to ask you if it is possible to create an option that objects with same Id don't have the same reference.

For instance in my code I have a situation like this (approximately)

[DataContract]
public class Earth
{
[DataMember(Name = "id")]
public int Id {get;set;}

[DataMember(Name = "countries")]
public Country[] Countries {get;set;}
}

[DataContract]
public class Country
{
[DataMember(Name = "id")]
public int Id {get;set;}

[DataMember(Name = "people")]
public Person[] People {get;set;}
}

[DataContract]
public class Person
{
[DataMember(Name = "Name")]
public string Name {get;set;}

// this doesn't exist in the api
public Country Country {get;set;}
}

In this example I have list of countries in Earth. And in those countries i have a list of people.

But people can live in multiple countries. As the deserializer cannot fill a country for each person (I would like a back link as well) I have to do something like this

foreach c in country
foreach p in c.people
p.country= c

to get a back link.

But the problem is that the deserializer actually creates the same person in multiple countries as the id is the same by reference. Then I cannot have them have a different country in the model (if i change country for one object I will change it for another as well).

my question is - can you create an option that objects which are same by id don't created the same by reference (but only by content)?

thanks in advance,
Ivan

Create Resource - Set Parent Id

The JSON API standard for resource with parent relationship is as follows:

{
  "data": {
    "type": "photos",
    "attributes": {
      "title": "Ember Hamster",
      "src": "http://example.com/images/productivity.png"
    },
    "relationships": {
      "photographer": {
        "data": { "type": "people", "id": "9" }
      }
    }
  }
}

(http://jsonapi.org/format/#crud-creating)

Let's assume we have the following model in our code:

public class Photo {
        public int Id { get; set; }
        public int PeopleId { get; set; }
        public string Title { get; set; }
        public string Src { get; set; }
        public People People { get; set; }
    }

PeopleId is a EF specific way of creating the relationship easier. Right now the api will crash because EF is not able to save the data since the only field set on the People relationship is the Id. A workaround in the controller is:

photo.peopleId = photo.People.Id;
photo.people = null;

Is there a way we could get this work done automatically?

Issue when serializing EF select result

Serializing the following:

new DocumentRoot<IEnumerable<Service>>
{
  Data = records
};

will produce wrong JSON output if records is an IEnumerable<Service> result from an Select(...) method call:

{
    "data": [
        {
            "type": "Service",
            "id": "ff3e06c0-7bdc-9846-bd33-42421f8b289f",
            "attributes": {
                "name": "Service 1",
                "date": "2017-01-01T00:00:00",
                "favorite": true
            }
        }
    ],
    "included": [
        {
            "type": "Service",
            "id": "ff3e06c0-7bdc-9846-bd33-42421f8b289f",
            "attributes": {
                "name": "Service 1",
                "date": "2017-01-01T00:00:00",
                "favorite": true
            }
        }
    ]
}

Note the unnecessary included property.
The serialization works well if I convert records to a List<Service>:

new DocumentRoot<IEnumerable<Service>>
{
  Data = records.ToList()
};

Included Resources only included in first request

With .Net Core and a default formatter set as shown in #35, I am getting the included resources only on the first request. Additional requests to the same endpoint do not have the included resources in the returned json. I am eagerly loading the related entities and they do indeed appear in the query response before serialization every time. Is this expected behavior? Do I have something misconfigured?

Thanks!

Use another naming strategy than CamelCaseNamingStrategy

The JsonApiSerializerSettings class sets its own ContractResolver, JsonApiContractResolver, so it's not possible to do this:

var jsonSettings = new JsonApiSerializerSettings(resourceConverter) {
    NullValueHandling = NullValueHandling.Ignore,
    ContractResolver = new DefaultContractResolver {
        NamingStrategy = new SnakeCaseNamingStrategy()
    }
};

The JsonApiContractResolver forces the use of CamelCaseNamingStrategy.

I can override property names with [JsonProperty("snake_name")] to workaround, but it would be cool to be able to pass down another naming strategy :)

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.