using System; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace StardewModdingAPI.Toolkit.Serialisation.Converters { /// Handles deserialisation of . internal class SemanticVersionConverter : JsonConverter { /********* ** Accessors *********/ /// Get whether this converter can read JSON. public override bool CanRead => true; /// Get whether this converter can write JSON. public override bool CanWrite => true; /********* ** Public methods *********/ /// Get whether this instance can convert the specified object type. /// The object type. public override bool CanConvert(Type objectType) { return typeof(ISemanticVersion).IsAssignableFrom(objectType); } /// Reads the JSON representation of the object. /// The JSON reader. /// The object type. /// The object being read. /// The calling serializer. public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { string path = reader.Path; switch (reader.TokenType) { case JsonToken.StartObject: return this.ReadObject(JObject.Load(reader)); case JsonToken.String: return this.ReadString(JToken.Load(reader).Value(), path); default: throw new SParseException($"Can't parse {nameof(ISemanticVersion)} from {reader.TokenType} node (path: {reader.Path})."); } } /// Writes the JSON representation of the object. /// The JSON writer. /// The value. /// The calling serializer. public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { writer.WriteValue(value?.ToString()); } /********* ** Private methods *********/ /// Read a JSON object. /// The JSON object to read. private ISemanticVersion ReadObject(JObject obj) { int major = obj.ValueIgnoreCase("MajorVersion"); int minor = obj.ValueIgnoreCase("MinorVersion"); int patch = obj.ValueIgnoreCase("PatchVersion"); string build = obj.ValueIgnoreCase("Build"); if (build == "0") build = null; // '0' from incorrect examples in old SMAPI documentation return new SemanticVersion(major, minor, patch, build); } /// Read a JSON string. /// The JSON string value. /// The path to the current JSON node. private ISemanticVersion ReadString(string str, string path) { if (string.IsNullOrWhiteSpace(str)) return null; if (!SemanticVersion.TryParse(str, out ISemanticVersion version)) throw new SParseException($"Can't parse semantic version from invalid value '{str}', should be formatted like 1.2, 1.2.30, or 1.2.30-beta (path: {path})."); return (SemanticVersion)version; } } }