Skip to content

Commit 42f173c

Browse files
authored
Update Newtonsoft.Json page (#25)
- Update Newtonsoft.Json page
1 parent ee2ecc2 commit 42f173c

1 file changed

Lines changed: 175 additions & 4 deletions

File tree

Lines changed: 175 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,178 @@
11
# Newtonsoft.Json
22

3-
!!! info "Work in Progress"
3+
FunQL uses [System.Text.Json](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/overview)
4+
by default for JSON serialization. However, if your project requires [Newtonsoft.Json](https://www.newtonsoft.com/json)
5+
(JSON.NET), FunQL can seamlessly integrate it for both serialization and deserialization.
46

5-
We are actively working on the documentation for FunQL .NET.
6-
Please check back later or visit [our repository](https://github.com/funql/funql-dotnet) to track progress or
7-
contribute.
7+
This section explains how to integrate Newtonsoft.Json with FunQL for both parsing (deserializing) and printing
8+
(serializing) constants.
9+
10+
## Configuring deserialization
11+
12+
To parse FunQL constants (e.g., `true`, `"name"`, `123`, or objects/arrays) using Newtonsoft.Json, we will need to
13+
override the default `IConstantParser`. First, we create a parser that uses Newtonsoft.Json, and then we configure our
14+
schema to use it.
15+
16+
### 1. Create the parser
17+
18+
Implement a custom `IConstantParser` that uses `JsonConvert.DeserializeObject()` to deserialize JSON strings into FunQL
19+
constants.
20+
21+
```csharp
22+
/// <summary>
23+
/// Implementation of <see cref="IConstantParser"/> that uses <see cref="JsonConvert"/> to parse the
24+
/// <see cref="Constant"/> node.
25+
/// </summary>
26+
/// <param name="jsonSerializerSettings">Settings for <see cref="Newtonsoft.Json"/>.</param>
27+
/// <inheritdoc/>
28+
public class NewtonsoftJsonConstantParser(
29+
JsonSerializerSettings jsonSerializerSettings
30+
) : IConstantParser
31+
{
32+
/// <summary>Options for <see cref="Newtonsoft.Json"/>.</summary>
33+
private readonly JsonSerializerSettings _jsonSerializerSettings = jsonSerializerSettings;
34+
35+
/// <inheritdoc/>
36+
public Constant ParseConstant(IParserState state)
37+
{
38+
state.IncreaseDepth();
39+
40+
var expectedType = state.RequireContext<ConstantParseContext>().ExpectedType;
41+
// If Type is a primitive/struct (int, double, DateTime, etc.), we should make it Nullable as 'null' is a valid
42+
// constant, but JsonConvert can only read 'null' if Type can be null
43+
expectedType = expectedType.ToNullableType();
44+
45+
var token = state.CurrentToken();
46+
switch (token.Type)
47+
{
48+
case TokenType.String:
49+
case TokenType.Number:
50+
case TokenType.Boolean:
51+
case TokenType.Null:
52+
case TokenType.Object:
53+
case TokenType.Array:
54+
// Valid token for constants
55+
break;
56+
case TokenType.OpenBracket:
57+
// Handle OpenBracket token as Array
58+
token = state.CurrentTokenAsArray();
59+
break;
60+
case TokenType.None:
61+
case TokenType.Eof:
62+
case TokenType.Identifier:
63+
case TokenType.OpenParen:
64+
case TokenType.CloseParen:
65+
case TokenType.Comma:
66+
case TokenType.Dot:
67+
case TokenType.Dollar:
68+
case TokenType.CloseBracket:
69+
default:
70+
// Invalid token for constants
71+
throw state.Lexer.SyntaxException($"Expected constant at position {token.Position}, but found '{token.Text}'.");
72+
}
73+
74+
var metadata = state.CreateMetadata();
75+
76+
try
77+
{
78+
var value = JsonConvert.DeserializeObject(token.Text, expectedType, _jsonSerializerSettings);
79+
// Successfully parsed, so go to next token
80+
state.NextToken();
81+
82+
state.DecreaseDepth();
83+
return new Constant(value, metadata);
84+
}
85+
catch (Exception e) when (e is JsonException)
86+
{
87+
throw new ParseException($"Failed to parse constant '{token.Text}' at position {token.Position}.", e);
88+
}
89+
}
90+
}
91+
```
92+
93+
!!! note
94+
95+
This code is based on the [default implementation](
96+
https://github.com/funql/funql-dotnet/blob/main/src/FunQL.Core/Constants/Parsers/JsonConstantParser.cs), adapted to
97+
use Newtonsoft.Json instead of System.Text.Json.
98+
99+
### 2. Configure Schema
100+
101+
To use the `NewtonsoftJsonConstantParser`, override the default `IConstantParser` in your schema's `OnInitializeSchema`
102+
method.
103+
104+
```csharp
105+
public sealed class ApiSchema : Schema {
106+
protected override void OnInitializeSchema(ISchemaConfigBuilder schema) {
107+
schema.AddParseFeature(it =>
108+
{
109+
IConstantParser? constantParser = null;
110+
it.MutableConfig.ConstantParserProvider = _ => constantParser ??= new NewtonsoftJsonConstantParser(
111+
new JsonSerializerSettings()
112+
);
113+
});
114+
}
115+
}
116+
```
117+
118+
Now, when you parse a FunQL query, the `NewtonsoftJsonConstantParser` will be used to parse the constants. You can also
119+
configure the `JsonSerializerSettings` as needed, adding custom converters, naming strategies, etc.
120+
121+
## Configuring serialization
122+
123+
When printing FunQL queries to a string, the `Constant` nodes are serialized to JSON using the `IConstantPrintVisitor`.
124+
First, we implement this class to use Newtonsoft.Json, and then we configure the schema to use this implementation.
125+
126+
### 1. Create the print visitor
127+
128+
The `NewtonsoftJsonConstantPrintVisitor` serializes FunQL constants into JSON strings using
129+
`JsonConvert.SerializeObject()`.
130+
131+
````csharp
132+
/// <summary>Implementation of <see cref="IConstantPrintVisitor{TState}"/> using <see cref="JsonConvert"/>.</summary>
133+
/// <param name="jsonSerializerSettings">Settings for <see cref="Newtonsoft.Json"/>.</param>
134+
/// <inheritdoc cref="IConstantPrintVisitor{TState}"/>
135+
public class NewtonsoftJsonConstantPrintVisitor<TState>(
136+
JsonSerializerSettings jsonSerializerSettings
137+
) : ConstantVisitor<TState>, IConstantPrintVisitor<TState> where TState : IPrintVisitorState
138+
{
139+
/// <summary>Settings to use when writing JSON.</summary>
140+
private readonly JsonSerializerSettings _jsonSerializerSettings = jsonSerializerSettings;
141+
142+
/// <inheritdoc/>
143+
public override Task Visit(Constant node, TState state, CancellationToken cancellationToken) =>
144+
state.OnVisit(node, async ct =>
145+
{
146+
var jsonValue = JsonConvert.SerializeObject(node.Value, _jsonSerializerSettings);
147+
await state.Write(jsonValue, ct);
148+
}, cancellationToken);
149+
}
150+
````
151+
152+
!!! note
153+
154+
This code is based on the [default implementation](
155+
https://github.com/funql/funql-dotnet/blob/main/src/FunQL.Core/Constants/Visitors/JsonConstantPrintVisitor.cs),
156+
adapted to use Newtonsoft.Json instead of System.Text.Json.
157+
158+
### 2. Configure Schema
159+
160+
To use the `NewtonsoftJsonConstantPrintVisitor`, override the default `IConstantPrintVisitor` in your schema's
161+
`OnInitializeSchema` method.
162+
163+
```csharp
164+
public sealed class ApiSchema : Schema {
165+
protected override void OnInitializeSchema(ISchemaConfigBuilder schema) {
166+
schema.AddPrintFeature(it =>
167+
{
168+
IConstantPrintVisitor<IPrintVisitorState>? constantPrintVisitor = null;
169+
it.MutableConfig.ConstantPrintVisitorProvider = _ =>
170+
constantPrintVisitor ??= new NewtonsoftJsonConstantPrintVisitor<IPrintVisitorState>(
171+
new JsonSerializerSettings()
172+
);
173+
});
174+
}
175+
}
176+
```
177+
178+
Now, when you print a FunQL query, the `NewtonsoftJsonConstantPrintVisitor` will be used to serialize the constants.

0 commit comments

Comments
 (0)