using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Text.Json;
using JSONEXCEPTION = System.Text.Json.JsonException;
using JSONTOKEN = System.Text.Json.JsonTokenType;
namespace SharpGLTF.IO
{
///
/// Represents the base class of a serializable glTF schema2 object.
/// Inherited by .
///
public abstract class JsonSerializable
{
#region validation
internal void ValidateReferences(Validation.ValidationContext validate)
{
validate = validate.GetContext(this);
OnValidateReferences(validate);
}
internal void ValidateContent(Validation.ValidationContext validate)
{
validate = validate.GetContext(this);
OnValidateContent(validate);
}
protected virtual void OnValidateReferences(Validation.ValidationContext validate) { }
protected virtual void OnValidateContent(Validation.ValidationContext validate) { }
#endregion
#region serialization
internal void Serialize(Utf8JsonWriter writer)
{
Guard.NotNull(writer, nameof(writer));
writer.WriteStartObject();
SerializeProperties(writer);
writer.WriteEndObject();
}
protected abstract void SerializeProperties(Utf8JsonWriter writer);
protected static void SerializeProperty(Utf8JsonWriter writer, string name, Object value)
{
if (value == null) return;
Guard.NotNull(writer, nameof(writer));
_SerializeProperty(writer, name, value);
}
protected static void SerializeProperty(Utf8JsonWriter writer, string name, Boolean? value, Boolean? defval = null)
{
if (!value.HasValue) return;
if (defval.HasValue && defval.Value.Equals(value.Value)) return;
Guard.NotNull(writer, nameof(writer));
writer.WriteBoolean(name, value.Value);
}
protected static void SerializeProperty(Utf8JsonWriter writer, string name, Int32? value, Int32? defval = null)
{
if (!value.HasValue) return;
if (defval.HasValue && defval.Value.Equals(value.Value)) return;
Guard.NotNull(writer, nameof(writer));
writer.WriteNumber(name, value.Value);
}
protected static void SerializeProperty(Utf8JsonWriter writer, string name, Single? value, Single? defval = null)
{
if (!value.HasValue) return;
if (defval.HasValue && defval.Value.Equals(value.Value)) return;
Guard.NotNull(writer, nameof(writer));
writer.WriteNumber(name, value.Value);
}
protected static void SerializeProperty(Utf8JsonWriter writer, string name, Double? value, Double? defval = null)
{
if (!value.HasValue) return;
if (defval.HasValue && defval.Value.Equals(value.Value)) return;
Guard.NotNull(writer, nameof(writer));
writer.WriteNumber(name, value.Value);
}
protected static void SerializeProperty(Utf8JsonWriter writer, string name, Vector2? value, Vector2? defval = null)
{
if (!value.HasValue) return;
if (defval.HasValue && defval.Value.Equals(value.Value)) return;
Guard.NotNull(writer, nameof(writer));
writer.WritePropertyName(name);
writer.WriteVector2(value.Value);
}
protected static void SerializeProperty(Utf8JsonWriter writer, string name, Vector3? value, Vector3? defval = null)
{
if (!value.HasValue) return;
if (defval.HasValue && defval.Value.Equals(value.Value)) return;
Guard.NotNull(writer, nameof(writer));
writer.WritePropertyName(name);
writer.WriteVector3(value.Value);
}
protected static void SerializeProperty(Utf8JsonWriter writer, string name, Vector4? value, Vector4? defval = null)
{
if (!value.HasValue) return;
if (defval.HasValue && defval.Value.Equals(value.Value)) return;
Guard.NotNull(writer, nameof(writer));
writer.WritePropertyName(name);
writer.WriteVector4(value.Value);
}
protected static void SerializeProperty(Utf8JsonWriter writer, string name, Quaternion? value, Quaternion? defval = null)
{
if (!value.HasValue) return;
if (defval.HasValue && defval.Value.Equals(value.Value)) return;
Guard.NotNull(writer, nameof(writer));
writer.WritePropertyName(name);
writer.WriteQuaternion(value.Value);
}
protected static void SerializeProperty(Utf8JsonWriter writer, string name, Matrix4x4? value, Matrix4x4? defval = null)
{
if (!value.HasValue) return;
if (defval.HasValue && defval.Value.Equals(value.Value)) return;
Guard.NotNull(writer, nameof(writer));
writer.WritePropertyName(name);
writer.WriteMatrix4x4(value.Value);
}
protected static void SerializePropertyEnumValue(Utf8JsonWriter writer, string name, T? value, T? defval = null)
where T : struct
{
Guard.IsTrue(typeof(T).IsEnum, nameof(T));
if (!value.HasValue) return;
if (defval.HasValue && defval.Value.Equals(value)) return;
Guard.NotNull(writer, nameof(writer));
writer.WriteNumber(name, (int)(Object)value);
}
protected static void SerializePropertyEnumSymbol(Utf8JsonWriter writer, string name, T? value, T? defval = null)
where T : struct
{
Guard.IsTrue(typeof(T).IsEnum, nameof(T));
if (!value.HasValue) return;
if (defval.HasValue && defval.Value.Equals(value)) return;
Guard.NotNull(writer, nameof(writer));
writer.WriteString(name, Enum.GetName(typeof(T), value));
}
protected static void SerializePropertyObject(Utf8JsonWriter writer, string name, T value)
where T : JsonSerializable
{
if (value == null) return;
Guard.NotNull(writer, nameof(writer));
_SerializeProperty(writer, name, value);
}
protected static void SerializeProperty(Utf8JsonWriter writer, string name, IReadOnlyList collection, int? minItems = 1)
{
if (collection == null) return;
if (minItems.HasValue && collection.Count < minItems.Value) return;
Guard.NotNull(writer, nameof(writer));
writer.WritePropertyName(name);
writer.WriteStartArray();
foreach (var item in collection) _SerializeValue(writer, item);
writer.WriteEndArray();
}
protected static void SerializeProperty(Utf8JsonWriter writer, string name, IReadOnlyDictionary collection)
{
if (collection == null) return;
if (collection.Count < 1) return;
Guard.NotNull(writer, nameof(writer));
writer.WritePropertyName(name);
writer.WriteStartObject();
foreach (var item in collection) _SerializeProperty(writer, item.Key, item.Value);
writer.WriteEndObject();
}
private static void _SerializeProperty(Utf8JsonWriter writer, String name, Object value)
{
Guard.NotNull(writer, nameof(writer));
Guard.NotNull(value, nameof(value));
if (_IsNullOrEmpty(value)) return;
if (writer.TryWriteProperty(name, value)) return;
writer.WritePropertyName(name);
_SerializeValue(writer, value);
}
private static bool _IsNullOrEmpty(Object value)
{
if (value == null) return true;
if (value is System.Collections.ICollection c && c.Count == 0) return true;
return false;
}
private static void _SerializeValue(Utf8JsonWriter writer, Object value)
{
Guard.NotNull(writer, nameof(writer));
Guard.NotNull(value, nameof(value));
System.Diagnostics.Debug.Assert(!value.GetType().IsEnum, "gltf schema does not define a typed way of serializing enums");
if (writer.TryWriteValue(value)) return;
if (value is System.Text.Json.Nodes.JsonNode jnode)
{
jnode.WriteTo(writer);
return;
}
if (value is JsonSerializable vgltf) { vgltf.Serialize(writer); return; }
if (value is System.Collections.IDictionary dict)
{
if (dict.Count == 0) return;
writer.WriteStartObject();
foreach (var key in dict.Keys)
{
var val = dict[key];
if (val == null) continue;
// if the value is a collection, we need to check if the collection is empty
// to prevent writing the key, without writing the value.
if (!(val is String || val is JsonSerializable))
{
if (val is System.Collections.IList xlist && xlist.Count == 0) continue;
if (val is System.Collections.IDictionary xdict && xdict.Count == 0) continue;
}
_SerializeProperty(writer, key.ToString(), val);
}
writer.WriteEndObject();
return;
}
if (value is System.Collections.IList list)
{
if (list.Count == 0) return;
writer.WriteStartArray();
foreach (var item in list)
{
_SerializeValue(writer, item);
}
writer.WriteEndArray();
return;
}
throw new NotImplementedException($"Serialization of {value.GetType().Name} types is not supported.");
}
#endregion
#region deserialization
internal void Deserialize(ref Utf8JsonReader reader)
{
if (reader.TokenType == JSONTOKEN.PropertyName) reader.Read();
if (reader.TokenType == JSONTOKEN.StartObject)
{
while (reader.Read() && reader.TokenType != JSONTOKEN.EndObject)
{
if (reader.TokenType == JSONTOKEN.PropertyName)
{
var key = reader.GetString();
DeserializeProperty(key, ref reader);
}
else
{
throw new NotImplementedException();
}
}
return;
}
throw new JSONEXCEPTION($"Unexpected token {reader.TokenType}");
}
protected static Object DeserializeUnknownObject(ref Utf8JsonReader reader)
{
if (reader.TokenType == JSONTOKEN.PropertyName) reader.Read();
if (reader.TokenType == JSONTOKEN.StartArray)
{
var list = new List