Skip to content

Commit 10f5db4

Browse files
authored
Merge pull request #4 from twcrews/dev
5.0.0
2 parents 62d937c + 597bbf3 commit 10f5db4

14 files changed

Lines changed: 127 additions & 89 deletions

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [5.0.0] - 2026-02-20
9+
10+
### Changed
11+
12+
- **Breaking change:** All model types have been changed from `class`es to `record`s.
13+
- **Breaking change:** All property accessors on model types have changed from `{ get; set; }` to `{ get; init; }`.
14+
15+
### Removed
16+
17+
- **Breaking change:** `JsonApiLinksObject` has been removed.
18+
19+
### Remarks
20+
21+
This version refactors the entire model into immutable `record` types. While opinionated, I'm convinced this change is in the best interest of creating robust and resilient code.
22+
23+
If you're not willing to part with mutable `class`es, this version is otherwise identical to version 4.0.0.
24+
825
## [4.0.0] - 2026-02-16
926

1027
### Changed
@@ -67,6 +84,7 @@ Additionally, this version aims to be more idiomatic by renaming class propertie
6784

6885
Initial release.
6986

87+
[5.0.0]: https://github.com/twcrews/jsonapi-client/compare/4.0.0...5.0.0
7088
[4.0.0]: https://github.com/twcrews/jsonapi-client/compare/3.0.0...4.0.0
7189
[3.0.0]: https://github.com/twcrews/jsonapi-client/compare/2.0.0...3.0.0
7290
[2.0.0]: https://github.com/twcrews/jsonapi-client/compare/1.0.0...2.0.0

Crews.Web.JsonApiClient.Tests/JsonApiDocumentTests.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public JsonApiDocumentTests()
1313
}
1414

1515
// Concrete implementation for testing abstract JsonApiDocument
16-
private class TestJsonApiDocument : JsonApiDocument { }
16+
private record TestJsonApiDocument : JsonApiDocument { }
1717

1818
#region HasCollectionResource Tests
1919

@@ -475,15 +475,15 @@ public void DeserializeCollectionGenericStaticMethodReturnsValidDocumentForValid
475475
Assert.True(doc.HasCollectionResource);
476476
}
477477

478-
public class MyModel
478+
public record MyModel
479479
{
480-
public string? Name { get; set; }
481-
public int Age { get; set; }
480+
public string? Name { get; init; }
481+
public int Age { get; init; }
482482
}
483483

484-
public class MyModelResource : JsonApiResource<MyModel> { }
484+
public record MyModelResource : JsonApiResource<MyModel> { }
485485

486-
public class MyModelDocument : JsonApiDocument<MyModelResource> { }
486+
public record MyModelDocument : JsonApiDocument<MyModelResource> { }
487487

488488
#endregion
489489
}

