// JsonKit v0.5 - A simple but flexible Json library in a single .cs file.
//
// Copyright (C) 2014 Topten Software (contact@toptensoftware.com) All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
using System;
using System.IO;
namespace Topten.JsonKit
{
///
/// The main static interface class to JsonKit
///
public static class Json
{
static Json()
{
WriteWhitespaceDefault = true;
StrictParserDefault = false;
#if !JSONKIT_NO_EMIT
Json.SetFormatterResolver(Emit.MakeFormatter);
Json.SetParserResolver(Emit.MakeParser);
Json.SetIntoParserResolver(Emit.MakeIntoParser);
#endif
}
///
/// Controls whether the write whitespace by default
///
public static bool WriteWhitespaceDefault
{
get;
set;
}
///
/// Controls whether parsing should be strict by default
///
public static bool StrictParserDefault
{
get;
set;
}
// Write an object to a text writer
///
/// Writes an object to a TextWriter
///
/// The target text writer
/// The object to be written
/// Options controlling output formatting
public static void Write(TextWriter w, object o, JsonOptions options = JsonOptions.None)
{
var writer = new JsonWriter(w, ResolveOptions(options));
writer.WriteValue(o);
}
///
/// Controls whether previous version should be saved if AutoSavePreviousVersion is used
///
public static bool SavePreviousVersions
{
get;
set;
}
///
/// Write a file atomically by writing to a temp file and then renaming it - prevents corrupted files if crash
/// in middle of writing file.
///
/// The output file name
/// The object to be written
/// Options controlling output
/// An optional back filename where previous version will be written
public static void WriteFileAtomic(string filename, object o, JsonOptions options = JsonOptions.None, string backupFilename = null)
{
var tempName = filename + ".tmp";
try
{
// Write the temp file
WriteFile(tempName, o, (options | JsonOptions.Flush));
if (System.IO.File.Exists(filename))
{
bool savePreviousVersion = false;
if ((options & JsonOptions.AutoSavePreviousVersion)!=0)
{
savePreviousVersion = SavePreviousVersions;
}
else if ((options & JsonOptions.SavePreviousVersion)!=0)
{
savePreviousVersion = true;
}
// Work out backup filename
if (savePreviousVersion)
{
// Make sure have a backup filename
if (backupFilename == null)
{
backupFilename = filename + ".previous";
}
}
else
{
// No backup
backupFilename = null;
}
// Replace it
int retry = 0;
while (true)
{
try
{
File.Replace(tempName, filename, backupFilename);
break;
}
catch (System.IO.IOException x)
{
retry++;
if (retry >= 5)
{
throw new System.IO.IOException(string.Format("Failed to replace temp file {0} with {1} and backup {2}, reason {3}", tempName, filename, backupFilename, x.Message), x);
}
System.Threading.Thread.Sleep(2000);
}
}
}
else
{
// Rename it
File.Move(tempName, filename);
}
}
catch
{
Utils.DeleteFile(tempName);
throw;
}
}
///
/// Write an object to a file
///
/// The output filename
/// The object to be written
/// Options controlling output
public static void WriteFile(string filename, object o, JsonOptions options = JsonOptions.None)
{
using (var w = new StreamWriter(filename))
{
Write(w, o, options);
if ((options & JsonOptions.Flush) != 0)
{
w.Flush();
w.BaseStream.Flush();
}
}
}
///
/// Format an object as a json string
///
/// The value to be formatted
/// Options controlling output
/// The formatted string
public static string Format(object o, JsonOptions options = JsonOptions.None)
{
var sw = new StringWriter();
var writer = new JsonWriter(sw, ResolveOptions(options));
writer.WriteValue(o);
return sw.ToString();
}
///
/// Parse an object of specified type from a text reader
///
/// The text reader to read from
/// The type of object to be parsed
/// Options controlling parsing
/// The parsed object
public static object Parse(TextReader r, Type type, JsonOptions options = JsonOptions.None)
{
JsonReader reader = null;
try
{
reader = new JsonReader(r, ResolveOptions(options));
var retv = reader.Parse(type);
reader.CheckEOF();
return retv;
}
catch (Exception x)
{
var loc = reader == null ? new LineOffset() : reader.CurrentTokenPosition;
Console.WriteLine("Exception thrown while parsing JSON at {0}, context:{1}\n{2}", loc, reader?.Context, x.ToString());
throw new JsonParseException(x, reader?.Context, loc);
}
}
///
/// Parse an object of specified type from a text reader
///
/// The type of object to be parsed
/// The text reader to read from
/// Options controlling parsing
/// The parsed object
public static T Parse(TextReader r, JsonOptions options = JsonOptions.None)
{
return (T)Parse(r, typeof(T), options);
}
///
/// Parse from text reader into an already instantied object
///
/// The text reader to read from
/// The object to serialize into
/// Options controlling parsing
public static void ParseInto(TextReader r, Object into, JsonOptions options = JsonOptions.None)
{
if (into == null)
throw new NullReferenceException();
if (into.GetType().IsValueType)
throw new InvalidOperationException("Can't ParseInto a value type");
JsonReader reader = null;
try
{
reader = new JsonReader(r, ResolveOptions(options));
reader.ParseInto(into);
reader.CheckEOF();
}
catch (Exception x)
{
var loc = reader == null ? new LineOffset() : reader.CurrentTokenPosition;
Console.WriteLine("Exception thrown while parsing JSON at {0}, context:{1}\n{2}", loc, reader.Context, x.ToString());
throw new JsonParseException(x,reader.Context,loc);
}
}
///
/// Parse an object of specified type from a file
///
/// The input filename
/// The type of object to be parsed
/// Options controlling parsing
/// The parsed object instance
public static object ParseFile(string filename, Type type, JsonOptions options = JsonOptions.None)
{
using (var r = new StreamReader(filename))
{
return Parse(r, type, options);
}
}
// Parse an object of specified type from a file
///
/// Parse an object of specified type from a file
///
/// The type of object to be parsed
/// The input filename
/// Options controlling parsing
///
public static T ParseFile(string filename, JsonOptions options = JsonOptions.None)
{
using (var r = new StreamReader(filename))
{
return Parse(r, options);
}
}
///
/// Parse from file into an already instantied object
///
/// The input filename
/// The object to serialize into
/// Options controlling parsing
public static void ParseFileInto(string filename, Object into, JsonOptions options = JsonOptions.None)
{
using (var r = new StreamReader(filename))
{
ParseInto(r, into, options);
}
}
///
/// Parse an object from a string
///
/// The JSON data
/// The type of object to be parsed
/// Options controlling parsing
/// The parsed object instance
public static object Parse(string data, Type type, JsonOptions options = JsonOptions.None)
{
return Parse(new StringReader(data), type, options);
}
///
/// Parse an object from a string
///
/// The type of object to be parsed
/// The JSON data
/// Options controlling parsing
///
public static T Parse(string data, JsonOptions options = JsonOptions.None)
{
return (T)Parse(new StringReader(data), options);
}
///
/// Parse from string into an already instantiated object
///
/// The JSON data
/// The object to serialize into
/// Options controlling parsing
public static void ParseInto(string data, Object into, JsonOptions options = JsonOptions.None)
{
ParseInto(new StringReader(data), into, options);
}
///
/// Create a clone of an object by serializing to JSON and the deserializing into a new instance
///
/// The type of object to be cloned
/// The object to be cloned
/// A cloned instance
public static T Clone(T source)
{
return (T)Reparse(source.GetType(), source);
}
// Create a clone of an object (untyped)
///
/// Create a clone of an object by serializing to JSON and the deserializing into a new instance
///
/// The object to be cloned
/// A cloned instance
public static object Clone(object source)
{
return Reparse(source.GetType(), source);
}
///
/// Clone an object into another instance
///
/// The object to clone to
/// The object to clone from
public static void CloneInto(object dest, object source)
{
ReparseInto(dest, source);
}
///
/// Reparse an object by writing to a stream and re-reading (possibly
/// as a different type).
///
/// The type of object to deserialize as
/// The source object to be reparsed
/// The newly parsed object instance
public static object Reparse(Type type, object source)
{
if (source == null)
return null;
var ms = new MemoryStream();
try
{
// Write
var w = new StreamWriter(ms);
Json.Write(w, source);
w.Flush();
// Read
ms.Seek(0, SeekOrigin.Begin);
var r = new StreamReader(ms);
return Json.Parse(r, type);
}
finally
{
ms.Dispose();
}
}
///
/// Reparse an object by writing to a stream and re-reading (possibly
/// as a different type).
///
/// The type of object to deserialize as
/// The source object to be reparsed
/// The newly parsed object instance
public static T Reparse(object source)
{
return (T)Reparse(typeof(T), source);
}
///
/// Reparse one object into another object
///
/// The destination object
/// The source object
public static void ReparseInto(object dest, object source)
{
var ms = new MemoryStream();
try
{
// Write
var w = new StreamWriter(ms);
Json.Write(w, source);
w.Flush();
// Read
ms.Seek(0, SeekOrigin.Begin);
var r = new StreamReader(ms);
Json.ParseInto(r, dest);
}
finally
{
ms.Dispose();
}
}
///
/// Register a callback that can format a value of a particular type into json
///
/// The type of object to be formatted
/// The formatter callback
public static void RegisterFormatter(Type type, Action formatter)
{
JsonWriter._formatters.Set(type, formatter);
}
///
/// Register a callback to format the key values of dictionaries
///
///
/// These formatters are only used when writing .NET dictionary
/// key instances - not when writing properties names.
///
/// The type of object to be formatted
/// The formatter callback
public static void RegisterKeyFormatter(Type type, Func