Skip to content

Commit 71f6ab0

Browse files
committed
Support getter only properties such as ElementArray in typed deserializer
1 parent a7f5618 commit 71f6ab0

File tree

3 files changed

+50
-14
lines changed

3 files changed

+50
-14
lines changed

AttributeList.cs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -261,13 +261,11 @@ public virtual object? this[string name]
261261
if (Owner != null && this == Owner.PrefixAttributes && value?.GetType() == typeof(Element))
262262
throw new AttributeTypeException("Elements are not supported as prefix attributes.");
263263

264-
var prop_attr = (PropertyInfo?)PropertyInfos[name];
264+
var prop = (PropertyInfo?)PropertyInfos[name];
265265

266-
if (prop_attr != null)
266+
if (prop != null)
267267
{
268-
PropertyInfo? prop = GetType().GetProperty(prop_attr.Name, BindingFlags.Public | BindingFlags.Instance);
269-
270-
if (prop != null && prop.CanWrite)
268+
if (prop.CanWrite)
271269
{
272270
// were actually fine with this being null, it will just set the value to null
273271
// but need to check so the type check doesn't fail if it is null
@@ -284,6 +282,24 @@ public virtual object? this[string name]
284282
}
285283
else
286284
{
285+
var existingArray = prop.GetValue(this) as Array<Element>;
286+
var incomingArray = value as Array<Element>;
287+
288+
if (existingArray is not null && incomingArray is not null)
289+
{
290+
// special case for reflection based deserialization
291+
if (existingArray.Count == 0)
292+
{
293+
existingArray.AddRange(incomingArray);
294+
return;
295+
}
296+
else
297+
{
298+
throw new InvalidOperationException($"Attribute '{name}' modifies property {prop.DeclaringType!.Name}.{prop.Name}, which is write only and can't be replaced.");
299+
}
300+
}
301+
302+
287303
throw new InvalidDataException("Property of deserialisation class must be writeable, make sure it's public and has a public setter");
288304
}
289305

ICodec.cs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,20 +230,38 @@ public static bool TryConstructCustomElement(Dictionary<string, Type> types, Dat
230230

231231
Type derivedType = classType;
232232

233-
ConstructorInfo? constructor = typeof(Element).GetConstructor(
233+
ConstructorInfo? elementConstructor = typeof(Element).GetConstructor(
234234
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
235235
null,
236236
[typeof(Datamodel), typeof(string), typeof(Guid), typeof(string)],
237237
null
238238
);
239239

240-
if (constructor == null)
240+
var customClassInitializer = derivedType.GetConstructor(
241+
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
242+
null,
243+
[],
244+
null
245+
);
246+
247+
if (elementConstructor == null)
241248
{
242249
throw new InvalidOperationException("Failed to get constructor while attemption reflection based deserialisation");
243250
}
244251

252+
if (customClassInitializer == null)
253+
{
254+
throw new InvalidOperationException("Failed to get custom element constructor.");
255+
}
256+
245257
object uninitializedObject = RuntimeHelpers.GetUninitializedObject(derivedType);
246-
constructor.Invoke(uninitializedObject, [dataModel, elem_name, elem_id, elem_class]);
258+
259+
elementConstructor.Invoke(uninitializedObject, [dataModel, elem_name, elem_id, elem_class]);
260+
261+
// this will initialize values such as
262+
// public Datamodel.ElementArray Children { get; } = [];
263+
customClassInitializer.Invoke(uninitializedObject, []);
264+
247265

248266
elem = (Element?)uninitializedObject;
249267
return true;

Tests/ValveMap.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,16 +175,17 @@ internal class CMapVariableSet : DMElement
175175
}
176176

177177

178+
[CamelCaseProperties]
178179
internal class CMapSelectionSet : DMElement
179180
{
180-
public Datamodel.ElementArray children { get; set; } = [];
181-
public string selectionSetName { get; set; } = string.Empty;
182-
public CObjectSelectionSetDataElement selectionSetData { get; set; } = [];
181+
public Datamodel.ElementArray Children { get; } = [];
182+
public string SelectionSetName { get; set; } = string.Empty;
183+
public CObjectSelectionSetDataElement SelectionSetData { get; set; } = [];
183184

184185
public CMapSelectionSet() { }
185186
public CMapSelectionSet(string name)
186187
{
187-
selectionSetName = name;
188+
SelectionSetName = name;
188189
}
189190
}
190191

@@ -202,13 +203,14 @@ internal class CMapEntity : BaseEntity
202203
}
203204

204205

206+
[LowercaseProperties]
205207
internal class CMapInstance : BaseEntity
206208
{
207209
/// <summary>
208210
/// A target <see cref="CMapGroup"/> to instance. With custom tint and transform.
209211
/// </summary>
210-
public CMapGroup? target { get; set; }
211-
public Datamodel.Color tintColor { get; set; } = new Datamodel.Color(255, 255, 255, 255);
212+
public CMapGroup? Target { get; set; }
213+
public Datamodel.Color TintColor { get; set; } = new Datamodel.Color(255, 255, 255, 255);
212214
}
213215

214216
internal class CMapGroup : MapNode

0 commit comments

Comments
 (0)