using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Xna.Framework.Input;
using Newtonsoft.Json;
namespace StardewModdingAPI.Framework.Serialisation
{
/// Encapsulates SMAPI's JSON file parsing.
internal class JsonHelper
{
/*********
** Accessors
*********/
/// The JSON settings to use when serialising and deserialising files.
private readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ObjectCreationHandling = ObjectCreationHandling.Replace, // avoid issue where default ICollection values are duplicated each time the config is loaded
Converters = new List
{
new SelectiveStringEnumConverter(),
new SelectiveStringEnumConverter(),
new SelectiveStringEnumConverter()
}
};
/*********
** Public methods
*********/
/// Read a JSON file.
/// The model type.
/// The absolete file path.
/// Returns the deserialised model, or null if the file doesn't exist or is empty.
/// The given path is empty or invalid.
public TModel ReadJsonFile(string fullPath)
where TModel : class
{
// validate
if (string.IsNullOrWhiteSpace(fullPath))
throw new ArgumentException("The file path is empty or invalid.", nameof(fullPath));
// read file
string json;
try
{
json = File.ReadAllText(fullPath);
}
catch (Exception ex) when (ex is DirectoryNotFoundException || ex is FileNotFoundException)
{
return null;
}
// deserialise model
try
{
return JsonConvert.DeserializeObject(json, this.JsonSettings);
}
catch (JsonReaderException ex)
{
string message = $"The file at {fullPath} doesn't seem to be valid JSON.";
string text = File.ReadAllText(fullPath);
if (text.Contains("“") || text.Contains("”"))
message += " Found curly quotes in the text; note that only straight quotes are allowed in JSON.";
message += $"\nTechnical details: {ex.Message}";
throw new JsonReaderException(message);
}
}
/// Save to a JSON file.
/// The model type.
/// The absolete file path.
/// The model to save.
/// The given path is empty or invalid.
public void WriteJsonFile(string fullPath, TModel model)
where TModel : class
{
// validate
if (string.IsNullOrWhiteSpace(fullPath))
throw new ArgumentException("The file path is empty or invalid.", nameof(fullPath));
// create directory if needed
string dir = Path.GetDirectoryName(fullPath);
if (dir == null)
throw new ArgumentException("The file path is invalid.", nameof(fullPath));
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
// write file
string json = JsonConvert.SerializeObject(model, this.JsonSettings);
File.WriteAllText(fullPath, json);
}
}
}