Crews.Web.JsonApiClient/Converters/JsonApiLinkConverter.cs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,32 @@ internal class JsonApiLinkConverter : JsonConverter<JsonApiLink>
2222
// Case 2: Link is an object with properties
2323
if (reader.TokenType == JsonTokenType.StartObject)
2424
{
25-
JsonApiLink link = new() { Href = new(string.Empty, UriKind.Relative) };
25+
Uri? href = null;
26+
string? rel = null;
27+
JsonApiLink? describedBy = null;
28+
string? title = null;
29+
string? type = null;
30+
string? hrefLang = null;
31+
JsonObject? meta = null;
2632

2733
while (reader.Read())
2834
{
2935
if (reader.TokenType == JsonTokenType.EndObject)
30-
return link;
36+
{
37+
if (href is null)
38+
throw new JsonException("Href is required for link objects.");
39+
40+
return new JsonApiLink
41+
{
42+
Href = href,
43+
Rel = rel,
44+
DescribedBy = describedBy,
45+
Title = title,
46+
Type = type,
47+
HrefLang = hrefLang,
48+
Meta = meta
49+
};
50+
}
3151

3252
if (reader.TokenType == JsonTokenType.PropertyName)
3353
{
@@ -37,26 +57,26 @@ internal class JsonApiLinkConverter : JsonConverter<JsonApiLink>
3757
switch (propertyName)
3858
{
3959
case "href":
40-
string href = reader.GetString() ?? throw new JsonException("Href cannot be null.");
41-
link.Href = new(href);
60+
string hrefString = reader.GetString() ?? throw new JsonException("Href cannot be null.");
61+
href = new(hrefString);
4262
break;
4363
case "rel":
44-
link.Rel = reader.GetString();
64+
rel = reader.GetString();
4565
break;
4666
case "describedby":
47-
link.DescribedBy = JsonSerializer.Deserialize<JsonApiLink>(ref reader, options);
67+
describedBy = JsonSerializer.Deserialize<JsonApiLink>(ref reader, options);
4868
break;
4969
case "title":
50-
link.Title = reader.GetString();
70+
title = reader.GetString();
5171
break;
5272
case "type":
53-
link.Type = reader.GetString();
73+
type = reader.GetString();
5474
break;
5575
case "hreflang":
56-
link.HrefLang = reader.GetString();
76+
hrefLang = reader.GetString();
5777
break;
5878
case "meta":
59-
link.Meta = JsonSerializer.Deserialize<JsonObject>(ref reader, options);
79+
meta = JsonSerializer.Deserialize<JsonObject>(ref reader, options);
6080
break;
6181
default:
6282
// Skip unknown properties

Crews.Web.JsonApiClient/Crews.Web.JsonApiClient.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
<PropertyGroup>
1111
<PackageId>Crews.Web.JsonApiClient</PackageId>
12-
<PackageVersion>4.0.0</PackageVersion>
12+
<PackageVersion>5.0.0</PackageVersion>
1313
<Authors>Tommy Crews</Authors>
1414
<Description>
1515
A library containing serialization models and methods for the JSON:API specification.

Crews.Web.JsonApiClient/JsonApiDocument.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,51 +7,51 @@ namespace Crews.Web.JsonApiClient;
77
/// <summary>
88
/// Represents a base class for JSON:API top-level objects as defined in section 7.1 of the JSON:API specification.
99
/// </summary>
10-
public class JsonApiDocument
10+
public record JsonApiDocument
1111
{
1212
/// <summary>
1313
/// Gets or sets the <c>jsonapi</c> property of the document.
1414
/// </summary>
1515
[JsonPropertyName("jsonapi")]
16-
public JsonApiInfo? JsonApi { get; set; }
16+
public JsonApiInfo? JsonApi { get; init; }
1717

1818
/// <summary>
1919
/// Gets or sets the primary data payload associated with the document.
2020
/// </summary>
2121
[JsonPropertyName("data")]
22-
public JsonElement? Data { get; set; }
22+
public JsonElement? Data { get; init; }
2323

2424
/// <summary>
2525
/// Gets or sets the collection of errors associated with the document.
2626
/// </summary>
2727
[JsonPropertyName("errors")]
28-
public IEnumerable<JsonApiError>? Errors { get; set; }
28+
public IEnumerable<JsonApiError>? Errors { get; init; }
2929

3030
/// <summary>
3131
/// Gets or sets the <c>links</c> property of the document.
3232
/// </summary>
3333
/// <seealso href="https://jsonapi.org/format/#document-links"/>
3434
[JsonPropertyName("links")]
35-
public Dictionary<string, JsonApiLink>? Links { get; set; }
35+
public Dictionary<string, JsonApiLink>? Links { get; init; }
3636

3737
/// <summary>
3838
/// Gets or sets the <c>included</c> property of the document.
3939
/// </summary>
4040
[JsonPropertyName("included")]
41-
public IEnumerable<JsonApiResource>? Included { get; set; }
41+
public IEnumerable<JsonApiResource>? Included { get; init; }
4242

4343
/// <summary>
4444
/// Gets or sets the <c>meta</c> property of the document.
4545
/// </summary>
4646
/// <seealso href="https://jsonapi.org/format/#document-meta"/>
4747
[JsonPropertyName("meta")]
48-
public JsonObject? Meta { get; set; }
48+
public JsonObject? Meta { get; init; }
4949

5050
/// <summary>
5151
/// Gets or sets members defined by any applied JSON:API extensions.
5252
/// </summary>
5353
[JsonExtensionData]
54-
public Dictionary<string, JsonElement>? Extensions { get; set; }
54+
public Dictionary<string, JsonElement>? Extensions { get; init; }
5555

5656
/// <summary>
5757
/// Gets a value indicating whether the <see cref="Data"/> property contains a resource collection object.
@@ -87,13 +87,13 @@ public class JsonApiDocument
8787
/// JSON:API specification.
8888
/// </summary>
8989
/// <typeparam name="T">The underlying resource type.</typeparam>
90-
public class JsonApiDocument<T> : JsonApiDocument where T : JsonApiResource
90+
public record JsonApiDocument<T> : JsonApiDocument where T : JsonApiResource
9191
{
9292
/// <summary>
9393
/// Gets or sets the primary data payload associated with the document.
9494
/// </summary>
9595
[JsonPropertyName("data")]
96-
public new T? Data { get; set; }
96+
public new T? Data { get; init; }
9797

9898
/// <summary>
9999
/// Gets a value indicating whether the <see cref="Data"/> property contains a single resource object.
@@ -120,13 +120,13 @@ public class JsonApiDocument<T> : JsonApiDocument where T : JsonApiResource
120120
/// JSON:API specification.
121121
/// </summary>
122122
/// <typeparam name="T">The underlying resource type.</typeparam>
123-
public class JsonApiCollectionDocument<T> : JsonApiDocument where T : JsonApiResource
123+
public record JsonApiCollectionDocument<T> : JsonApiDocument where T : JsonApiResource
124124
{
125125
/// <summary>
126126
/// Gets or sets the primary data payload associated with the document.
127127
/// </summary>
128128
[JsonPropertyName("data")]
129-
public new IEnumerable<T>? Data { get; set; }
129+
public new IEnumerable<T>? Data { get; init; }
130130

131131
/// <summary>
132132
/// Gets a value indicating whether the <see cref="Data"/> property contains a resource collection object.

Crews.Web.JsonApiClient/JsonApiError.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,53 +6,53 @@ namespace Crews.Web.JsonApiClient;
66
/// <summary>
77
/// Represents an error in a document as defined in section 11.2 of the JSON:API specification.
88
/// </summary>
9-
public class JsonApiError
9+
public record JsonApiError
1010
{
1111
/// <summary>
1212
/// Gets or sets the unique identifier for this particular occurrence of the problem.
1313
/// </summary>
1414
[JsonPropertyName("id")]
15-
public string? Id { get; set; }
15+
public string? Id { get; init; }
1616

1717
/// <summary>
1818
/// Gets or sets the links that provide additional information about the error.
1919
/// </summary>
2020
[JsonPropertyName("links")]
21-
public JsonApiErrorLinksObject? Links { get; set; }
21+
public JsonApiErrorLinksObject? Links { get; init; }
2222

2323
/// <summary>
2424
/// Gets or sets the HTTP status code associated with the error.
2525
/// </summary>
2626
[JsonPropertyName("status")]
27-
public string? Status { get; set; }
27+
public string? Status { get; init; }
2828

2929
/// <summary>
3030
/// Gets or sets the application-specific error code associated with the error.
3131
/// </summary>
3232
[JsonPropertyName("code")]
33-
public string? Code { get; set; }
33+
public string? Code { get; init; }
3434

3535
/// <summary>
3636
/// Gets or sets the title of the error.
3737
/// </summary>
3838
[JsonPropertyName("title")]
39-
public string? Title { get; set; }
39+
public string? Title { get; init; }
4040

4141
/// <summary>
4242
/// Gets or sets the detailed description of the error.
4343
/// </summary>
4444
[JsonPropertyName("detail")]
45-
public string? Detail { get; set; }
45+
public string? Detail { get; init; }
4646

4747
/// <summary>
4848
/// Gets or sets the source of the error.
4949
/// </summary>
5050
[JsonPropertyName("source")]
51-
public JsonApiErrorSource? Source { get; set; }
51+
public JsonApiErrorSource? Source { get; init; }
5252

5353
/// <summary>
5454
/// Gets or sets the additional metadata associated with the object.
5555
/// </summary>
5656
[JsonPropertyName("meta")]
57-
public JsonObject? Meta { get; set; }
57+
public JsonObject? Meta { get; init; }
5858
}

Crews.Web.JsonApiClient/JsonApiErrorLinksObject.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,25 @@ namespace Crews.Web.JsonApiClient;
77
/// <summary>
88
/// Represents a set of links that provide additional information about an error in a JSON:API document.
99
/// </summary>
10-
public class JsonApiErrorLinksObject
10+
public record JsonApiErrorLinksObject
1111
{
1212
/// <summary>
1313
/// Gets or sets a link that provides additional information about the error.
1414
/// </summary>
1515
[JsonPropertyName("about")]
1616
[JsonConverter(typeof(JsonApiLinkConverter))]
17-
public JsonApiLink? About { get; set; }
17+
public JsonApiLink? About { get; init; }
1818

1919
/// <summary>
2020
/// Gets or sets the link that specifies the type of the error.
2121
/// </summary>
2222
[JsonPropertyName("type")]
2323
[JsonConverter(typeof(JsonApiLinkConverter))]
24-
public JsonApiLink? Type { get; set; }
24+
public JsonApiLink? Type { get; init; }
2525

2626
/// <summary>
2727
/// Gets or sets a collection of additional JSON properties that are not mapped to known members.
2828
/// </summary>
2929
[JsonExtensionData]
30-
public Dictionary<string, JsonElement>? Extensions { get; set; }
30+
public Dictionary<string, JsonElement>? Extensions { get; init; }
3131
}

Crews.Web.JsonApiClient/JsonApiErrorSource.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,23 @@ namespace Crews.Web.JsonApiClient;
66
/// Represents the source of an error in a request, such as a specific field, parameter, or header that caused the
77
/// error.
88
/// </summary>
9-
public class JsonApiErrorSource
9+
public record JsonApiErrorSource
1010
{
1111
/// <summary>
1212
/// Gets or sets the JSON Pointer that identifies the location within a JSON document that caused the error.
1313
/// </summary>
1414
[JsonPropertyName("pointer")]
15-
public string? Pointer { get; set; }
15+
public string? Pointer { get; init; }
1616

1717
/// <summary>
1818
/// Gets or sets the URI query parameter value that caused the error.
1919
/// </summary>
2020
[JsonPropertyName("parameter")]
21-
public string? Parameter { get; set; }
21+
public string? Parameter { get; init; }
2222

2323
/// <summary>
2424
/// Gets or sets the name of a request header that caused the error.
2525
/// </summary>
2626
[JsonPropertyName("header")]
27-
public string? Header { get; set; }
27+
public string? Header { get; init; }
2828
}

Crews.Web.JsonApiClient/JsonApiInfo.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,29 @@ namespace Crews.Web.JsonApiClient;
66
/// <summary>
77
/// Represents information about the JSON:API implementation as defined in section 7.7 of the JSON:API specification.
88
/// </summary>
9-
public class JsonApiInfo
9+
public record JsonApiInfo
1010
{
1111
/// <summary>
1212
/// Gets or sets the JSON:API version for the document.
1313
/// </summary>
1414
[JsonPropertyName("version")]
15-
public string? Version { get; set; }
15+
public string? Version { get; init; }
1616

1717
/// <summary>
1818
/// Gets or sets the collection of extension URIs associated with the document.
1919
/// </summary>
2020
[JsonPropertyName("ext")]
21-
public IEnumerable<Uri>? Ext { get; set; }
21+
public IEnumerable<Uri>? Ext { get; init; }
2222

2323
/// <summary>
2424
/// Gets or sets the collection of profile URIs associated with the document.
2525
/// </summary>
2626
[JsonPropertyName("profile")]
27-
public IEnumerable<Uri>? Profile { get; set; }
27+
public IEnumerable<Uri>? Profile { get; init; }
2828

2929
/// <summary>
3030
/// Gets or sets the collection of additional metadata associated with the object.
3131
/// </summary>
3232
[JsonPropertyName("meta")]
33-
public JsonObject? Meta { get; set; }
33+
public JsonObject? Meta { get; init; }
3434
}

0 commit comments

Comments
 (0)