Array and list support for VKV.
This commit is contained in:
parent
ab0453e0ab
commit
c1a90cfc99
@ -1,4 +1,5 @@
|
|||||||
using Valve.Vkv.ObjectModels;
|
using System;
|
||||||
|
using Valve.Vkv.ObjectModels;
|
||||||
|
|
||||||
using ValveParsers = Valve.Miscellaneous.TypeParsers;
|
using ValveParsers = Valve.Miscellaneous.TypeParsers;
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ public static class VkvConvert
|
|||||||
name = DeserializeString(parts[0], options);
|
name = DeserializeString(parts[0], options);
|
||||||
if (parts.Length == 2)
|
if (parts.Length == 2)
|
||||||
{
|
{
|
||||||
object value = DeserializeObject(DeserializeString(parts[1], options));
|
string value = DeserializeString(parts[1], options);
|
||||||
node = new VkvSingleNode(value);
|
node = new VkvSingleNode(value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -69,8 +70,6 @@ public static class VkvConvert
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static object DeserializeObject(string content) =>
|
|
||||||
ValveParsers.ParseAll(content);
|
|
||||||
private static string DeserializeString(string content, VkvOptions options)
|
private static string DeserializeString(string content, VkvOptions options)
|
||||||
{
|
{
|
||||||
if (options.useQuotes)
|
if (options.useQuotes)
|
||||||
@ -120,76 +119,122 @@ public static class VkvConvert
|
|||||||
{
|
{
|
||||||
if (node is null) return null;
|
if (node is null) return null;
|
||||||
|
|
||||||
if (node is VkvSingleNode single)
|
if (node is VkvSingleNode single) return FromSingleNode(outputType, single);
|
||||||
{
|
else if (node is VkvTreeNode tree) return FromTreeNode(outputType, tree, options);
|
||||||
object? value = single.value;
|
|
||||||
if (value is null) return null;
|
|
||||||
else if (value is string str)
|
|
||||||
{
|
|
||||||
value = ValveParsers.ParseAll(str);
|
|
||||||
if (value is string still && outputType.IsEnum)
|
|
||||||
{
|
|
||||||
if (Enum.TryParse(outputType, still, true, out object? res) && res is not null)
|
|
||||||
value = res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Convert.ChangeType(value, outputType);
|
|
||||||
}
|
|
||||||
else if (node is VkvTreeNode tree)
|
|
||||||
{
|
|
||||||
object? instance = Activator.CreateInstance(outputType);
|
|
||||||
if (instance is null) return null;
|
|
||||||
|
|
||||||
IEnumerable<FieldInfo> validFields = from field in outputType.GetFields()
|
|
||||||
let isPublic = field.IsPublic
|
|
||||||
let isStatic = field.IsStatic
|
|
||||||
let isIgnored = field.CustomAttributes.Any(x =>
|
|
||||||
x.AttributeType == typeof(VkvIgnoreAttribute))
|
|
||||||
let isConst = field.IsLiteral
|
|
||||||
where isPublic && !isStatic && !isIgnored && !isConst
|
|
||||||
select field;
|
|
||||||
|
|
||||||
IEnumerable<PropertyInfo> validProperties;
|
|
||||||
if (options.serializeProperties)
|
|
||||||
{
|
|
||||||
validProperties = from prop in outputType.GetProperties()
|
|
||||||
let canSet = prop.SetMethod is not null
|
|
||||||
let isPublic = canSet && prop.SetMethod!.IsPublic
|
|
||||||
let isStatic = canSet && prop.SetMethod!.IsStatic
|
|
||||||
let isIgnored = prop.CustomAttributes.Any(x =>
|
|
||||||
x.AttributeType == typeof(VkvIgnoreAttribute))
|
|
||||||
where canSet && isPublic && !isStatic && !isIgnored
|
|
||||||
select prop;
|
|
||||||
}
|
|
||||||
else validProperties = Array.Empty<PropertyInfo>();
|
|
||||||
|
|
||||||
foreach (FieldInfo field in validFields)
|
|
||||||
{
|
|
||||||
string name = field.Name;
|
|
||||||
|
|
||||||
VkvNode? subNode = tree[name];
|
|
||||||
if (subNode is null) continue;
|
|
||||||
|
|
||||||
object? result = FromNodeTree(field.FieldType, subNode, options);
|
|
||||||
if (result is null) continue;
|
|
||||||
field.SetValue(instance, result);
|
|
||||||
}
|
|
||||||
foreach (PropertyInfo prop in validProperties)
|
|
||||||
{
|
|
||||||
string name = prop.Name;
|
|
||||||
|
|
||||||
VkvNode? subNode = tree[name];
|
|
||||||
if (subNode is null) continue;
|
|
||||||
|
|
||||||
object? result = FromNodeTree(prop.PropertyType, subNode, options);
|
|
||||||
if (result is null) continue;
|
|
||||||
prop.SetValue(instance, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
else throw new VkvSerializationException("Unknown VKV node type.");
|
else throw new VkvSerializationException("Unknown VKV node type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static object? FromSingleNode(Type outputType, VkvSingleNode node)
|
||||||
|
{
|
||||||
|
object? value = node.value;
|
||||||
|
if (value is null) return null;
|
||||||
|
else if (value is string str)
|
||||||
|
{
|
||||||
|
value = ValveParsers.ParseAll(str);
|
||||||
|
if (value is string still && outputType.IsEnum)
|
||||||
|
{
|
||||||
|
if (Enum.TryParse(outputType, still, true, out object? res) && res is not null)
|
||||||
|
value = res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Convert.ChangeType(value, outputType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object? FromTreeNode(Type outputType, VkvTreeNode node, VkvOptions options)
|
||||||
|
{
|
||||||
|
if (outputType.IsArray)
|
||||||
|
return FromTreeNodeArray(outputType, node, options);
|
||||||
|
|
||||||
|
else if (outputType.GetInterface("IList") is not null)
|
||||||
|
return FromTreeNodeList(outputType, node, options);
|
||||||
|
|
||||||
|
object? instance = Activator.CreateInstance(outputType);
|
||||||
|
if (instance is null) return null;
|
||||||
|
|
||||||
|
IEnumerable<FieldInfo> validFields = from field in outputType.GetFields()
|
||||||
|
let isPublic = field.IsPublic
|
||||||
|
let isStatic = field.IsStatic
|
||||||
|
let isIgnored = field.CustomAttributes.Any(x =>
|
||||||
|
x.AttributeType == typeof(VkvIgnoreAttribute))
|
||||||
|
let isConst = field.IsLiteral
|
||||||
|
where isPublic && !isStatic && !isIgnored && !isConst
|
||||||
|
select field;
|
||||||
|
|
||||||
|
IEnumerable<PropertyInfo> validProperties;
|
||||||
|
if (options.serializeProperties)
|
||||||
|
{
|
||||||
|
validProperties = from prop in outputType.GetProperties()
|
||||||
|
let canSet = prop.SetMethod is not null
|
||||||
|
let isPublic = canSet && prop.SetMethod!.IsPublic
|
||||||
|
let isStatic = canSet && prop.SetMethod!.IsStatic
|
||||||
|
let isIgnored = prop.CustomAttributes.Any(x =>
|
||||||
|
x.AttributeType == typeof(VkvIgnoreAttribute))
|
||||||
|
where canSet && isPublic && !isStatic && !isIgnored
|
||||||
|
select prop;
|
||||||
|
}
|
||||||
|
else validProperties = Array.Empty<PropertyInfo>();
|
||||||
|
|
||||||
|
foreach (FieldInfo field in validFields)
|
||||||
|
{
|
||||||
|
string name = field.Name;
|
||||||
|
|
||||||
|
VkvNode? subNode = node[name];
|
||||||
|
if (subNode is null) continue;
|
||||||
|
|
||||||
|
object? result = FromNodeTree(field.FieldType, subNode, options);
|
||||||
|
if (result is null) continue;
|
||||||
|
field.SetValue(instance, result);
|
||||||
|
}
|
||||||
|
foreach (PropertyInfo prop in validProperties)
|
||||||
|
{
|
||||||
|
string name = prop.Name;
|
||||||
|
|
||||||
|
VkvNode? subNode = node[name];
|
||||||
|
if (subNode is null) continue;
|
||||||
|
|
||||||
|
object? result = FromNodeTree(prop.PropertyType, subNode, options);
|
||||||
|
if (result is null) continue;
|
||||||
|
prop.SetValue(instance, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object? FromTreeNodeArray(Type outputType, VkvTreeNode node, VkvOptions options)
|
||||||
|
{
|
||||||
|
Type elementType = outputType.GetElementType()!;
|
||||||
|
Array array = Array.CreateInstance(elementType, node.SubNodeCount);
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
foreach (KeyValuePair<string, VkvNode?> subNode in node)
|
||||||
|
{
|
||||||
|
string indexStr = index.ToString();
|
||||||
|
if (subNode.Key != indexStr) throw new VkvSerializationException($"Cannot convert node tree to array.");
|
||||||
|
array.SetValue(FromNodeTree(elementType, subNode.Value, options), index);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object? FromTreeNodeList(Type outputType, VkvTreeNode node, VkvOptions options)
|
||||||
|
{
|
||||||
|
IList? instance = (IList?)Activator.CreateInstance(outputType);
|
||||||
|
if (instance is null) return null;
|
||||||
|
|
||||||
|
// There is no guarentee that the first type argument corresponds to the element type,
|
||||||
|
// but as far as I know there isn't a better way.
|
||||||
|
Type elementType = outputType.IsGenericType ? outputType.GetGenericArguments().First() : typeof(object);
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
foreach (KeyValuePair<string, VkvNode?> subNode in node)
|
||||||
|
{
|
||||||
|
string indexStr = index.ToString();
|
||||||
|
if (subNode.Key != indexStr) throw new VkvSerializationException($"Cannot convert node tree to array.");
|
||||||
|
instance.Add(FromNodeTree(elementType, subNode.Value, options));
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region SerializeNode
|
#region SerializeNode
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user