Updated exceptron driver to 0.1.0.34
This commit is contained in:
parent
85914b5262
commit
fb74a1a6a7
|
@ -38,9 +38,8 @@
|
|||
<Reference Include="Exceptioneer.WindowsFormsClient">
|
||||
<HintPath>..\Libraries\Exceptioneer.WindowsFormsClient.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Exceptron.Driver, Version=0.1.0.30, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Exceptron.Driver.0.1.0.30\lib\net20\Exceptron.Driver.dll</HintPath>
|
||||
<Reference Include="Exceptron.Driver">
|
||||
<HintPath>..\packages\Exceptron.Driver.0.1.0.34\lib\net20\Exceptron.Driver.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.4.5.4\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||
|
|
|
@ -111,7 +111,7 @@ namespace NzbDrone.Common
|
|||
ApplicationVersion = new EnvironmentProvider().Version.ToString()
|
||||
};
|
||||
|
||||
ExceptronDriver.ThrowsExceptions = !EnvironmentProvider.IsProduction;
|
||||
ExceptronDriver.ClientConfiguration.ThrowsExceptions = !EnvironmentProvider.IsProduction;
|
||||
ExceptronDriver.Enviroment = EnvironmentProvider.IsProduction ? "Prod" : "Dev";
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Exceptron.Driver" version="0.1.0.30" />
|
||||
<package id="Exceptron.Driver" version="0.1.0.34" />
|
||||
<package id="Newtonsoft.Json" version="4.5.4" />
|
||||
<package id="NLog" version="2.0.0.2000" />
|
||||
</packages>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,24 @@
|
|||
using Exceptron.Driver.Message;
|
||||
|
||||
namespace Exceptron.Driver
|
||||
{
|
||||
public class ClientConfiguration
|
||||
{
|
||||
|
||||
public ClientConfiguration()
|
||||
{
|
||||
ServerUrl = "http://api.exceptron.com/v1a/";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If ExceptronClinet should throw exceptions in case of an error. Default: <see cref="bool.False"/>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Its recommended that this flag is set to True during development and <see cref="bool.False"/> in production systems.
|
||||
/// If an exception is thrown while this flag is set to <see cref="bool.False"/> the thrown exception will be returned in <see cref="ExceptionResponse.Exception"/>
|
||||
/// </remarks>
|
||||
public bool ThrowsExceptions { get; set; }
|
||||
|
||||
internal string ServerUrl { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,211 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using Exceptron.Driver.Message;
|
||||
|
||||
namespace Exceptron.Driver
|
||||
{
|
||||
public class ExceptionClient
|
||||
{
|
||||
private readonly string _apiKey;
|
||||
internal IRestClient RestClient { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Version of Driver
|
||||
/// </summary>
|
||||
public string DriverVersion
|
||||
{
|
||||
get { return Assembly.GetExecutingAssembly().GetName().Version.ToString(); }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Name of Driver
|
||||
/// </summary>
|
||||
public string DriverName
|
||||
{
|
||||
get { return "Official .NET"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Client Configuration
|
||||
/// </summary>
|
||||
public ClientConfiguration ClientConfiguration { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Environment that the application is running in
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// Dev, Staging, Production
|
||||
/// </example>
|
||||
public string Enviroment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Version of application executing. Default: Version of <see cref="Assembly.GetEntryAssembly()"/>
|
||||
/// </summary>
|
||||
public string ApplicationVersion { get; set; }
|
||||
|
||||
|
||||
|
||||
/// <param name="apiKey">Your Exceptron API Key</param>
|
||||
public ExceptionClient(string apiKey)
|
||||
: this(apiKey, new ClientConfiguration())
|
||||
{
|
||||
}
|
||||
|
||||
/// <param name="apiKey">Your Exceptron API Key</param>
|
||||
/// <param name="clientConfiguration">Configuration to use for this client </param>
|
||||
private ExceptionClient(string apiKey, ClientConfiguration clientConfiguration)
|
||||
{
|
||||
ClientConfiguration = clientConfiguration;
|
||||
_apiKey = apiKey;
|
||||
|
||||
RestClient = new RestClient();
|
||||
|
||||
SetApplicationVersion();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Submit an exception to Exceptron Servers.
|
||||
/// </summary>
|
||||
/// <param name="exceptionData">Exception data to be reported to the server</param>
|
||||
public ExceptionResponse SubmitException(ExceptionData exceptionData)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (exceptionData == null)
|
||||
throw new ArgumentNullException("exceptionData");
|
||||
|
||||
if (exceptionData.Exception == null)
|
||||
throw new ArgumentException("ExceptionData.Exception Cannot be null.", "exceptionData");
|
||||
|
||||
var report = new ExceptionReport();
|
||||
|
||||
report.ap = _apiKey;
|
||||
report.dn = DriverName;
|
||||
report.dv = DriverVersion;
|
||||
report.aver = ApplicationVersion;
|
||||
|
||||
report.ext = exceptionData.Exception.GetType().FullName;
|
||||
report.stk = ConvertToFrames(exceptionData.Exception);
|
||||
report.exm = exceptionData.Exception.Message;
|
||||
|
||||
report.cmp = exceptionData.Component;
|
||||
report.uid = exceptionData.UserId;
|
||||
report.env = Enviroment;
|
||||
report.msg = exceptionData.Message;
|
||||
report.cul = Thread.CurrentThread.CurrentCulture.Name;
|
||||
report.os = Environment.OSVersion.VersionString;
|
||||
report.sv = (int)exceptionData.Severity;
|
||||
|
||||
var response = RestClient.Put<ExceptionResponse>(ClientConfiguration.ServerUrl, report);
|
||||
|
||||
return response;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Trace.WriteLine("Unable to submit exception to exceptron. ", e.ToString());
|
||||
|
||||
if (ClientConfiguration.ThrowsExceptions)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ExceptionResponse { Exception = e };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void SetApplicationVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
var entryAssembly = GetWebEntryAssembly();
|
||||
|
||||
if (entryAssembly == null)
|
||||
{
|
||||
entryAssembly = Assembly.GetEntryAssembly();
|
||||
}
|
||||
|
||||
if (entryAssembly == null)
|
||||
{
|
||||
entryAssembly = Assembly.GetCallingAssembly();
|
||||
}
|
||||
|
||||
if (entryAssembly != null)
|
||||
{
|
||||
ApplicationVersion = entryAssembly.GetName().Version.ToString();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Trace.WriteLine("Can't figure out application version.", e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
static private Assembly GetWebEntryAssembly()
|
||||
{
|
||||
if (System.Web.HttpContext.Current == null ||
|
||||
System.Web.HttpContext.Current.ApplicationInstance == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var type = System.Web.HttpContext.Current.ApplicationInstance.GetType();
|
||||
while (type != null && type.Namespace == "ASP")
|
||||
{
|
||||
type = type.BaseType;
|
||||
}
|
||||
|
||||
return type == null ? null : type.Assembly;
|
||||
}
|
||||
|
||||
internal static List<Frame> ConvertToFrames(Exception exception)
|
||||
{
|
||||
if (exception == null) return null;
|
||||
|
||||
var stackTrace = new StackTrace(exception, true);
|
||||
|
||||
var frames = stackTrace.GetFrames();
|
||||
|
||||
if (frames == null) return null;
|
||||
|
||||
var result = new List<Frame>();
|
||||
|
||||
for (int index = 0; index < frames.Length; index++)
|
||||
{
|
||||
var frame = frames[index];
|
||||
var method = frame.GetMethod();
|
||||
var declaringType = method.DeclaringType;
|
||||
|
||||
var currentFrame = new Frame
|
||||
{
|
||||
i = index,
|
||||
fn = frame.GetFileName(),
|
||||
ln = frame.GetFileLineNumber(),
|
||||
m = method.ToString(),
|
||||
};
|
||||
|
||||
|
||||
currentFrame.m = currentFrame.m.Substring(currentFrame.m.IndexOf(' ')).Trim();
|
||||
|
||||
|
||||
if (declaringType != null)
|
||||
{
|
||||
currentFrame.c = declaringType.FullName;
|
||||
}
|
||||
|
||||
result.Add(currentFrame);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
|
||||
namespace Exceptron.Driver
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents information that will be used to construct an exception report.
|
||||
/// </summary>
|
||||
public class ExceptionData
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception that is being reported
|
||||
/// </summary>
|
||||
public Exception Exception { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Component that caused this error.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It is common to use the logger name that was used to log the exception as the component.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// DataAccess, Configuration, Registration, etc.
|
||||
/// </example>
|
||||
public string Component { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ID that will uniquely identify the user
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This Id does not have to be tied to the user's identity.
|
||||
/// You can use a system generated unique ID such as GUID.
|
||||
/// This field is used to report how many unique users are experiencing an error.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// "62E5C8EF-0CA2-43AB-B278-FC6994F776ED"
|
||||
/// "Timmy@aol.com"
|
||||
/// "26437"
|
||||
/// </example>
|
||||
public string UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Any message that should be attached to this exceptions
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// Something went wrong while checking for application updates.
|
||||
/// </example>
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Severity of the exception being reported
|
||||
/// </summary>
|
||||
public ExceptionSeverity Severity { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
namespace Exceptron.Driver
|
||||
{
|
||||
/// <summary>
|
||||
/// Severity of the exception being reported
|
||||
/// </summary>
|
||||
public enum ExceptionSeverity
|
||||
{
|
||||
/// <summary>
|
||||
/// Excepted Error. Can be ignored
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Error that can be handled gracefully
|
||||
/// </summary>
|
||||
Warning = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Blocking user from completing their intended action
|
||||
/// </summary>
|
||||
Error = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Will most likely cause the application to crash
|
||||
/// </summary>
|
||||
Fatal = 3
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace Exceptron.Driver
|
||||
{
|
||||
internal interface IRestClient
|
||||
{
|
||||
TResponse Put<TResponse>(string url, object report) where TResponse : class, new();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Exceptron.Driver.Message
|
||||
{
|
||||
internal class ExceptionReport
|
||||
{
|
||||
/// <summary>
|
||||
/// API key
|
||||
/// </summary>
|
||||
public string ap { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Application Version
|
||||
/// </summary>
|
||||
public string aver { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Exception Severity
|
||||
/// </summary>
|
||||
public int sv { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User or Instance ID
|
||||
/// </summary>
|
||||
public string uid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of exception
|
||||
/// </summary>
|
||||
public string ext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Exception message
|
||||
/// </summary>
|
||||
public string exm { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of frames that make up the StackTrace of the exception
|
||||
/// </summary>
|
||||
public List<Frame> stk { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Component that experienced this exception
|
||||
/// </summary>
|
||||
public string cmp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Environment that this exception occurred in.
|
||||
/// </summary>
|
||||
/// <example>Dev, Stage, Production</example>
|
||||
public string env { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Message that was logged along with the exception.
|
||||
/// </summary>
|
||||
public string msg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User's culture in
|
||||
/// </summary>
|
||||
/// <remarks>http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.name.aspx</remarks>
|
||||
public string cul { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// OS Version
|
||||
/// </summary>
|
||||
public string os { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the driver that generated and is sending this message
|
||||
/// </summary>
|
||||
public string dn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Version of the driver that generated and is sending this message
|
||||
/// </summary>
|
||||
public string dv { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
|
||||
namespace Exceptron.Driver.Message
|
||||
{
|
||||
public class ExceptionResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception report reference ID. This ID will be shared across
|
||||
/// similar exceptions
|
||||
/// </summary>
|
||||
public string RefId { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Was the report successfully processed on the server
|
||||
/// </summary>
|
||||
public bool Successful
|
||||
{
|
||||
get
|
||||
{
|
||||
return !string.IsNullOrEmpty(RefId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception that caused the message to fail.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This property will only be populated if <see cref="ClientConfiguration.ThrowsExceptions"/> is set to <see cref="bool.False"/>/>
|
||||
/// Exception is thrown if <see cref="ClientConfiguration.ThrowsExceptions"/> is set to <see cref="bool.True"/>.
|
||||
/// </remarks>
|
||||
public Exception Exception { get; internal set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
namespace Exceptron.Driver.Message
|
||||
{
|
||||
internal class Frame
|
||||
{
|
||||
/// <summary>
|
||||
/// Order of current frame
|
||||
/// </summary>
|
||||
public int i { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Line number of the current frame
|
||||
/// </summary>
|
||||
public int ln { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// File name of the current frame
|
||||
/// </summary>
|
||||
public string fn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Method name for current frame
|
||||
/// </summary>
|
||||
public string m { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Class name for current frame
|
||||
/// </summary>
|
||||
public string c { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Exceptron.Driver")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Exceptron.Driver")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2012")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("a463887e-594f-4733-b227-a79f4ffb2158")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("0.1.0.34")]
|
||||
[assembly: AssemblyFileVersion("0.1.0.34")]
|
||||
[assembly: InternalsVisibleTo("Exceptron.Driver.Tests")]
|
||||
[assembly: InternalsVisibleTo("Exceptron.Api.v1a.Tests")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
|
@ -0,0 +1,79 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using Exceptron.Driver.fastJSON;
|
||||
|
||||
namespace Exceptron.Driver
|
||||
{
|
||||
public sealed class RestClient : IRestClient
|
||||
{
|
||||
public TResponse Put<TResponse>(string url, object content) where TResponse : class ,new()
|
||||
{
|
||||
|
||||
if(content == null)
|
||||
throw new ArgumentNullException("content can not be null", "content");
|
||||
|
||||
if (string.IsNullOrEmpty(url))
|
||||
throw new ArgumentNullException("url can not be null or empty", "url");
|
||||
|
||||
Trace.WriteLine("Attempting PUT to " + url);
|
||||
|
||||
var json = JSON.Instance.ToJSON(content);
|
||||
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(json);
|
||||
var request = (HttpWebRequest)WebRequest.Create(url);
|
||||
request.Timeout = 10000;
|
||||
request.Method = "PUT";
|
||||
request.ContentType = "application/json";
|
||||
request.ContentLength = bytes.Length;
|
||||
request.Accept = "application/json";
|
||||
|
||||
var dataStream = request.GetRequestStream();
|
||||
dataStream.Write(bytes, 0, bytes.Length);
|
||||
dataStream.Close();
|
||||
var webResponse = request.GetResponse();
|
||||
|
||||
var responseStream = new StreamReader(webResponse.GetResponseStream(), Encoding.GetEncoding(1252));
|
||||
var responseString = responseStream.ReadToEnd();
|
||||
|
||||
Trace.WriteLine(responseString);
|
||||
var response = JSON.Instance.ToObject<TResponse>(responseString);
|
||||
|
||||
return response;
|
||||
|
||||
/* try
|
||||
{
|
||||
var dataStream = request.GetRequestStream();
|
||||
dataStream.Write(bytes, 0, bytes.Length);
|
||||
dataStream.Close();
|
||||
webResponse = request.GetResponse();
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
Trace.WriteLine("An Error has occurred while Doing HTTP PUT. " + ex);
|
||||
webResponse = ex.Response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine("An Error has occurred while Doing HTTP PUT. " + ex);
|
||||
}
|
||||
|
||||
var response = new TResponse();
|
||||
|
||||
if (webResponse != null && webResponse.ContentType.Contains("json"))
|
||||
{
|
||||
var responseStream = new StreamReader(webResponse.GetResponseStream(), Encoding.GetEncoding(1252));
|
||||
var responseString = responseStream.ReadToEnd();
|
||||
|
||||
Trace.WriteLine(responseString);
|
||||
response = JSON.Instance.ToObject<TResponse>(responseString);
|
||||
}
|
||||
|
||||
if (response == null) response = new TResponse();
|
||||
|
||||
return response;*/
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace Exceptron.Driver
|
||||
{
|
||||
public class ValidationError
|
||||
{
|
||||
public string ErrorCode { get; set; }
|
||||
|
||||
public string FieldName { get; set; }
|
||||
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
//http://fastjson.codeplex.com/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Exceptron.Driver.fastJSON
|
||||
{
|
||||
internal class Getters
|
||||
{
|
||||
public string Name;
|
||||
public JSON.GenericGetter Getter;
|
||||
public Type propertyType;
|
||||
}
|
||||
|
||||
internal class DatasetSchema
|
||||
{
|
||||
public List<string> Info { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,854 @@
|
|||
//http://fastjson.codeplex.com/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using Exceptron.Driver.fastJSON;
|
||||
|
||||
namespace Exceptron.Driver.fastJSON
|
||||
{
|
||||
internal delegate string Serialize(object data);
|
||||
internal delegate object Deserialize(string data);
|
||||
|
||||
internal class JSON
|
||||
{
|
||||
public readonly static JSON Instance = new JSON();
|
||||
|
||||
private JSON()
|
||||
{
|
||||
UseSerializerExtension = false;
|
||||
SerializeNullValues = false;
|
||||
UseOptimizedDatasetSchema = false;
|
||||
UsingGlobalTypes = false;
|
||||
}
|
||||
public bool UseOptimizedDatasetSchema = true;
|
||||
public bool UseFastGuid = true;
|
||||
public bool UseSerializerExtension = true;
|
||||
public bool IndentOutput = false;
|
||||
public bool SerializeNullValues = true;
|
||||
public bool UseUTCDateTime = false;
|
||||
public bool ShowReadOnlyProperties = false;
|
||||
public bool UsingGlobalTypes = true;
|
||||
|
||||
public string ToJSON(object obj)
|
||||
{
|
||||
return ToJSON(obj, UseSerializerExtension, UseFastGuid, UseOptimizedDatasetSchema, SerializeNullValues);
|
||||
}
|
||||
|
||||
public string ToJSON(object obj,
|
||||
bool enableSerializerExtensions)
|
||||
{
|
||||
return ToJSON(obj, enableSerializerExtensions, UseFastGuid, UseOptimizedDatasetSchema, SerializeNullValues);
|
||||
}
|
||||
|
||||
public string ToJSON(object obj,
|
||||
bool enableSerializerExtensions,
|
||||
bool enableFastGuid)
|
||||
{
|
||||
return ToJSON(obj, enableSerializerExtensions, enableFastGuid, UseOptimizedDatasetSchema, SerializeNullValues);
|
||||
}
|
||||
|
||||
public string ToJSON(object obj,
|
||||
bool enableSerializerExtensions,
|
||||
bool enableFastGuid,
|
||||
bool enableOptimizedDatasetSchema,
|
||||
bool serializeNullValues)
|
||||
{
|
||||
return new JSONSerializer(enableOptimizedDatasetSchema, enableFastGuid, enableSerializerExtensions, serializeNullValues, IndentOutput).ConvertToJSON(obj);
|
||||
}
|
||||
|
||||
public object Parse(string json)
|
||||
{
|
||||
return new JsonParser(json).Decode();
|
||||
}
|
||||
|
||||
public T ToObject<T>(string json)
|
||||
{
|
||||
return (T)ToObject(json, typeof(T));
|
||||
}
|
||||
|
||||
public object ToObject(string json)
|
||||
{
|
||||
return ToObject(json, null);
|
||||
}
|
||||
|
||||
public object ToObject(string json, Type type)
|
||||
{
|
||||
Dictionary<string, object> ht = new JsonParser(json).Decode() as Dictionary<string, object>;
|
||||
if (ht == null) return null;
|
||||
return ParseDictionary(ht, null, type);
|
||||
}
|
||||
|
||||
#if CUSTOMTYPE
|
||||
internal SafeDictionary<Type, Serialize> _customSerializer = new SafeDictionary<Type, Serialize>();
|
||||
internal SafeDictionary<Type, Deserialize> _customDeserializer = new SafeDictionary<Type, Deserialize>();
|
||||
|
||||
public void RegisterCustomType(Type type, Serialize serializer, Deserialize deserializer)
|
||||
{
|
||||
if (type != null && serializer != null && deserializer != null)
|
||||
{
|
||||
_customSerializer.Add(type, serializer);
|
||||
_customDeserializer.Add(type, deserializer);
|
||||
// reset property cache
|
||||
_propertycache = new SafeDictionary<string, SafeDictionary<string, myPropInfo>>();
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsTypeRegistered(Type t)
|
||||
{
|
||||
Serialize s;
|
||||
return _customSerializer.TryGetValue(t, out s);
|
||||
}
|
||||
#endif
|
||||
|
||||
#region [ PROPERTY GET SET CACHE ]
|
||||
SafeDictionary<Type, string> _tyname = new SafeDictionary<Type, string>();
|
||||
internal string GetTypeAssemblyName(Type t)
|
||||
{
|
||||
string val = "";
|
||||
if (_tyname.TryGetValue(t, out val))
|
||||
return val;
|
||||
else
|
||||
{
|
||||
string s = t.AssemblyQualifiedName;
|
||||
_tyname.Add(t, s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
SafeDictionary<string, Type> _typecache = new SafeDictionary<string, Type>();
|
||||
private Type GetTypeFromCache(string typename)
|
||||
{
|
||||
Type val = null;
|
||||
if (_typecache.TryGetValue(typename, out val))
|
||||
return val;
|
||||
else
|
||||
{
|
||||
Type t = Type.GetType(typename);
|
||||
_typecache.Add(typename, t);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
SafeDictionary<Type, CreateObject> _constrcache = new SafeDictionary<Type, CreateObject>();
|
||||
private delegate object CreateObject();
|
||||
private object FastCreateInstance(Type objtype)
|
||||
{
|
||||
try
|
||||
{
|
||||
CreateObject c = null;
|
||||
if (_constrcache.TryGetValue(objtype, out c))
|
||||
{
|
||||
return c();
|
||||
}
|
||||
else
|
||||
{
|
||||
DynamicMethod dynMethod = new DynamicMethod("_", objtype, null, true);
|
||||
ILGenerator ilGen = dynMethod.GetILGenerator();
|
||||
|
||||
ilGen.Emit(OpCodes.Newobj, objtype.GetConstructor(Type.EmptyTypes));
|
||||
ilGen.Emit(OpCodes.Ret);
|
||||
c = (CreateObject)dynMethod.CreateDelegate(typeof(CreateObject));
|
||||
_constrcache.Add(objtype, c);
|
||||
return c();
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
throw new Exception(string.Format("Failed to fast create instance for type '{0}' from assemebly '{1}'",
|
||||
objtype.FullName, objtype.AssemblyQualifiedName), exc);
|
||||
}
|
||||
}
|
||||
|
||||
private struct myPropInfo
|
||||
{
|
||||
public bool filled;
|
||||
public Type pt;
|
||||
public Type bt;
|
||||
public Type changeType;
|
||||
public bool isDictionary;
|
||||
public bool isValueType;
|
||||
public bool isGenericType;
|
||||
public bool isArray;
|
||||
public bool isByteArray;
|
||||
public bool isGuid;
|
||||
#if !SILVERLIGHT
|
||||
public bool isDataSet;
|
||||
public bool isDataTable;
|
||||
public bool isHashtable;
|
||||
#endif
|
||||
public GenericSetter setter;
|
||||
public bool isEnum;
|
||||
public bool isDateTime;
|
||||
public Type[] GenericTypes;
|
||||
public bool isInt;
|
||||
public bool isLong;
|
||||
public bool isString;
|
||||
public bool isBool;
|
||||
public bool isClass;
|
||||
public GenericGetter getter;
|
||||
public bool isStringDictionary;
|
||||
public string Name;
|
||||
#if CUSTOMTYPE
|
||||
public bool isCustomType;
|
||||
#endif
|
||||
public bool CanWrite;
|
||||
}
|
||||
|
||||
SafeDictionary<string, SafeDictionary<string, myPropInfo>> _propertycache = new SafeDictionary<string, SafeDictionary<string, myPropInfo>>();
|
||||
private SafeDictionary<string, myPropInfo> Getproperties(Type type, string typename)
|
||||
{
|
||||
SafeDictionary<string, myPropInfo> sd = null;
|
||||
if (_propertycache.TryGetValue(typename, out sd))
|
||||
{
|
||||
return sd;
|
||||
}
|
||||
else
|
||||
{
|
||||
sd = new SafeDictionary<string, myPropInfo>();
|
||||
PropertyInfo[] pr = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
||||
foreach (PropertyInfo p in pr)
|
||||
{
|
||||
myPropInfo d = CreateMyProp(p.PropertyType, p.Name);
|
||||
d.CanWrite = p.CanWrite;
|
||||
d.setter = CreateSetMethod(p);
|
||||
d.getter = CreateGetMethod(p);
|
||||
sd.Add(p.Name, d);
|
||||
}
|
||||
|
||||
_propertycache.Add(typename, sd);
|
||||
return sd;
|
||||
}
|
||||
}
|
||||
|
||||
private myPropInfo CreateMyProp(Type t, string name)
|
||||
{
|
||||
myPropInfo d = new myPropInfo();
|
||||
d.filled = true;
|
||||
d.CanWrite = true;
|
||||
d.pt = t;
|
||||
d.Name = name;
|
||||
d.isDictionary = t.Name.Contains("Dictionary");
|
||||
if (d.isDictionary)
|
||||
d.GenericTypes = t.GetGenericArguments();
|
||||
d.isValueType = t.IsValueType;
|
||||
d.isGenericType = t.IsGenericType;
|
||||
d.isArray = t.IsArray;
|
||||
if (d.isArray)
|
||||
d.bt = t.GetElementType();
|
||||
if (d.isGenericType)
|
||||
d.bt = t.GetGenericArguments()[0];
|
||||
d.isByteArray = t == typeof(byte[]);
|
||||
d.isGuid = (t == typeof(Guid) || t == typeof(Guid?));
|
||||
#if !SILVERLIGHT
|
||||
d.isHashtable = t == typeof(Hashtable);
|
||||
d.isDataSet = t == typeof(DataSet);
|
||||
d.isDataTable = t == typeof(DataTable);
|
||||
#endif
|
||||
|
||||
d.changeType = GetChangeType(t);
|
||||
d.isEnum = t.IsEnum;
|
||||
d.isDateTime = t == typeof(DateTime) || t == typeof(DateTime?);
|
||||
d.isInt = t == typeof(int) || t == typeof(int?);
|
||||
d.isLong = t == typeof(long) || t == typeof(long?);
|
||||
d.isString = t == typeof(string);
|
||||
d.isBool = t == typeof(bool) || t == typeof(bool?);
|
||||
d.isClass = t.IsClass;
|
||||
|
||||
if (d.isDictionary && d.GenericTypes.Length > 0 && d.GenericTypes[0] == typeof(string))
|
||||
d.isStringDictionary = true;
|
||||
|
||||
#if CUSTOMTYPE
|
||||
if (IsTypeRegistered(t))
|
||||
d.isCustomType = true;
|
||||
#endif
|
||||
return d;
|
||||
}
|
||||
|
||||
private delegate void GenericSetter(object target, object value);
|
||||
|
||||
private static GenericSetter CreateSetMethod(PropertyInfo propertyInfo)
|
||||
{
|
||||
MethodInfo setMethod = propertyInfo.GetSetMethod(nonPublic: true);
|
||||
if (setMethod == null)
|
||||
return null;
|
||||
|
||||
Type[] arguments = new Type[2];
|
||||
arguments[0] = arguments[1] = typeof(object);
|
||||
|
||||
DynamicMethod setter = new DynamicMethod("_", typeof(void), arguments, true);
|
||||
ILGenerator il = setter.GetILGenerator();
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
|
||||
if (propertyInfo.PropertyType.IsClass)
|
||||
il.Emit(OpCodes.Castclass, propertyInfo.PropertyType);
|
||||
else
|
||||
il.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
|
||||
|
||||
il.EmitCall(OpCodes.Callvirt, setMethod, null);
|
||||
il.Emit(OpCodes.Ret);
|
||||
|
||||
return (GenericSetter)setter.CreateDelegate(typeof(GenericSetter));
|
||||
}
|
||||
|
||||
internal delegate object GenericGetter(object obj);
|
||||
|
||||
|
||||
private GenericGetter CreateGetMethod(PropertyInfo propertyInfo)
|
||||
{
|
||||
MethodInfo getMethod = propertyInfo.GetGetMethod();
|
||||
if (getMethod == null)
|
||||
return null;
|
||||
|
||||
Type[] arguments = new Type[1];
|
||||
arguments[0] = typeof(object);
|
||||
|
||||
DynamicMethod getter = new DynamicMethod("_", typeof(object), arguments, true);
|
||||
ILGenerator il = getter.GetILGenerator();
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
|
||||
il.EmitCall(OpCodes.Callvirt, getMethod, null);
|
||||
|
||||
if (!propertyInfo.PropertyType.IsClass)
|
||||
il.Emit(OpCodes.Box, propertyInfo.PropertyType);
|
||||
|
||||
il.Emit(OpCodes.Ret);
|
||||
|
||||
return (GenericGetter)getter.CreateDelegate(typeof(GenericGetter));
|
||||
}
|
||||
|
||||
readonly SafeDictionary<Type, List<Getters>> _getterscache = new SafeDictionary<Type, List<Getters>>();
|
||||
internal List<Getters> GetGetters(Type type)
|
||||
{
|
||||
List<Getters> val = null;
|
||||
if (_getterscache.TryGetValue(type, out val))
|
||||
return val;
|
||||
|
||||
PropertyInfo[] props = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
||||
List<Getters> getters = new List<Getters>();
|
||||
foreach (PropertyInfo p in props)
|
||||
{
|
||||
if (!p.CanWrite && ShowReadOnlyProperties == false) continue;
|
||||
|
||||
object[] att = p.GetCustomAttributes(typeof(System.Xml.Serialization.XmlIgnoreAttribute), false);
|
||||
if (att != null && att.Length > 0)
|
||||
continue;
|
||||
|
||||
JSON.GenericGetter g = CreateGetMethod(p);
|
||||
if (g != null)
|
||||
{
|
||||
Getters gg = new Getters();
|
||||
gg.Name = p.Name;
|
||||
gg.Getter = g;
|
||||
gg.propertyType = p.PropertyType;
|
||||
getters.Add(gg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_getterscache.Add(type, getters);
|
||||
return getters;
|
||||
}
|
||||
|
||||
private object ChangeType(object value, Type conversionType)
|
||||
{
|
||||
if (conversionType == typeof(int))
|
||||
return (int)CreateLong((string)value);
|
||||
|
||||
else if (conversionType == typeof(long))
|
||||
return CreateLong((string)value);
|
||||
|
||||
else if (conversionType == typeof(string))
|
||||
return (string)value;
|
||||
|
||||
else if (conversionType == typeof(Guid))
|
||||
return CreateGuid((string)value);
|
||||
|
||||
else if (conversionType.IsEnum)
|
||||
return CreateEnum(conversionType, (string)value);
|
||||
|
||||
return Convert.ChangeType(value, conversionType, CultureInfo.InvariantCulture);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
private object ParseDictionary(Dictionary<string, object> d, Dictionary<string, object> globaltypes, Type type)
|
||||
{
|
||||
object tn = "";
|
||||
if (d.TryGetValue("$types", out tn))
|
||||
{
|
||||
UsingGlobalTypes = true;
|
||||
globaltypes = new Dictionary<string, object>();
|
||||
foreach (var kv in (Dictionary<string, object>)tn)
|
||||
{
|
||||
globaltypes.Add((string)kv.Value, kv.Key);
|
||||
}
|
||||
}
|
||||
|
||||
bool found = d.TryGetValue("$type", out tn);
|
||||
#if !SILVERLIGHT
|
||||
if (found == false && type == typeof(System.Object))
|
||||
{
|
||||
return CreateDataset(d, globaltypes);
|
||||
}
|
||||
#endif
|
||||
if (found)
|
||||
{
|
||||
if (UsingGlobalTypes)
|
||||
{
|
||||
object tname = "";
|
||||
if (globaltypes.TryGetValue((string)tn, out tname))
|
||||
tn = tname;
|
||||
}
|
||||
type = GetTypeFromCache((string)tn);
|
||||
}
|
||||
|
||||
if (type == null)
|
||||
throw new Exception("Cannot determine type");
|
||||
|
||||
string typename = type.FullName;
|
||||
object o = FastCreateInstance(type);
|
||||
SafeDictionary<string, myPropInfo> props = Getproperties(type, typename);
|
||||
foreach (string name in d.Keys)
|
||||
{
|
||||
if (name == "$map")
|
||||
{
|
||||
ProcessMap(o, props, (Dictionary<string, object>)d[name]);
|
||||
continue;
|
||||
}
|
||||
myPropInfo pi;
|
||||
if (props.TryGetValue(name, out pi) == false)
|
||||
continue;
|
||||
if (pi.filled == true)
|
||||
{
|
||||
object v = d[name];
|
||||
|
||||
if (v != null)
|
||||
{
|
||||
object oset = null;
|
||||
|
||||
if (pi.isInt)
|
||||
oset = (int)CreateLong((string)v);
|
||||
#if CUSTOMTYPE
|
||||
else if (pi.isCustomType)
|
||||
oset = CreateCustom((string)v, pi.pt);
|
||||
#endif
|
||||
else if (pi.isLong)
|
||||
oset = CreateLong((string)v);
|
||||
|
||||
else if (pi.isString)
|
||||
oset = (string)v;
|
||||
|
||||
else if (pi.isBool)
|
||||
oset = (bool)v;
|
||||
|
||||
else if (pi.isGenericType && pi.isValueType == false && pi.isDictionary == false)
|
||||
#if SILVERLIGHT
|
||||
oset = CreateGenericList((List<object>)v, pi.pt, pi.bt, globaltypes);
|
||||
#else
|
||||
oset = CreateGenericList((ArrayList)v, pi.pt, pi.bt, globaltypes);
|
||||
#endif
|
||||
else if (pi.isByteArray)
|
||||
oset = Convert.FromBase64String((string)v);
|
||||
|
||||
else if (pi.isArray && pi.isValueType == false)
|
||||
#if SILVERLIGHT
|
||||
oset = CreateArray((List<object>)v, pi.pt, pi.bt, globaltypes);
|
||||
#else
|
||||
oset = CreateArray((ArrayList)v, pi.pt, pi.bt, globaltypes);
|
||||
#endif
|
||||
else if (pi.isGuid)
|
||||
oset = CreateGuid((string)v);
|
||||
#if !SILVERLIGHT
|
||||
else if (pi.isDataSet)
|
||||
oset = CreateDataset((Dictionary<string, object>)v, globaltypes);
|
||||
|
||||
else if (pi.isDataTable)
|
||||
oset = this.CreateDataTable((Dictionary<string, object>)v, globaltypes);
|
||||
#endif
|
||||
|
||||
else if (pi.isStringDictionary)
|
||||
oset = CreateStringKeyDictionary((Dictionary<string, object>)v, pi.pt, pi.GenericTypes, globaltypes);
|
||||
|
||||
#if !SILVERLIGHT
|
||||
else if (pi.isDictionary || pi.isHashtable)
|
||||
oset = CreateDictionary((ArrayList)v, pi.pt, pi.GenericTypes, globaltypes);
|
||||
#else
|
||||
else if (pi.isDictionary)
|
||||
oset = CreateDictionary((List<object>)v, pi.pt, pi.GenericTypes, globaltypes);
|
||||
#endif
|
||||
|
||||
else if (pi.isEnum)
|
||||
oset = CreateEnum(pi.pt, (string)v);
|
||||
|
||||
else if (pi.isDateTime)
|
||||
oset = CreateDateTime((string)v);
|
||||
|
||||
else if (pi.isClass && v is Dictionary<string, object>)
|
||||
oset = ParseDictionary((Dictionary<string, object>)v, globaltypes, pi.pt);
|
||||
|
||||
else if (pi.isValueType)
|
||||
oset = ChangeType(v, pi.changeType);
|
||||
|
||||
#if SILVERLIGHT
|
||||
else if (v is List<object>)
|
||||
oset = CreateArray((List<object>)v, pi.pt, typeof(object), globaltypes);
|
||||
#else
|
||||
else if (v is ArrayList)
|
||||
oset = CreateArray((ArrayList)v, pi.pt, typeof(object), globaltypes);
|
||||
#endif
|
||||
else
|
||||
oset = v;
|
||||
|
||||
if (pi.CanWrite)
|
||||
pi.setter(o, oset);
|
||||
}
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
#if CUSTOMTYPE
|
||||
private object CreateCustom(string v, Type type)
|
||||
{
|
||||
Deserialize d;
|
||||
_customDeserializer.TryGetValue(type, out d);
|
||||
return d(v);
|
||||
}
|
||||
#endif
|
||||
|
||||
private void ProcessMap(object obj, SafeDictionary<string, JSON.myPropInfo> props, Dictionary<string, object> dic)
|
||||
{
|
||||
foreach (KeyValuePair<string, object> kv in dic)
|
||||
{
|
||||
myPropInfo p = props[kv.Key];
|
||||
object o = p.getter(obj);
|
||||
Type t = Type.GetType((string)kv.Value);
|
||||
if (t == typeof(Guid))
|
||||
p.setter(obj, CreateGuid((string)o));
|
||||
}
|
||||
}
|
||||
|
||||
private long CreateLong(string s)
|
||||
{
|
||||
long num = 0;
|
||||
bool neg = false;
|
||||
foreach (char cc in s)
|
||||
{
|
||||
if (cc == '-')
|
||||
neg = true;
|
||||
else if (cc == '+')
|
||||
neg = false;
|
||||
else
|
||||
{
|
||||
num *= 10;
|
||||
num += (int)(cc - '0');
|
||||
}
|
||||
}
|
||||
|
||||
return neg ? -num : num;
|
||||
}
|
||||
|
||||
private object CreateEnum(Type pt, string v)
|
||||
{
|
||||
// TODO : optimize create enum
|
||||
#if !SILVERLIGHT
|
||||
return Enum.Parse(pt, v);
|
||||
#else
|
||||
return Enum.Parse(pt, v, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
private Guid CreateGuid(string s)
|
||||
{
|
||||
if (s.Length > 30)
|
||||
return new Guid(s);
|
||||
else
|
||||
return new Guid(Convert.FromBase64String(s));
|
||||
}
|
||||
|
||||
private DateTime CreateDateTime(string value)
|
||||
{
|
||||
bool utc = false;
|
||||
// 0123456789012345678
|
||||
// datetime format = yyyy-MM-dd HH:mm:ss
|
||||
int year = (int)CreateLong(value.Substring(0, 4));
|
||||
int month = (int)CreateLong(value.Substring(5, 2));
|
||||
int day = (int)CreateLong(value.Substring(8, 2));
|
||||
int hour = (int)CreateLong(value.Substring(11, 2));
|
||||
int min = (int)CreateLong(value.Substring(14, 2));
|
||||
int sec = (int)CreateLong(value.Substring(17, 2));
|
||||
|
||||
if (value.EndsWith("Z"))
|
||||
utc = true;
|
||||
|
||||
if (UseUTCDateTime == false && utc == false)
|
||||
return new DateTime(year, month, day, hour, min, sec);
|
||||
else
|
||||
return new DateTime(year, month, day, hour, min, sec, DateTimeKind.Utc).ToLocalTime();
|
||||
}
|
||||
|
||||
#if SILVERLIGHT
|
||||
private object CreateArray(List<object> data, Type pt, Type bt, Dictionary<string, object> globalTypes)
|
||||
{
|
||||
Array col = Array.CreateInstance(bt, data.Count);
|
||||
// create an array of objects
|
||||
for (int i = 0; i < data.Count; i++)// each (object ob in data)
|
||||
{
|
||||
object ob = data[i];
|
||||
if (ob is IDictionary)
|
||||
col.SetValue(ParseDictionary((Dictionary<string, object>)ob, globalTypes, bt), i);
|
||||
else
|
||||
col.SetValue(ChangeType(ob, bt), i);
|
||||
}
|
||||
|
||||
return col;
|
||||
}
|
||||
#else
|
||||
private object CreateArray(ArrayList data, Type pt, Type bt, Dictionary<string, object> globalTypes)
|
||||
{
|
||||
ArrayList col = new ArrayList();
|
||||
// create an array of objects
|
||||
foreach (object ob in data)
|
||||
{
|
||||
if (ob is IDictionary)
|
||||
col.Add(ParseDictionary((Dictionary<string, object>)ob, globalTypes, bt));
|
||||
else
|
||||
col.Add(ChangeType(ob, bt));
|
||||
}
|
||||
return col.ToArray(bt);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if SILVERLIGHT
|
||||
private object CreateGenericList(List<object> data, Type pt, Type bt, Dictionary<string, object> globalTypes)
|
||||
#else
|
||||
private object CreateGenericList(ArrayList data, Type pt, Type bt, Dictionary<string, object> globalTypes)
|
||||
#endif
|
||||
{
|
||||
IList col = (IList)FastCreateInstance(pt);
|
||||
// create an array of objects
|
||||
foreach (object ob in data)
|
||||
{
|
||||
if (ob is IDictionary)
|
||||
col.Add(ParseDictionary((Dictionary<string, object>)ob, globalTypes, bt));
|
||||
#if SILVERLIGHT
|
||||
else if (ob is List<object>)
|
||||
col.Add(((List<object>)ob).ToArray());
|
||||
#else
|
||||
else if (ob is ArrayList)
|
||||
col.Add(((ArrayList)ob).ToArray());
|
||||
#endif
|
||||
else
|
||||
col.Add(ChangeType(ob, bt));
|
||||
}
|
||||
return col;
|
||||
}
|
||||
|
||||
private object CreateStringKeyDictionary(Dictionary<string, object> reader, Type pt, Type[] types, Dictionary<string, object> globalTypes)
|
||||
{
|
||||
var col = (IDictionary)FastCreateInstance(pt);
|
||||
Type t1 = null;
|
||||
Type t2 = null;
|
||||
if (types != null)
|
||||
{
|
||||
t1 = types[0];
|
||||
t2 = types[1];
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, object> values in reader)
|
||||
{
|
||||
var key = values.Key;//ChangeType(values.Key, t1);
|
||||
object val = null;
|
||||
if (values.Value is Dictionary<string, object>)
|
||||
val = ParseDictionary((Dictionary<string, object>)values.Value, globalTypes, t2);
|
||||
else
|
||||
val = ChangeType(values.Value, t2);
|
||||
col.Add(key, val);
|
||||
}
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
#if SILVERLIGHT
|
||||
private object CreateDictionary(List<object> reader, Type pt, Type[] types, Dictionary<string, object> globalTypes)
|
||||
#else
|
||||
private object CreateDictionary(ArrayList reader, Type pt, Type[] types, Dictionary<string, object> globalTypes)
|
||||
#endif
|
||||
{
|
||||
IDictionary col = (IDictionary)FastCreateInstance(pt);
|
||||
Type t1 = null;
|
||||
Type t2 = null;
|
||||
if (types != null)
|
||||
{
|
||||
t1 = types[0];
|
||||
t2 = types[1];
|
||||
}
|
||||
|
||||
foreach (Dictionary<string, object> values in reader)
|
||||
{
|
||||
object key = values["k"];
|
||||
object val = values["v"];
|
||||
|
||||
if (key is Dictionary<string, object>)
|
||||
key = ParseDictionary((Dictionary<string, object>)key, globalTypes, t1);
|
||||
else
|
||||
key = ChangeType(key, t1);
|
||||
|
||||
if (val is Dictionary<string, object>)
|
||||
val = ParseDictionary((Dictionary<string, object>)val, globalTypes, t2);
|
||||
else
|
||||
val = ChangeType(val, t2);
|
||||
|
||||
col.Add(key, val);
|
||||
}
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
private Type GetChangeType(Type conversionType)
|
||||
{
|
||||
if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
|
||||
return conversionType.GetGenericArguments()[0];
|
||||
|
||||
return conversionType;
|
||||
}
|
||||
#if !SILVERLIGHT
|
||||
private DataSet CreateDataset(Dictionary<string, object> reader, Dictionary<string, object> globalTypes)
|
||||
{
|
||||
DataSet ds = new DataSet();
|
||||
ds.EnforceConstraints = false;
|
||||
ds.BeginInit();
|
||||
|
||||
// read dataset schema here
|
||||
ReadSchema(reader, ds, globalTypes);
|
||||
|
||||
foreach (KeyValuePair<string, object> pair in reader)
|
||||
{
|
||||
if (pair.Key == "$type" || pair.Key == "$schema") continue;
|
||||
|
||||
ArrayList rows = (ArrayList)pair.Value;
|
||||
if (rows == null) continue;
|
||||
|
||||
DataTable dt = ds.Tables[pair.Key];
|
||||
ReadDataTable(rows, dt);
|
||||
}
|
||||
|
||||
ds.EndInit();
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
private void ReadSchema(Dictionary<string, object> reader, DataSet ds, Dictionary<string, object> globalTypes)
|
||||
{
|
||||
var schema = reader["$schema"];
|
||||
|
||||
if (schema is string)
|
||||
{
|
||||
TextReader tr = new StringReader((string)schema);
|
||||
ds.ReadXmlSchema(tr);
|
||||
}
|
||||
else
|
||||
{
|
||||
DatasetSchema ms = (DatasetSchema)ParseDictionary((Dictionary<string, object>)schema, globalTypes, typeof(DatasetSchema));
|
||||
ds.DataSetName = ms.Name;
|
||||
for (int i = 0; i < ms.Info.Count; i += 3)
|
||||
{
|
||||
if (ds.Tables.Contains(ms.Info[i]) == false)
|
||||
ds.Tables.Add(ms.Info[i]);
|
||||
ds.Tables[ms.Info[i]].Columns.Add(ms.Info[i + 1], Type.GetType(ms.Info[i + 2]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadDataTable(ArrayList rows, DataTable dt)
|
||||
{
|
||||
dt.BeginInit();
|
||||
dt.BeginLoadData();
|
||||
List<int> guidcols = new List<int>();
|
||||
List<int> datecol = new List<int>();
|
||||
|
||||
foreach (DataColumn c in dt.Columns)
|
||||
{
|
||||
if (c.DataType == typeof(Guid) || c.DataType == typeof(Guid?))
|
||||
guidcols.Add(c.Ordinal);
|
||||
if (UseUTCDateTime && (c.DataType == typeof(DateTime) || c.DataType == typeof(DateTime?)))
|
||||
datecol.Add(c.Ordinal);
|
||||
}
|
||||
|
||||
foreach (ArrayList row in rows)
|
||||
{
|
||||
object[] v = new object[row.Count];
|
||||
row.CopyTo(v, 0);
|
||||
foreach (int i in guidcols)
|
||||
{
|
||||
string s = (string)v[i];
|
||||
if (s != null && s.Length < 36)
|
||||
v[i] = new Guid(Convert.FromBase64String(s));
|
||||
}
|
||||
if (UseUTCDateTime)
|
||||
{
|
||||
foreach (int i in datecol)
|
||||
{
|
||||
string s = (string)v[i];
|
||||
if (s != null)
|
||||
v[i] = CreateDateTime(s);
|
||||
}
|
||||
}
|
||||
dt.Rows.Add(v);
|
||||
}
|
||||
|
||||
dt.EndLoadData();
|
||||
dt.EndInit();
|
||||
}
|
||||
|
||||
DataTable CreateDataTable(Dictionary<string, object> reader, Dictionary<string, object> globalTypes)
|
||||
{
|
||||
var dt = new DataTable();
|
||||
|
||||
// read dataset schema here
|
||||
var schema = reader["$schema"];
|
||||
|
||||
if (schema is string)
|
||||
{
|
||||
TextReader tr = new StringReader((string)schema);
|
||||
dt.ReadXmlSchema(tr);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ms = (DatasetSchema)this.ParseDictionary((Dictionary<string, object>)schema, globalTypes, typeof(DatasetSchema));
|
||||
dt.TableName = ms.Info[0];
|
||||
for (int i = 0; i < ms.Info.Count; i += 3)
|
||||
{
|
||||
dt.Columns.Add(ms.Info[i + 1], Type.GetType(ms.Info[i + 2]));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var pair in reader)
|
||||
{
|
||||
if (pair.Key == "$type" || pair.Key == "$schema")
|
||||
continue;
|
||||
|
||||
var rows = (ArrayList)pair.Value;
|
||||
if (rows == null)
|
||||
continue;
|
||||
|
||||
if (!dt.TableName.Equals(pair.Key, StringComparison.InvariantCultureIgnoreCase))
|
||||
continue;
|
||||
|
||||
ReadDataTable(rows, dt);
|
||||
}
|
||||
|
||||
return dt;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,408 @@
|
|||
//http://fastjson.codeplex.com/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Exceptron.Driver.fastJSON
|
||||
{
|
||||
/// <summary>
|
||||
/// This class encodes and decodes JSON strings.
|
||||
/// Spec. details, see http://www.json.org/
|
||||
///
|
||||
/// JSON uses Arrays and Objects. These correspond here to the datatypes ArrayList and Hashtable.
|
||||
/// All numbers are parsed to doubles.
|
||||
/// </summary>
|
||||
internal class JsonParser
|
||||
{
|
||||
enum Token
|
||||
{
|
||||
None = -1, // Used to denote no Lookahead available
|
||||
Curly_Open,
|
||||
Curly_Close,
|
||||
Squared_Open,
|
||||
Squared_Close,
|
||||
Colon,
|
||||
Comma,
|
||||
String,
|
||||
Number,
|
||||
True,
|
||||
False,
|
||||
Null
|
||||
}
|
||||
|
||||
readonly char[] json;
|
||||
readonly StringBuilder s = new StringBuilder();
|
||||
Token lookAheadToken = Token.None;
|
||||
int index;
|
||||
|
||||
internal JsonParser(string json)
|
||||
{
|
||||
this.json = json.ToCharArray();
|
||||
}
|
||||
|
||||
public object Decode()
|
||||
{
|
||||
return ParseValue();
|
||||
}
|
||||
|
||||
private Dictionary<string, object> ParseObject()
|
||||
{
|
||||
Dictionary<string, object> table = new Dictionary<string, object>();
|
||||
|
||||
ConsumeToken(); // {
|
||||
|
||||
while (true)
|
||||
{
|
||||
switch (LookAhead())
|
||||
{
|
||||
|
||||
case Token.Comma:
|
||||
ConsumeToken();
|
||||
break;
|
||||
|
||||
case Token.Curly_Close:
|
||||
ConsumeToken();
|
||||
return table;
|
||||
|
||||
default:
|
||||
{
|
||||
|
||||
// name
|
||||
string name = ParseString();
|
||||
|
||||
// :
|
||||
if (NextToken() != Token.Colon)
|
||||
{
|
||||
throw new Exception("Expected colon at index " + index);
|
||||
}
|
||||
|
||||
// value
|
||||
object value = ParseValue();
|
||||
|
||||
table[name] = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if SILVERLIGHT
|
||||
private List<object> ParseArray()
|
||||
{
|
||||
List<object> array = new List<object>();
|
||||
#else
|
||||
private ArrayList ParseArray()
|
||||
{
|
||||
ArrayList array = new ArrayList();
|
||||
#endif
|
||||
ConsumeToken(); // [
|
||||
|
||||
while (true)
|
||||
{
|
||||
switch (LookAhead())
|
||||
{
|
||||
|
||||
case Token.Comma:
|
||||
ConsumeToken();
|
||||
break;
|
||||
|
||||
case Token.Squared_Close:
|
||||
ConsumeToken();
|
||||
return array;
|
||||
|
||||
default:
|
||||
{
|
||||
array.Add(ParseValue());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object ParseValue()
|
||||
{
|
||||
switch (LookAhead())
|
||||
{
|
||||
case Token.Number:
|
||||
return ParseNumber();
|
||||
|
||||
case Token.String:
|
||||
return ParseString();
|
||||
|
||||
case Token.Curly_Open:
|
||||
return ParseObject();
|
||||
|
||||
case Token.Squared_Open:
|
||||
return ParseArray();
|
||||
|
||||
case Token.True:
|
||||
ConsumeToken();
|
||||
return true;
|
||||
|
||||
case Token.False:
|
||||
ConsumeToken();
|
||||
return false;
|
||||
|
||||
case Token.Null:
|
||||
ConsumeToken();
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new Exception("Unrecognized token at index" + index);
|
||||
}
|
||||
|
||||
private string ParseString()
|
||||
{
|
||||
ConsumeToken(); // "
|
||||
|
||||
s.Length = 0;
|
||||
|
||||
int runIndex = -1;
|
||||
|
||||
while (index < json.Length)
|
||||
{
|
||||
var c = json[index++];
|
||||
|
||||
if (c == '"')
|
||||
{
|
||||
if (runIndex != -1)
|
||||
{
|
||||
if (s.Length == 0)
|
||||
return new string(json, runIndex, index - runIndex - 1);
|
||||
|
||||
s.Append(json, runIndex, index - runIndex - 1);
|
||||
}
|
||||
return s.ToString();
|
||||
}
|
||||
|
||||
if (c != '\\')
|
||||
{
|
||||
if (runIndex == -1)
|
||||
runIndex = index - 1;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (index == json.Length) break;
|
||||
|
||||
if (runIndex != -1)
|
||||
{
|
||||
s.Append(json, runIndex, index - runIndex - 1);
|
||||
runIndex = -1;
|
||||
}
|
||||
|
||||
switch (json[index++])
|
||||
{
|
||||
case '"':
|
||||
s.Append('"');
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
s.Append('\\');
|
||||
break;
|
||||
|
||||
case '/':
|
||||
s.Append('/');
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
s.Append('\b');
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
s.Append('\f');
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
s.Append('\n');
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
s.Append('\r');
|
||||
break;
|
||||
|
||||
case 't':
|
||||
s.Append('\t');
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
{
|
||||
int remainingLength = json.Length - index;
|
||||
if (remainingLength < 4) break;
|
||||
|
||||
// parse the 32 bit hex into an integer codepoint
|
||||
uint codePoint = ParseUnicode(json[index], json[index + 1], json[index + 2], json[index + 3]);
|
||||
s.Append((char)codePoint);
|
||||
|
||||
// skip 4 chars
|
||||
index += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("Unexpectedly reached end of string");
|
||||
}
|
||||
|
||||
private uint ParseSingleChar(char c1, uint multipliyer)
|
||||
{
|
||||
uint p1 = 0;
|
||||
if (c1 >= '0' && c1 <= '9')
|
||||
p1 = (uint)(c1 - '0') * multipliyer;
|
||||
else if (c1 >= 'A' && c1 <= 'F')
|
||||
p1 = (uint)((c1 - 'A') + 10) * multipliyer;
|
||||
else if (c1 >= 'a' && c1 <= 'f')
|
||||
p1 = (uint)((c1 - 'a') + 10) * multipliyer;
|
||||
return p1;
|
||||
}
|
||||
|
||||
private uint ParseUnicode(char c1, char c2, char c3, char c4)
|
||||
{
|
||||
uint p1 = ParseSingleChar(c1, 0x1000);
|
||||
uint p2 = ParseSingleChar(c2, 0x100);
|
||||
uint p3 = ParseSingleChar(c3, 0x10);
|
||||
uint p4 = ParseSingleChar(c4, 1);
|
||||
|
||||
return p1 + p2 + p3 + p4;
|
||||
}
|
||||
|
||||
private string ParseNumber()
|
||||
{
|
||||
ConsumeToken();
|
||||
|
||||
// Need to start back one place because the first digit is also a token and would have been consumed
|
||||
var startIndex = index - 1;
|
||||
|
||||
do
|
||||
{
|
||||
var c = json[index];
|
||||
|
||||
if ((c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E')
|
||||
{
|
||||
if (++index == json.Length) throw new Exception("Unexpected end of string whilst parsing number");
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
} while (true);
|
||||
|
||||
return new string(json, startIndex, index - startIndex);
|
||||
}
|
||||
|
||||
private Token LookAhead()
|
||||
{
|
||||
if (lookAheadToken != Token.None) return lookAheadToken;
|
||||
|
||||
return lookAheadToken = NextTokenCore();
|
||||
}
|
||||
|
||||
private void ConsumeToken()
|
||||
{
|
||||
lookAheadToken = Token.None;
|
||||
}
|
||||
|
||||
private Token NextToken()
|
||||
{
|
||||
var result = lookAheadToken != Token.None ? lookAheadToken : NextTokenCore();
|
||||
|
||||
lookAheadToken = Token.None;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Token NextTokenCore()
|
||||
{
|
||||
char c;
|
||||
|
||||
// Skip past whitespace
|
||||
do
|
||||
{
|
||||
c = json[index];
|
||||
|
||||
if (c > ' ') break;
|
||||
if (c != ' ' && c != '\t' && c != '\n' && c != '\r') break;
|
||||
|
||||
} while (++index < json.Length);
|
||||
|
||||
if (index == json.Length)
|
||||
{
|
||||
throw new Exception("Reached end of string unexpectedly");
|
||||
}
|
||||
|
||||
c = json[index];
|
||||
|
||||
index++;
|
||||
|
||||
//if (c >= '0' && c <= '9')
|
||||
// return Token.Number;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '{':
|
||||
return Token.Curly_Open;
|
||||
|
||||
case '}':
|
||||
return Token.Curly_Close;
|
||||
|
||||
case '[':
|
||||
return Token.Squared_Open;
|
||||
|
||||
case ']':
|
||||
return Token.Squared_Close;
|
||||
|
||||
case ',':
|
||||
return Token.Comma;
|
||||
|
||||
case '"':
|
||||
return Token.String;
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case '-': case '+': case '.':
|
||||
return Token.Number;
|
||||
|
||||
case ':':
|
||||
return Token.Colon;
|
||||
|
||||
case 'f':
|
||||
if (json.Length - index >= 4 &&
|
||||
json[index + 0] == 'a' &&
|
||||
json[index + 1] == 'l' &&
|
||||
json[index + 2] == 's' &&
|
||||
json[index + 3] == 'e')
|
||||
{
|
||||
index += 4;
|
||||
return Token.False;
|
||||
}
|
||||
break;
|
||||
|
||||
case 't':
|
||||
if (json.Length - index >= 3 &&
|
||||
json[index + 0] == 'r' &&
|
||||
json[index + 1] == 'u' &&
|
||||
json[index + 2] == 'e')
|
||||
{
|
||||
index += 3;
|
||||
return Token.True;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
if (json.Length - index >= 3 &&
|
||||
json[index + 0] == 'u' &&
|
||||
json[index + 1] == 'l' &&
|
||||
json[index + 2] == 'l')
|
||||
{
|
||||
index += 3;
|
||||
return Token.Null;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
throw new Exception("Could not find token at index " + --index);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,518 @@
|
|||
//http://fastjson.codeplex.com/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Exceptron.Driver.fastJSON
|
||||
{
|
||||
internal class JSONSerializer
|
||||
{
|
||||
private readonly StringBuilder _output = new StringBuilder();
|
||||
readonly bool useMinimalDataSetSchema;
|
||||
readonly bool fastguid = true;
|
||||
readonly bool useExtension = true;
|
||||
readonly bool serializeNulls = true;
|
||||
readonly int _MAX_DEPTH = 10;
|
||||
bool _Indent = false;
|
||||
bool _useGlobalTypes = true;
|
||||
int _current_depth = 0;
|
||||
private Dictionary<string, int> _globalTypes = new Dictionary<string, int>();
|
||||
|
||||
internal JSONSerializer(bool UseMinimalDataSetSchema, bool UseFastGuid, bool UseExtensions, bool SerializeNulls, bool IndentOutput)
|
||||
{
|
||||
this.useMinimalDataSetSchema = UseMinimalDataSetSchema;
|
||||
this.fastguid = UseFastGuid;
|
||||
this.useExtension = UseExtensions;
|
||||
_Indent = IndentOutput;
|
||||
this.serializeNulls = SerializeNulls;
|
||||
if (useExtension == false)
|
||||
_useGlobalTypes = false;
|
||||
}
|
||||
|
||||
internal string ConvertToJSON(object obj)
|
||||
{
|
||||
WriteValue(obj);
|
||||
|
||||
string str = "";
|
||||
if (_useGlobalTypes)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append("{\"$types\":{");
|
||||
bool pendingSeparator = false;
|
||||
foreach (var kv in _globalTypes)
|
||||
{
|
||||
if (pendingSeparator) sb.Append(',');
|
||||
pendingSeparator = true;
|
||||
sb.Append("\"");
|
||||
sb.Append(kv.Key);
|
||||
sb.Append("\":\"");
|
||||
sb.Append(kv.Value);
|
||||
sb.Append("\"");
|
||||
}
|
||||
sb.Append("},");
|
||||
str = sb.ToString() + _output.ToString();
|
||||
}
|
||||
else
|
||||
str = _output.ToString();
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
private void WriteValue(object obj)
|
||||
{
|
||||
if (obj == null || obj is DBNull)
|
||||
_output.Append("null");
|
||||
|
||||
else if (obj is string || obj is char)
|
||||
WriteString((string)obj);
|
||||
|
||||
else if (obj is Guid)
|
||||
WriteGuid((Guid)obj);
|
||||
|
||||
else if (obj is bool)
|
||||
_output.Append(((bool)obj) ? "true" : "false"); // conform to standard
|
||||
|
||||
else if (
|
||||
obj is int || obj is long || obj is double ||
|
||||
obj is decimal || obj is float ||
|
||||
obj is byte || obj is short ||
|
||||
obj is sbyte || obj is ushort ||
|
||||
obj is uint || obj is ulong
|
||||
)
|
||||
_output.Append(((IConvertible)obj).ToString(NumberFormatInfo.InvariantInfo));
|
||||
|
||||
else if (obj is DateTime)
|
||||
WriteDateTime((DateTime)obj);
|
||||
|
||||
else if (obj is IDictionary && obj.GetType().IsGenericType && obj.GetType().GetGenericArguments()[0] == typeof(string))
|
||||
WriteStringDictionary((IDictionary)obj);
|
||||
|
||||
else if (obj is IDictionary)
|
||||
WriteDictionary((IDictionary)obj);
|
||||
#if !SILVERLIGHT
|
||||
else if (obj is DataSet)
|
||||
WriteDataset((DataSet)obj);
|
||||
|
||||
else if (obj is DataTable)
|
||||
this.WriteDataTable((DataTable)obj);
|
||||
#endif
|
||||
else if (obj is byte[])
|
||||
WriteBytes((byte[])obj);
|
||||
|
||||
else if (obj is Array || obj is IList || obj is ICollection)
|
||||
WriteArray((IEnumerable)obj);
|
||||
|
||||
else if (obj is Enum)
|
||||
WriteEnum((Enum)obj);
|
||||
|
||||
#if CUSTOMTYPE
|
||||
else if (JSON.Instance.IsTypeRegistered(obj.GetType()))
|
||||
WriteCustom(obj);
|
||||
#endif
|
||||
else
|
||||
WriteObject(obj);
|
||||
}
|
||||
|
||||
#if CUSTOMTYPE
|
||||
private void WriteCustom(object obj)
|
||||
{
|
||||
Serialize s;
|
||||
JSON.Instance._customSerializer.TryGetValue(obj.GetType(), out s);
|
||||
WriteStringFast(s(obj));
|
||||
}
|
||||
#endif
|
||||
|
||||
private void WriteEnum(Enum e)
|
||||
{
|
||||
// TODO : optimize enum write
|
||||
WriteStringFast(e.ToString());
|
||||
}
|
||||
|
||||
private void WriteGuid(Guid g)
|
||||
{
|
||||
if (fastguid == false)
|
||||
WriteStringFast(g.ToString());
|
||||
else
|
||||
WriteBytes(g.ToByteArray());
|
||||
}
|
||||
|
||||
private void WriteBytes(byte[] bytes)
|
||||
{
|
||||
#if !SILVERLIGHT
|
||||
WriteStringFast(Convert.ToBase64String(bytes, 0, bytes.Length, Base64FormattingOptions.None));
|
||||
#else
|
||||
WriteStringFast(Convert.ToBase64String(bytes, 0, bytes.Length));
|
||||
#endif
|
||||
}
|
||||
|
||||
private void WriteDateTime(DateTime dateTime)
|
||||
{
|
||||
// datetime format standard : yyyy-MM-dd HH:mm:ss
|
||||
DateTime dt = dateTime;
|
||||
if (JSON.Instance.UseUTCDateTime)
|
||||
dt = dateTime.ToUniversalTime();
|
||||
|
||||
_output.Append("\"");
|
||||
_output.Append(dt.Year.ToString("0000", NumberFormatInfo.InvariantInfo));
|
||||
_output.Append("-");
|
||||
_output.Append(dt.Month.ToString("00", NumberFormatInfo.InvariantInfo));
|
||||
_output.Append("-");
|
||||
_output.Append(dt.Day.ToString("00", NumberFormatInfo.InvariantInfo));
|
||||
_output.Append(" ");
|
||||
_output.Append(dt.Hour.ToString("00", NumberFormatInfo.InvariantInfo));
|
||||
_output.Append(":");
|
||||
_output.Append(dt.Minute.ToString("00", NumberFormatInfo.InvariantInfo));
|
||||
_output.Append(":");
|
||||
_output.Append(dt.Second.ToString("00", NumberFormatInfo.InvariantInfo));
|
||||
|
||||
if (JSON.Instance.UseUTCDateTime)
|
||||
_output.Append("Z");
|
||||
|
||||
_output.Append("\"");
|
||||
}
|
||||
#if !SILVERLIGHT
|
||||
private DatasetSchema GetSchema(DataTable ds)
|
||||
{
|
||||
if (ds == null) return null;
|
||||
|
||||
DatasetSchema m = new DatasetSchema();
|
||||
m.Info = new List<string>();
|
||||
m.Name = ds.TableName;
|
||||
|
||||
foreach (DataColumn c in ds.Columns)
|
||||
{
|
||||
m.Info.Add(ds.TableName);
|
||||
m.Info.Add(c.ColumnName);
|
||||
m.Info.Add(c.DataType.ToString());
|
||||
}
|
||||
// TODO : serialize relations and constraints here
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
private DatasetSchema GetSchema(DataSet ds)
|
||||
{
|
||||
if (ds == null) return null;
|
||||
|
||||
DatasetSchema m = new DatasetSchema();
|
||||
m.Info = new List<string>();
|
||||
m.Name = ds.DataSetName;
|
||||
|
||||
foreach (DataTable t in ds.Tables)
|
||||
{
|
||||
foreach (DataColumn c in t.Columns)
|
||||
{
|
||||
m.Info.Add(t.TableName);
|
||||
m.Info.Add(c.ColumnName);
|
||||
m.Info.Add(c.DataType.ToString());
|
||||
}
|
||||
}
|
||||
// TODO : serialize relations and constraints here
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
private string GetXmlSchema(DataTable dt)
|
||||
{
|
||||
using (var writer = new StringWriter())
|
||||
{
|
||||
dt.WriteXmlSchema(writer);
|
||||
return dt.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteDataset(DataSet ds)
|
||||
{
|
||||
_output.Append('{');
|
||||
if (useExtension)
|
||||
{
|
||||
WritePair("$schema", useMinimalDataSetSchema ? (object)GetSchema(ds) : ds.GetXmlSchema());
|
||||
_output.Append(',');
|
||||
}
|
||||
bool tablesep = false;
|
||||
foreach (DataTable table in ds.Tables)
|
||||
{
|
||||
if (tablesep) _output.Append(",");
|
||||
tablesep = true;
|
||||
WriteDataTableData(table);
|
||||
}
|
||||
// end dataset
|
||||
_output.Append('}');
|
||||
}
|
||||
|
||||
private void WriteDataTableData(DataTable table)
|
||||
{
|
||||
_output.Append('\"');
|
||||
_output.Append(table.TableName);
|
||||
_output.Append("\":[");
|
||||
DataColumnCollection cols = table.Columns;
|
||||
bool rowseparator = false;
|
||||
foreach (DataRow row in table.Rows)
|
||||
{
|
||||
if (rowseparator) _output.Append(",");
|
||||
rowseparator = true;
|
||||
_output.Append('[');
|
||||
|
||||
bool pendingSeperator = false;
|
||||
foreach (DataColumn column in cols)
|
||||
{
|
||||
if (pendingSeperator) _output.Append(',');
|
||||
WriteValue(row[column]);
|
||||
pendingSeperator = true;
|
||||
}
|
||||
_output.Append(']');
|
||||
}
|
||||
|
||||
_output.Append(']');
|
||||
}
|
||||
|
||||
void WriteDataTable(DataTable dt)
|
||||
{
|
||||
this._output.Append('{');
|
||||
if (this.useExtension)
|
||||
{
|
||||
this.WritePair("$schema", this.useMinimalDataSetSchema ? (object)this.GetSchema(dt) : this.GetXmlSchema(dt));
|
||||
this._output.Append(',');
|
||||
}
|
||||
|
||||
WriteDataTableData(dt);
|
||||
|
||||
// end datatable
|
||||
this._output.Append('}');
|
||||
}
|
||||
#endif
|
||||
bool _firstWritten = false;
|
||||
private void WriteObject(object obj)
|
||||
{
|
||||
Indent();
|
||||
if (_useGlobalTypes == false)
|
||||
_output.Append('{');
|
||||
else
|
||||
{
|
||||
if (_firstWritten)
|
||||
_output.Append("{");
|
||||
}
|
||||
_firstWritten = true;
|
||||
_current_depth++;
|
||||
if (_current_depth > _MAX_DEPTH)
|
||||
throw new Exception("Serializer encountered maximum depth of " + _MAX_DEPTH);
|
||||
|
||||
|
||||
Dictionary<string, string> map = new Dictionary<string, string>();
|
||||
Type t = obj.GetType();
|
||||
bool append = false;
|
||||
if (useExtension)
|
||||
{
|
||||
if (_useGlobalTypes == false)
|
||||
WritePairFast("$type", JSON.Instance.GetTypeAssemblyName(t));
|
||||
else
|
||||
{
|
||||
int dt = 0;
|
||||
string ct = JSON.Instance.GetTypeAssemblyName(t);
|
||||
if (_globalTypes.TryGetValue(ct, out dt) == false)
|
||||
{
|
||||
dt = _globalTypes.Count + 1;
|
||||
_globalTypes.Add(ct, dt);
|
||||
}
|
||||
WritePairFast("$type", dt.ToString());
|
||||
}
|
||||
append = true;
|
||||
}
|
||||
|
||||
List<Getters> g = JSON.Instance.GetGetters(t);
|
||||
foreach (var p in g)
|
||||
{
|
||||
if (append)
|
||||
_output.Append(',');
|
||||
object o = p.Getter(obj);
|
||||
if ((o == null || o is DBNull) && serializeNulls == false)
|
||||
append = false;
|
||||
else
|
||||
{
|
||||
WritePair(p.Name, o);
|
||||
if (o != null && useExtension)
|
||||
{
|
||||
Type tt = o.GetType();
|
||||
if (tt == typeof(System.Object))
|
||||
map.Add(p.Name, tt.ToString());
|
||||
}
|
||||
append = true;
|
||||
}
|
||||
}
|
||||
if (map.Count > 0 && useExtension)
|
||||
{
|
||||
_output.Append(",\"$map\":");
|
||||
WriteStringDictionary(map);
|
||||
}
|
||||
_current_depth--;
|
||||
Indent();
|
||||
_output.Append('}');
|
||||
_current_depth--;
|
||||
|
||||
}
|
||||
|
||||
private void Indent()
|
||||
{
|
||||
Indent(false);
|
||||
}
|
||||
|
||||
private void Indent(bool dec)
|
||||
{
|
||||
if (_Indent)
|
||||
{
|
||||
_output.Append("\r\n");
|
||||
for (int i = 0; i < _current_depth - (dec ? 1 : 0); i++)
|
||||
_output.Append("\t");
|
||||
}
|
||||
}
|
||||
|
||||
private void WritePairFast(string name, string value)
|
||||
{
|
||||
if ((value == null) && serializeNulls == false)
|
||||
return;
|
||||
Indent();
|
||||
WriteStringFast(name);
|
||||
|
||||
_output.Append(':');
|
||||
|
||||
WriteStringFast(value);
|
||||
}
|
||||
|
||||
private void WritePair(string name, object value)
|
||||
{
|
||||
if ((value == null || value is DBNull) && serializeNulls == false)
|
||||
return;
|
||||
Indent();
|
||||
WriteStringFast(name);
|
||||
|
||||
_output.Append(':');
|
||||
|
||||
WriteValue(value);
|
||||
}
|
||||
|
||||
private void WriteArray(IEnumerable array)
|
||||
{
|
||||
Indent();
|
||||
_output.Append('[');
|
||||
|
||||
bool pendingSeperator = false;
|
||||
|
||||
foreach (object obj in array)
|
||||
{
|
||||
Indent();
|
||||
if (pendingSeperator) _output.Append(',');
|
||||
|
||||
WriteValue(obj);
|
||||
|
||||
pendingSeperator = true;
|
||||
}
|
||||
Indent();
|
||||
_output.Append(']');
|
||||
}
|
||||
|
||||
private void WriteStringDictionary(IDictionary dic)
|
||||
{
|
||||
Indent();
|
||||
_output.Append('{');
|
||||
|
||||
bool pendingSeparator = false;
|
||||
|
||||
foreach (DictionaryEntry entry in dic)
|
||||
{
|
||||
if (pendingSeparator) _output.Append(',');
|
||||
|
||||
WritePair((string)entry.Key, entry.Value);
|
||||
|
||||
pendingSeparator = true;
|
||||
}
|
||||
Indent();
|
||||
_output.Append('}');
|
||||
}
|
||||
|
||||
private void WriteDictionary(IDictionary dic)
|
||||
{
|
||||
Indent();
|
||||
_output.Append('[');
|
||||
|
||||
bool pendingSeparator = false;
|
||||
|
||||
foreach (DictionaryEntry entry in dic)
|
||||
{
|
||||
if (pendingSeparator) _output.Append(',');
|
||||
Indent();
|
||||
_output.Append('{');
|
||||
WritePair("k", entry.Key);
|
||||
_output.Append(",");
|
||||
WritePair("v", entry.Value);
|
||||
Indent();
|
||||
_output.Append('}');
|
||||
|
||||
pendingSeparator = true;
|
||||
}
|
||||
Indent();
|
||||
_output.Append(']');
|
||||
}
|
||||
|
||||
private void WriteStringFast(string s)
|
||||
{
|
||||
//Indent();
|
||||
_output.Append('\"');
|
||||
_output.Append(s);
|
||||
_output.Append('\"');
|
||||
}
|
||||
|
||||
private void WriteString(string s)
|
||||
{
|
||||
//Indent();
|
||||
_output.Append('\"');
|
||||
|
||||
int runIndex = -1;
|
||||
|
||||
for (var index = 0; index < s.Length; ++index)
|
||||
{
|
||||
var c = s[index];
|
||||
|
||||
if (c >= ' ' && c < 128 && c != '\"' && c != '\\')
|
||||
{
|
||||
if (runIndex == -1)
|
||||
{
|
||||
runIndex = index;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (runIndex != -1)
|
||||
{
|
||||
_output.Append(s, runIndex, index - runIndex);
|
||||
runIndex = -1;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '\t': _output.Append("\\t"); break;
|
||||
case '\r': _output.Append("\\r"); break;
|
||||
case '\n': _output.Append("\\n"); break;
|
||||
case '"':
|
||||
case '\\': _output.Append('\\'); _output.Append(c); break;
|
||||
default:
|
||||
_output.Append("\\u");
|
||||
_output.Append(((int)c).ToString("X4", NumberFormatInfo.InvariantInfo));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (runIndex != -1)
|
||||
{
|
||||
_output.Append(s, runIndex, s.Length - runIndex);
|
||||
}
|
||||
|
||||
_output.Append('\"');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
//http://fastjson.codeplex.com/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Exceptron.Driver.fastJSON
|
||||
{
|
||||
internal class SafeDictionary<TKey, TValue>
|
||||
{
|
||||
private readonly object _Padlock = new object();
|
||||
private readonly Dictionary<TKey, TValue> _Dictionary = new Dictionary<TKey, TValue>();
|
||||
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
return _Dictionary.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Dictionary[key];
|
||||
}
|
||||
}
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
return ((ICollection<KeyValuePair<TKey, TValue>>)_Dictionary).GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
lock (_Padlock)
|
||||
{
|
||||
if (_Dictionary.ContainsKey(key) == false)
|
||||
_Dictionary.Add(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue