Fauna v10 .NET/C# Driver 0.2.0-beta
 
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 interface IClassDocumentSerializer : ISerializer
9{
10 public object DeserializeDocument(MappingContext context, string? id, string? name, ref Utf8FaunaReader reader);
11}
12
13internal class ClassSerializer<T> : BaseSerializer<T>, IClassDocumentSerializer
14{
15 private const string IdField = "id";
16 private const string NameField = "name";
17 private readonly MappingInfo _info;
18 private readonly bool _isDocument;
19
20 public ClassSerializer(MappingInfo info)
21 {
22 Debug.Assert(info.Type == typeof(T));
23 _info = info;
24 }
25
26 public ClassSerializer(MappingInfo info, bool isDocument)
27 {
28 Debug.Assert(info.Type == typeof(T));
29 _info = info;
30 _isDocument = isDocument;
31 }
32
33 public object DeserializeDocument(MappingContext context, string? id, string? name, ref Utf8FaunaReader reader)
34 {
35 object instance = CreateInstance();
36 if (id is not null) TrySetId(instance, id);
37 if (name is not null) TrySetName(instance, name);
38 SetFields(instance, context, ref reader, TokenType.EndDocument);
39 return instance;
40 }
41
42 public override T Deserialize(MappingContext context, ref Utf8FaunaReader reader)
43 {
44 var endToken = reader.CurrentTokenType switch
45 {
46 TokenType.StartDocument => TokenType.EndDocument,
47 TokenType.StartObject => TokenType.EndObject,
48 TokenType.StartRef => TokenType.EndRef,
49 var other => throw UnexpectedToken(other),
50 };
51
52 if (endToken == TokenType.EndRef)
53 {
54 string? id = null;
55 string? name = null;
56 Module? coll = null;
57 string? cause = null;
58 bool exists = true;
59
60 while (reader.Read() && reader.CurrentTokenType != TokenType.EndRef)
61 {
62 if (reader.CurrentTokenType != TokenType.FieldName)
63 throw new SerializationException(
64 $"Unexpected token while deserializing into DocumentRef: {reader.CurrentTokenType}");
65
66 string fieldName = reader.GetString()!;
67 reader.Read();
68 switch (fieldName)
69 {
70 case "id":
71 id = reader.GetString();
72 break;
73 case "name":
74 name = reader.GetString();
75 break;
76 case "coll":
77 coll = reader.GetModule();
78 break;
79 case "cause":
80 cause = reader.GetString();
81 break;
82 case "exists":
83 exists = reader.GetBoolean();
84 break;
85 }
86 }
87
88 if ((id != null || name != null) && coll != null && exists)
89 {
90 throw new SerializationException("Cannot deserialize refs into classes.");
91 }
92
93 throw new NullDocumentException((id ?? name)!, coll!, cause!);
94 }
95
96 object instance = CreateInstance();
97 SetFields(instance, context, ref reader, endToken);
98 return (T)instance;
99 }
100
101 public override void Serialize(MappingContext ctx, Utf8FaunaWriter w, object? o)
102 {
103 var skipFields = new HashSet<string>();
104
105 if (_isDocument)
106 {
107 foreach (FieldInfo fi in _info.Fields)
108 {
109 if (fi.Name.ToLowerInvariant() == "id" && fi.Property.GetValue(o) is null) skipFields.Add("id");
110 if (fi.Name.ToLowerInvariant() == "coll" && fi.Property.GetValue(o) is null) skipFields.Add("coll");
111 if (fi.Name.ToLowerInvariant() == "ts" && fi.Property.GetValue(o) is null) skipFields.Add("ts");
112 }
113 }
114
115 SerializeInternal(ctx, w, o, skipFields);
116 }
117
118 private static void SerializeInternal(MappingContext ctx, Utf8FaunaWriter w, object? o, IReadOnlySet<string> skipFields)
119 {
120 if (o == null)
121 {
122 w.WriteNullValue();
123 return;
124 }
125
126 var t = o.GetType();
127 var info = ctx.GetInfo(t);
128 bool shouldEscape = info.ShouldEscapeObject;
129
130 if (shouldEscape) w.WriteStartEscapedObject(); else w.WriteStartObject();
131 foreach (var field in info.Fields)
132 {
133 if (skipFields.Contains(field.Name.ToLowerInvariant())) continue;
134
135 w.WriteFieldName(field.Name);
136 object? v = field.Property.GetValue(o);
137 field.Serializer.Serialize(ctx, w, v);
138 }
139 if (shouldEscape) w.WriteEndEscapedObject(); else w.WriteEndObject();
140 }
141
142 private object CreateInstance() => Activator.CreateInstance(_info.Type)!;
143
144 private void SetFields(object instance, MappingContext context, ref Utf8FaunaReader reader, TokenType endToken)
145 {
146 while (reader.Read() && reader.CurrentTokenType != endToken)
147 {
148 if (reader.CurrentTokenType != TokenType.FieldName)
149 throw UnexpectedToken(reader.CurrentTokenType);
150
151 string fieldName = reader.GetString()!;
152 reader.Read();
153
154 if (fieldName == IdField && reader.CurrentTokenType == TokenType.String)
155 {
156 TrySetId(instance, reader.GetString()!);
157 }
158 else if (fieldName == NameField && reader.CurrentTokenType == TokenType.String)
159 {
160 TrySetName(instance, reader.GetString()!);
161 }
162 else if (_info.FieldsByName.TryGetValue(fieldName, out var field))
163 {
164 field.Property.SetValue(instance, field.Serializer.Deserialize(context, ref reader));
165 }
166 else
167 {
168 reader.Skip();
169 }
170 }
171 }
172
173 private void TrySetId(object instance, string id)
174 {
175 if (!_info.FieldsByName.TryGetValue(IdField, out var field))
176 {
177 return;
178 }
179
180 if (field.Type == typeof(long))
181 {
182 field.Property.SetValue(instance, long.Parse(id));
183 }
184 else if (field.Type == typeof(string))
185 {
186 field.Property.SetValue(instance, id);
187 }
188 else
189 {
190 throw UnexpectedToken(TokenType.String);
191 }
192 }
193
194 private void TrySetName(object instance, string name)
195 {
196 if (_info.FieldsByName.TryGetValue(NameField, out var field))
197 {
198 if (field.Type == typeof(string))
199 {
200 field.Property.SetValue(instance, name);
201 }
202 else
203 {
204 throw UnexpectedToken(TokenType.String);
205 }
206 }
207 }
208
209 private new SerializationException UnexpectedToken(TokenType tokenType) =>
210 new($"Unexpected token while deserializing into class {_info.Type.Name}: {tokenType}");
211}
A class that encapsulates the field mapping, serialization, and deserialization of a particular field...
Definition FieldInfo.cs:11
PropertyInfo Property
The property info of an associated class.
Definition FieldInfo.cs:19
string Name
The name of the field.
Definition FieldInfo.cs:15
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
TokenType
Enumerates the types of tokens used in Fauna serialization.
Definition TokenType.cs:7