Skip to content

Commit

Permalink
Merge pull request #78 from gregsdennis/json-more-docs
Browse files Browse the repository at this point in the history
Json more docs
  • Loading branch information
gregsdennis committed Mar 2, 2021
2 parents 5b163fa + 5fb6b11 commit 75462fb
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 103 deletions.
17 changes: 12 additions & 5 deletions Json.More/EnumStringConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,22 @@ namespace Json.More
/// of composite values is not supported.
/// </remarks>
/// <example>
/// public class MyClass {
/// [JsonConverter(typeof(EnumStringConverter&lt;MyEnum&gt;))]
/// public MyEnum Value { get; set; }
/// }
///
/// The attribute can be applied to both the enum type itself:
/// ```c#
/// public enum MyEnum {
/// Foo,
/// Bar
/// }
/// ```
///
/// or to a property of the enum type:
///
/// ```c#
/// public class MyClass {
/// [JsonConverter(typeof(EnumStringConverter&lt;MyEnum&gt;))]
/// public MyEnum Value { get; set; }
/// }
/// ```
/// </example>
public class EnumStringConverter<T> : JsonConverter<T>
where T : Enum
Expand Down
4 changes: 2 additions & 2 deletions Json.More/Json.More.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>Json.More.Net</PackageId>
<Authors>Greg Dennis</Authors>
<Version>1.4.0</Version>
<Version>1.4.1</Version>
<Description>Provides extended functionality for the System.Text.Json namespace.</Description>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageProjectUrl>https://github.com/gregsdennis/json-everything</PackageProjectUrl>
Expand All @@ -14,7 +14,7 @@
<PackageReleaseNotes>https://gregsdennis.github.io/json-everything/release-notes/json-more.html</PackageReleaseNotes>
<LangVersion>latest</LangVersion>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.4.0.0</FileVersion>
<FileVersion>1.4.1.0</FileVersion>
<DocumentationFile>Json.More.xml</DocumentationFile>
<PackageIcon>json-logo-256.png</PackageIcon>
<IncludeSymbols>true</IncludeSymbols>
Expand Down
2 changes: 2 additions & 0 deletions Json.More/Json.More.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Json.More/JsonElementEqualityComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public bool Equals(JsonElement x, JsonElement y)
/// <exception cref="T:System.ArgumentNullException">The type of <paramref name="obj">obj</paramref> is a reference type and <paramref name="obj">obj</paramref> is null.</exception>
public int GetHashCode(JsonElement obj)
{
return obj.GetHashCode();
return obj.GetEquivalenceHashCode();
}
}
}
2 changes: 1 addition & 1 deletion JsonLogic/RuleRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static class RuleRegistry
/// Gets a <see cref="Rule"/> implementation for a given identifier string.
/// </summary>
/// <param name="identifier">The identifier.</param>
/// <returns>The <see cref="Type"/> of the rule.</returns>
/// <returns>The <see cref="System.Type"/> of the rule.</returns>
public static Type? GetRule(string identifier)
{
return _rules.TryGetValue(identifier, out var t) ? t : null;
Expand Down
62 changes: 1 addition & 61 deletions JsonSchema.Generation/JsonSchema.Net.Generation.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs_source/examples/more-enums.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Serializing Enumerations by Their Name
# Serializing Enumerations by Name

By default, the `System.Text.Json` serializer will convert enumeration members to their numeric values. But suppose we're interacting with an API that expects named values. To do this, we need to tell the serializer how to convert the enum values into strings. This is the purpose of the `EnumStringConverter<T>` class.

Expand Down
60 changes: 60 additions & 0 deletions docs_source/examples/more-extensions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# `JsonElement` (and `JsonDocument`) Extensions

`System.Text.Json` is great for serialization needs, but the Document Object Model (DOM) needs a bit of work. To this end, `Json.More` supplies several extensions that make working with the `JsonElement` struct easier.

***NOTE** Many of these extensions exist for `JsonDocument` as well by proxying to the `.RootElement` property.*

## JSON-ifying

One might expect `JsonElement.ToString()` to return valid JSON. I sure did. It doesn't.

So we created the `.ToJsonString()` extension method. This method basically just serializes the element, but

```c#
element.ToJsonString();
```

is a lot more readable than

```c#
JsonSerializer.Serialize(element);
```

## Equality

Equality isn't implemented for `JsonElement` _at all_! So anytime you do

```c#
element1 == element2
```

or even

```c#
Equals(element1, element2)
```

you just get the base `object` implementation, which just compares references. That's not really useful.

To get JSON-equivalence, you need to use `.IsEquivalentTo()`. This will compare primitive values (`bool`, `number`, `string`, and `null`) directly and as expected. For objects and arrays, it follows the JSON specification for equality:

- **objects** - unordered key-matching with value comparison
- **arrays** - sequential value comparison

There's even a `.GetEquivalenceHashCode()` method that follows the same rules!

*Credit for `.GetEquivalenceHashCode()` goes to StackOverflow user [dbc](https://stackoverflow.com/users/3744182/dbc) for their [wonderful answer](https://stackoverflow.com/a/60592310/878701).*

These extension methods have also been integrated into `JsonElementEqualityComparer` which implements `IEqualityComparer<JsonElement>` and can be used in, e.g., Linq queries.

## Conversions

A staple of this library suite's predecessor was the ability to easily build JSON in C# code. Sadly, type conversion is a feature that was left out for `JsonElement`.

Now you have `.ToJsonElement()`. This will convert the appropriate types into corresponding `JsonElement` representations.

- `bool`
- number types (`int`, `double`, `decimal`, etc.)
- `string` (also supports `null`!)
- `IEnumerable<JsonElement>` for arrays
- `IDictionary<string, JsonElement>` for objects
8 changes: 5 additions & 3 deletions docs_source/examples/toc.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
- name: Json.More.Net
items:
- name: Enum serialization by name
- name: Enum Serialization by Name
href: more-enums.md
- name: Extensions on `JsonElement`
href: more-extensions.md
- name: JsonSchema.Net
items:
- name: Extending schema validation
- name: Extending Schema Validation
href: schema-vocabs.md
- name: JsonSchema.Net.Generation
items:
- name: Extending schema generation
- name: Extending Schema Generation
href: schema-gen.md
6 changes: 5 additions & 1 deletion docs_source/release-notes/json-more.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# [1.4.0](https://github.com/gregsdennis/json-everything/pull/???)
# [1.4.1](https://github.com/gregsdennis/json-everything/pull/78)

`JsonElementEqualityComparer` now uses `.GetEquivalenceHashCode()`.

# [1.4.0](https://github.com/gregsdennis/json-everything/pull/75)

Added support for nullable reference types.

Expand Down
124 changes: 96 additions & 28 deletions docs_source/usage/schema-building.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,35 +20,103 @@ Done.

## Inline

To build a schema inline, you will utilize the `JsonSchemaBuiler` class and its fluent-style extension methods.

```json
{
"properties":{
"myProperty":{
"type":"string",
"minLength":10
}
},
"required":["myProperty"]
}
```

```c#
// JsonSchemaBuilder has an implicit cast for convenience that calls
// the .Build() method. To use this you need to specify the type in
// the declaration.
JsonSchema schema = new JsonSchemaBuilder()
.Properties(
(
"myProperty", new JsonSchemaBuilder()
.Type(SchemaValueType.String),
.MinLength(10)
There are many reasons why you would want to hard-code your schemas. This library actually hard-codes all of the meta-schemas. Whatever your reason, the `JsonSchemaBuilder` class is going to be your friend.

The builder class itself is pretty simple. It just has an `.Add()` method which takes an instance of `IJsonSchemaKeyword`. The real power comes from the multitudes of extension methods. There's at least one for every keyword, and they all take the appropriate types for the data that the keyword expects.

Once you've added all of your properties, just call the `.Build()` method to get your schema object.

```c#
var builder = new JsonSchemaBuilder()
// builder methods
;
var schema = builder.Build();
```

Let's take a look at some of the builder extension methods.

### Easy Mode

Some of the more straightforward builder methods are for like the `title` and `$comment` keywords, which just take a string:

```c#
builder.Comment("a comment")
.Title("A title for my schema");
```

Notice that these methods implement a fluent interface so that you can chain them together.

### A Little Spice

Other extension methods can take multiple values. These have been overloaded to accept both `IEnumerable<T>` and `params` arrays just to keep things flexible.

```c#
var required = new List<string>{"prop1", "prop2"};
builder.Required(required);
```

or just

```c#
builder.Required("prop1", "prop2");
```

### Now You're Cooking With Fire

Lastly, we have the extension methods which take advantage of C# 7 tuples. These include keywords like `$defs` and `properties` which take objects in their JSON form.

```c#
builder.Properties(
("prop1", new JsonSchemaBuilder()
.Type(SchemaValueType.String)
.MinLength(8)
),
("prop2", new JsonSchemaBuilder()
.Type(SchemaValueType.Number)
.MultipleOf(42)
)
)
.Required("myProperty");
);
```

Did you notice how the `JsonSchemaBuilder` is just included directly without the `.Build()` method? These methods actually require `JsonSchema` objects. This leads us into the next part.

### Conversions

`JsonSchemaBuilder` defines an implicit cast to `JsonSchema` which calls the `.Build()` method.

To help things further, `JsonSchema` also defines implicit conversions from `bool`. This allows you to simply use `true` and `false` to create their respective schemas.

```c#
builder.Properties(
("prop1", new JsonSchemaBuilder()
.Type(SchemaValueType.String)
.MinLength(8)
),
("prop2", new JsonSchemaBuilder()
.Type(SchemaValueType.Number)
.MultipleOf(42)
),
("prop3", true)
);
```

The fluent-style interface is designed to be able to express the schema in C# in such a way that it mimics the JSON representation.
This cast can be used anywhere a `JsonSchema` is needed, such as in the `additionalProperties` or `items` keywords.

***NOTE** The meta-schemas exposed by the library are built using the fluent syntax.*
```c#
builder.Properties(
("prop1", new JsonSchemaBuilder()
.Type(SchemaValueType.String)
.MinLength(8)
),
("prop2", new JsonSchemaBuilder()
.Type(SchemaValueType.Number)
.MultipleOf(42)
),
("prop3", true),
("prop4", new JsonSchemaBuilder()
.Type(SchemaValueType.Array)
.Items(true)
)
)
.AdditionalProperties(false);
```

0 comments on commit 75462fb

Please sign in to comment.