Fauna v10 .NET/C# Driver 1.0.0
 
Loading...
Searching...
No Matches
ClassSerializer.cs
Go to the documentation of this file.
1using System.Diagnostics;
3using Fauna.Mapping;
4using Fauna.Types;
5
6namespace Fauna.Serialization;
7
8internal class ClassSerializer<T> : BaseSerializer<T>, IPartialDocumentSerializer
9{
10 private const string IdField = "id";
11 private const string NameField = "name";
12 private const string CollField = "coll";
13 private readonly MappingInfo _info;
14
15 public ClassSerializer(MappingInfo info)
16 {
17 Debug.Assert(info.Type == typeof(T));
18 _info = info;
19 }
20
21 public override List<FaunaType> GetSupportedTypes() => new List<FaunaType> { FaunaType.Document, FaunaType.Null, FaunaType.Object, FaunaType.Ref };
22
23 public object DeserializeDocument(MappingContext context, string? id, string? name, Module? coll, ref Utf8FaunaReader reader)
24 {
25 object instance = CreateInstance();
26 if (id is not null) TrySetId(instance, id);
27 if (name is not null) TrySetName(instance, name);
28 if (coll is not null) TrySetColl(instance, coll);
29 SetFields(instance, context, ref reader, TokenType.EndDocument);
30 return instance;
31 }
32
33 public override T Deserialize(MappingContext ctx, ref Utf8FaunaReader reader)
34 {
35 var endToken = reader.CurrentTokenType switch
36 {
37 TokenType.StartDocument => TokenType.EndDocument,
38 TokenType.StartObject => TokenType.EndObject,
39 TokenType.StartRef => TokenType.EndRef,
40 var other => throw UnexpectedToken(other),
41 };
42
43 if (endToken == TokenType.EndRef)
44 {
45 string? id = null;
46 string? name = null;
47 Module? coll = null;
48 string? cause = null;
49 bool exists = true;
50
51 while (reader.Read() && reader.CurrentTokenType != TokenType.EndRef)
52 {
53 if (reader.CurrentTokenType != TokenType.FieldName)
54 throw new SerializationException(
55 $"Unexpected token while deserializing into Ref: {reader.CurrentTokenType}");
56
57 string fieldName = reader.GetString()!;
58 reader.Read();
59 switch (fieldName)
60 {
61 case "id":
62 id = reader.GetString();
63 break;
64 case "name":
65 name = reader.GetString();
66 break;
67 case "coll":
68 coll = reader.GetModule();
69 break;
70 case "cause":
71 cause = reader.GetString();
72 break;
73 case "exists":
74 exists = reader.GetBoolean();
75 break;
76 }
77 }
78
79 if ((id != null || name != null) && coll != null && exists)
80 {
81 throw new SerializationException("Cannot deserialize refs into classes.");
82 }
83
84 if (id != null)
85 {
86 throw new NullDocumentException(id, null, coll!, cause!);
87 }
88
89 throw new NullDocumentException(null, name, coll!, cause!);
90 }
91
92 object instance = CreateInstance();
93 SetFields(instance, ctx, ref reader, endToken);
94 return (T)instance;
95 }
96
97 public override void Serialize(MappingContext ctx, Utf8FaunaWriter writer, object? o)
98 {
99 SerializeInternal(ctx, writer, o);
100 }
101
102 private static void SerializeInternal(MappingContext ctx, Utf8FaunaWriter w, object? o)
103 {
104 if (o == null)
105 {
106 w.WriteNullValue();
107 return;
108 }
109
110 var t = o.GetType();
111 var info = ctx.GetInfo(t);
112 bool shouldEscape = info.ShouldEscapeObject;
113
114 if (shouldEscape) w.WriteStartEscapedObject(); else w.WriteStartObject();
115 foreach (var field in info.Fields)
116 {
117 if (field.FieldType is FieldType.ServerGeneratedId or FieldType.Ts or FieldType.Coll)
118 {
119 continue;
120 }
121
122 object? v = field.Property.GetValue(o);
123 if (field.FieldType is FieldType.ClientGeneratedId && v == null)
124 {
125 // The field is a client generated ID but set to null, so assume they're doing something
126 // other than creating the object.
127 continue;
128 }
129
130 w.WriteFieldName(field.Name);
131 field.Serializer.Serialize(ctx, w, v);
132 }
133 if (shouldEscape) w.WriteEndEscapedObject(); else w.WriteEndObject();
134 }
135
136 private object CreateInstance() => Activator.CreateInstance(_info.Type)!;
137
138 private void SetFields(object instance, MappingContext context, ref Utf8FaunaReader reader, TokenType endToken)
139 {
140 while (reader.Read() && reader.CurrentTokenType != endToken)
141 {
142 if (reader.CurrentTokenType != TokenType.FieldName)
143 throw UnexpectedToken(reader.CurrentTokenType);
144
145 string fieldName = reader.GetString()!;
146 reader.Read();
147
148 if (fieldName == IdField && reader.CurrentTokenType == TokenType.String)
149 {
150 TrySetId(instance, reader.GetString()!);
151 }
152 else if (fieldName == NameField && reader.CurrentTokenType == TokenType.String)
153 {
154 TrySetName(instance, reader.GetString()!);
155 }
156 else if (_info.FieldsByName.TryGetValue(fieldName, out var field))
157 {
158 field.Property.SetValue(instance, field.Serializer.Deserialize(context, ref reader));
159 }
160 else
161 {
162 reader.Skip();
163 }
164 }
165 }
166
167 private void TrySetId(object instance, string id)
168 {
169 if (!_info.FieldsByName.TryGetValue(IdField, out var field))
170 {
171 return;
172 }
173
174 if (field.Type == typeof(long))
175 {
176 field.Property.SetValue(instance, long.Parse(id));
177 }
178 else if (field.Type == typeof(string))
179 {
180 field.Property.SetValue(instance, id);
181 }
182 else
183 {
184 throw UnexpectedToken(TokenType.String);
185 }
186 }
187
188 private void TrySetName(object instance, string name)
189 {
190 if (_info.FieldsByName.TryGetValue(NameField, out var field))
191 {
192 if (field.Type == typeof(string))
193 {
194 field.Property.SetValue(instance, name);
195 }
196 else
197 {
198 throw UnexpectedToken(TokenType.String);
199 }
200 }
201 }
202
203 private void TrySetColl(object instance, Module coll)
204 {
205 if (_info.FieldsByName.TryGetValue(CollField, out var field))
206 {
207 if (field.Type == typeof(Module))
208 {
209 field.Property.SetValue(instance, coll);
210 }
211 else
212 {
213 throw UnexpectedToken(TokenType.String);
214 }
215 }
216 }
217
218 private new SerializationException UnexpectedToken(TokenType tokenType) =>
219 new($"Unexpected token while deserializing into class {_info.Type.Name}: {tokenType}");
220}
An exception representing a case when a document cannot be materialized because it does not exist.
Represents error that occur during serialization and deserialization of Fauna data.
A class representing the mapping context to be used during serialization and deserialization.
MappingInfo GetInfo(Type ty, string? colName=null)
Gets the MappingInfo for a given Type.
A class that encapsulates the class mapping, serialization, and deserialization of a Fauna object,...
Type Type
The associated type.
Represents a module, a singleton object grouping related functionalities. Modules are serialized as @...
Definition Module.cs:8
void Serialize(MappingContext ctx, Utf8FaunaWriter writer, object? o)
Serializes the provided object into the Utf8FaunaWriter.
new T Deserialize(MappingContext ctx, ref Utf8FaunaReader reader)
Consumes all or some of a Utf8FaunaReader and returns an instance of T .
FieldType
An enum for the field type, used with concrete implementations of BaseFieldAttribute.
Definition FieldType.cs:7
FaunaType
An enum representing possible Fauna types.
Definition FaunaType.cs:7
TokenType
Enumerates the types of tokens used in Fauna serialization.
Definition TokenType.cs:7