mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
769 lines
19 KiB
769 lines
19 KiB
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. |
|
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. |
|
|
|
namespace LightJson |
|
{ |
|
using System; |
|
using System.Collections.Generic; |
|
using System.Diagnostics; |
|
using System.Diagnostics.CodeAnalysis; |
|
using System.Globalization; |
|
using LightJson.Serialization; |
|
|
|
/// <summary> |
|
/// A wrapper object that contains a valid JSON value. |
|
/// </summary> |
|
[DebuggerDisplay("{ToString(),nq}", Type = "JsonValue({Type})")] |
|
[DebuggerTypeProxy(typeof(JsonValueDebugView))] |
|
internal struct JsonValue |
|
{ |
|
/// <summary> |
|
/// Represents a null JsonValue. |
|
/// </summary> |
|
public static readonly JsonValue Null = new JsonValue(JsonValueType.Null, default(double), null); |
|
|
|
private readonly JsonValueType type; |
|
private readonly object reference; |
|
private readonly double value; |
|
|
|
/// <summary> |
|
/// Initializes a new instance of the <see cref="JsonValue"/> struct, representing a Boolean value. |
|
/// </summary> |
|
/// <param name="value">The value to be wrapped.</param> |
|
public JsonValue(bool? value) |
|
{ |
|
if (value.HasValue) { |
|
this.reference = null; |
|
|
|
this.type = JsonValueType.Boolean; |
|
|
|
this.value = value.Value ? 1 : 0; |
|
} else { |
|
this = JsonValue.Null; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Initializes a new instance of the <see cref="JsonValue"/> struct, representing a Number value. |
|
/// </summary> |
|
/// <param name="value">The value to be wrapped.</param> |
|
public JsonValue(double? value) |
|
{ |
|
if (value.HasValue) { |
|
this.reference = null; |
|
|
|
this.type = JsonValueType.Number; |
|
|
|
this.value = value.Value; |
|
} else { |
|
this = JsonValue.Null; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Initializes a new instance of the <see cref="JsonValue"/> struct, representing a String value. |
|
/// </summary> |
|
/// <param name="value">The value to be wrapped.</param> |
|
public JsonValue(string value) |
|
{ |
|
if (value != null) { |
|
this.value = default(double); |
|
|
|
this.type = JsonValueType.String; |
|
|
|
this.reference = value; |
|
} else { |
|
this = JsonValue.Null; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Initializes a new instance of the <see cref="JsonValue"/> struct, representing a JsonObject. |
|
/// </summary> |
|
/// <param name="value">The value to be wrapped.</param> |
|
public JsonValue(JsonObject value) |
|
{ |
|
if (value != null) { |
|
this.value = default(double); |
|
|
|
this.type = JsonValueType.Object; |
|
|
|
this.reference = value; |
|
} else { |
|
this = JsonValue.Null; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Initializes a new instance of the <see cref="JsonValue"/> struct, representing a Array reference value. |
|
/// </summary> |
|
/// <param name="value">The value to be wrapped.</param> |
|
public JsonValue(JsonArray value) |
|
{ |
|
if (value != null) { |
|
this.value = default(double); |
|
|
|
this.type = JsonValueType.Array; |
|
|
|
this.reference = value; |
|
} else { |
|
this = JsonValue.Null; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Initializes a new instance of the <see cref="JsonValue"/> struct. |
|
/// </summary> |
|
/// <param name="type">The Json type of the JsonValue.</param> |
|
/// <param name="value"> |
|
/// The internal value of the JsonValue. |
|
/// This is used when the Json type is Number or Boolean. |
|
/// </param> |
|
/// <param name="reference"> |
|
/// The internal value reference of the JsonValue. |
|
/// This value is used when the Json type is String, JsonObject, or JsonArray. |
|
/// </param> |
|
private JsonValue(JsonValueType type, double value, object reference) |
|
{ |
|
this.type = type; |
|
this.value = value; |
|
this.reference = reference; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the type of this JsonValue. |
|
/// </summary> |
|
/// <value>The type of this JsonValue.</value> |
|
public JsonValueType Type { |
|
get { |
|
return this.type; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets a value indicating whether this JsonValue is Null. |
|
/// </summary> |
|
/// <value>A value indicating whether this JsonValue is Null.</value> |
|
public bool IsNull { |
|
get { |
|
return this.Type == JsonValueType.Null; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets a value indicating whether this JsonValue is a Boolean. |
|
/// </summary> |
|
/// <value>A value indicating whether this JsonValue is a Boolean.</value> |
|
public bool IsBoolean { |
|
get { |
|
return this.Type == JsonValueType.Boolean; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets a value indicating whether this JsonValue is an Integer. |
|
/// </summary> |
|
/// <value>A value indicating whether this JsonValue is an Integer.</value> |
|
public bool IsInteger { |
|
get { |
|
if (!this.IsNumber) { |
|
return false; |
|
} |
|
|
|
var value = this.value; |
|
return unchecked((int)value) == value; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets a value indicating whether this JsonValue is a Number. |
|
/// </summary> |
|
/// <value>A value indicating whether this JsonValue is a Number.</value> |
|
public bool IsNumber { |
|
get { |
|
return this.Type == JsonValueType.Number; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets a value indicating whether this JsonValue is a String. |
|
/// </summary> |
|
/// <value>A value indicating whether this JsonValue is a String.</value> |
|
public bool IsString { |
|
get { |
|
return this.Type == JsonValueType.String; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets a value indicating whether this JsonValue is a JsonObject. |
|
/// </summary> |
|
/// <value>A value indicating whether this JsonValue is a JsonObject.</value> |
|
public bool IsJsonObject { |
|
get { |
|
return this.Type == JsonValueType.Object; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets a value indicating whether this JsonValue is a JsonArray. |
|
/// </summary> |
|
/// <value>A value indicating whether this JsonValue is a JsonArray.</value> |
|
public bool IsJsonArray { |
|
get { |
|
return this.Type == JsonValueType.Array; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets a value indicating whether this JsonValue represents a DateTime. |
|
/// </summary> |
|
/// <value>A value indicating whether this JsonValue represents a DateTime.</value> |
|
public bool IsDateTime { |
|
get { |
|
return this.AsDateTime != null; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets a value indicating whether this value is true or false. |
|
/// </summary> |
|
/// <value>This value as a Boolean type.</value> |
|
public bool AsBoolean { |
|
get { |
|
switch (this.Type) { |
|
case JsonValueType.Boolean: |
|
return this.value == 1; |
|
|
|
case JsonValueType.Number: |
|
return this.value != 0; |
|
|
|
case JsonValueType.String: |
|
return (string)this.reference != string.Empty; |
|
|
|
case JsonValueType.Object: |
|
case JsonValueType.Array: |
|
return true; |
|
|
|
default: |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets this value as an Integer type. |
|
/// </summary> |
|
/// <value>This value as an Integer type.</value> |
|
public int AsInteger { |
|
get { |
|
var value = this.AsNumber; |
|
|
|
// Prevent overflow if the value doesn't fit. |
|
if (value >= int.MaxValue) { |
|
return int.MaxValue; |
|
} |
|
|
|
if (value <= int.MinValue) { |
|
return int.MinValue; |
|
} |
|
|
|
return (int)value; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets this value as a Number type. |
|
/// </summary> |
|
/// <value>This value as a Number type.</value> |
|
public double AsNumber { |
|
get { |
|
switch (this.Type) { |
|
case JsonValueType.Boolean: |
|
return (this.value == 1) |
|
? 1 |
|
: 0; |
|
|
|
case JsonValueType.Number: |
|
return this.value; |
|
|
|
case JsonValueType.String: |
|
double number; |
|
if (double.TryParse((string)this.reference, NumberStyles.Float, CultureInfo.InvariantCulture, out number)) { |
|
return number; |
|
} else { |
|
goto default; |
|
} |
|
|
|
default: |
|
return 0; |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets this value as a String type. |
|
/// </summary> |
|
/// <value>This value as a String type.</value> |
|
public string AsString { |
|
get { |
|
switch (this.Type) { |
|
case JsonValueType.Boolean: |
|
return (this.value == 1) |
|
? "true" |
|
: "false"; |
|
|
|
case JsonValueType.Number: |
|
return this.value.ToString(CultureInfo.InvariantCulture); |
|
|
|
case JsonValueType.String: |
|
return (string)this.reference; |
|
|
|
default: |
|
return null; |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets this value as an JsonObject. |
|
/// </summary> |
|
/// <value>This value as an JsonObject.</value> |
|
public JsonObject AsJsonObject { |
|
get { |
|
return this.IsJsonObject |
|
? (JsonObject)this.reference |
|
: null; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets this value as an JsonArray. |
|
/// </summary> |
|
/// <value>This value as an JsonArray.</value> |
|
public JsonArray AsJsonArray { |
|
get { |
|
return this.IsJsonArray |
|
? (JsonArray)this.reference |
|
: null; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets this value as a system.DateTime. |
|
/// </summary> |
|
/// <value>This value as a system.DateTime.</value> |
|
public DateTime? AsDateTime { |
|
get { |
|
DateTime value; |
|
|
|
if (this.IsString && DateTime.TryParse((string)this.reference, out value)) { |
|
return value; |
|
} else { |
|
return null; |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets this (inner) value as a System.object. |
|
/// </summary> |
|
/// <value>This (inner) value as a System.object.</value> |
|
public object AsObject { |
|
get { |
|
switch (this.Type) { |
|
case JsonValueType.Boolean: |
|
case JsonValueType.Number: |
|
return this.value; |
|
|
|
case JsonValueType.String: |
|
case JsonValueType.Object: |
|
case JsonValueType.Array: |
|
return this.reference; |
|
|
|
default: |
|
return null; |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets or sets the value associated with the specified key. |
|
/// </summary> |
|
/// <param name="key">The key of the value to get or set.</param> |
|
/// <exception cref="System.InvalidOperationException"> |
|
/// Thrown when this JsonValue is not a JsonObject. |
|
/// </exception> |
|
public JsonValue this[string key] { |
|
get { |
|
if (this.IsJsonObject) { |
|
return ((JsonObject)this.reference)[key]; |
|
} else { |
|
throw new InvalidOperationException("This value does not represent a JsonObject."); |
|
} |
|
} |
|
|
|
set { |
|
if (this.IsJsonObject) { |
|
((JsonObject)this.reference)[key] = value; |
|
} else { |
|
throw new InvalidOperationException("This value does not represent a JsonObject."); |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets or sets the value at the specified index. |
|
/// </summary> |
|
/// <param name="index">The zero-based index of the value to get or set.</param> |
|
/// <exception cref="System.InvalidOperationException"> |
|
/// Thrown when this JsonValue is not a JsonArray |
|
/// </exception> |
|
public JsonValue this[int index] { |
|
get { |
|
if (this.IsJsonArray) { |
|
return ((JsonArray)this.reference)[index]; |
|
} else { |
|
throw new InvalidOperationException("This value does not represent a JsonArray."); |
|
} |
|
} |
|
|
|
set { |
|
if (this.IsJsonArray) { |
|
((JsonArray)this.reference)[index] = value; |
|
} else { |
|
throw new InvalidOperationException("This value does not represent a JsonArray."); |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given nullable boolean into a JsonValue. |
|
/// </summary> |
|
/// <param name="value">The value to be converted.</param> |
|
public static implicit operator JsonValue(bool? value) |
|
{ |
|
return new JsonValue(value); |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given nullable double into a JsonValue. |
|
/// </summary> |
|
/// <param name="value">The value to be converted.</param> |
|
public static implicit operator JsonValue(double? value) |
|
{ |
|
return new JsonValue(value); |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given string into a JsonValue. |
|
/// </summary> |
|
/// <param name="value">The value to be converted.</param> |
|
public static implicit operator JsonValue(string value) |
|
{ |
|
return new JsonValue(value); |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given JsonObject into a JsonValue. |
|
/// </summary> |
|
/// <param name="value">The value to be converted.</param> |
|
public static implicit operator JsonValue(JsonObject value) |
|
{ |
|
return new JsonValue(value); |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given JsonArray into a JsonValue. |
|
/// </summary> |
|
/// <param name="value">The value to be converted.</param> |
|
public static implicit operator JsonValue(JsonArray value) |
|
{ |
|
return new JsonValue(value); |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given DateTime? into a JsonValue. |
|
/// </summary> |
|
/// <remarks> |
|
/// The DateTime value will be stored as a string using ISO 8601 format, |
|
/// since JSON does not define a DateTime type. |
|
/// </remarks> |
|
/// <param name="value">The value to be converted.</param> |
|
public static implicit operator JsonValue(DateTime? value) |
|
{ |
|
if (value == null) { |
|
return JsonValue.Null; |
|
} |
|
|
|
return new JsonValue(value.Value.ToString("o")); |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given JsonValue into an Int. |
|
/// </summary> |
|
/// <param name="jsonValue">The JsonValue to be converted.</param> |
|
public static explicit operator int(JsonValue jsonValue) |
|
{ |
|
if (jsonValue.IsInteger) { |
|
return jsonValue.AsInteger; |
|
} else { |
|
return 0; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given JsonValue into a nullable Int. |
|
/// </summary> |
|
/// <param name="jsonValue">The JsonValue to be converted.</param> |
|
/// <exception cref="System.InvalidCastException"> |
|
/// Throws System.InvalidCastException when the inner value type of the |
|
/// JsonValue is not the desired type of the conversion. |
|
/// </exception> |
|
public static explicit operator int? (JsonValue jsonValue) |
|
{ |
|
if (jsonValue.IsNull) { |
|
return null; |
|
} else { |
|
return (int)jsonValue; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given JsonValue into a Bool. |
|
/// </summary> |
|
/// <param name="jsonValue">The JsonValue to be converted.</param> |
|
public static explicit operator bool(JsonValue jsonValue) |
|
{ |
|
if (jsonValue.IsBoolean) { |
|
return jsonValue.value == 1; |
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given JsonValue into a nullable Bool. |
|
/// </summary> |
|
/// <param name="jsonValue">The JsonValue to be converted.</param> |
|
/// <exception cref="System.InvalidCastException"> |
|
/// Throws System.InvalidCastException when the inner value type of the |
|
/// JsonValue is not the desired type of the conversion. |
|
/// </exception> |
|
public static explicit operator bool? (JsonValue jsonValue) |
|
{ |
|
if (jsonValue.IsNull) { |
|
return null; |
|
} else { |
|
return (bool)jsonValue; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given JsonValue into a Double. |
|
/// </summary> |
|
/// <param name="jsonValue">The JsonValue to be converted.</param> |
|
public static explicit operator double(JsonValue jsonValue) |
|
{ |
|
if (jsonValue.IsNumber) { |
|
return jsonValue.value; |
|
} else { |
|
return double.NaN; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given JsonValue into a nullable Double. |
|
/// </summary> |
|
/// <param name="jsonValue">The JsonValue to be converted.</param> |
|
/// <exception cref="System.InvalidCastException"> |
|
/// Throws System.InvalidCastException when the inner value type of the |
|
/// JsonValue is not the desired type of the conversion. |
|
/// </exception> |
|
public static explicit operator double? (JsonValue jsonValue) |
|
{ |
|
if (jsonValue.IsNull) { |
|
return null; |
|
} else { |
|
return (double)jsonValue; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given JsonValue into a String. |
|
/// </summary> |
|
/// <param name="jsonValue">The JsonValue to be converted.</param> |
|
public static explicit operator string(JsonValue jsonValue) |
|
{ |
|
if (jsonValue.IsString || jsonValue.IsNull) { |
|
return jsonValue.reference as string; |
|
} else { |
|
return null; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given JsonValue into a JsonObject. |
|
/// </summary> |
|
/// <param name="jsonValue">The JsonValue to be converted.</param> |
|
public static explicit operator JsonObject(JsonValue jsonValue) |
|
{ |
|
if (jsonValue.IsJsonObject || jsonValue.IsNull) { |
|
return jsonValue.reference as JsonObject; |
|
} else { |
|
return null; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given JsonValue into a JsonArray. |
|
/// </summary> |
|
/// <param name="jsonValue">The JsonValue to be converted.</param> |
|
public static explicit operator JsonArray(JsonValue jsonValue) |
|
{ |
|
if (jsonValue.IsJsonArray || jsonValue.IsNull) { |
|
return jsonValue.reference as JsonArray; |
|
} else { |
|
return null; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given JsonValue into a DateTime. |
|
/// </summary> |
|
/// <param name="jsonValue">The JsonValue to be converted.</param> |
|
public static explicit operator DateTime(JsonValue jsonValue) |
|
{ |
|
var dateTime = jsonValue.AsDateTime; |
|
|
|
if (dateTime.HasValue) { |
|
return dateTime.Value; |
|
} else { |
|
return DateTime.MinValue; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Converts the given JsonValue into a nullable DateTime. |
|
/// </summary> |
|
/// <param name="jsonValue">The JsonValue to be converted.</param> |
|
public static explicit operator DateTime? (JsonValue jsonValue) |
|
{ |
|
if (jsonValue.IsDateTime || jsonValue.IsNull) { |
|
return jsonValue.AsDateTime; |
|
} else { |
|
return null; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Returns a value indicating whether the two given JsonValues are equal. |
|
/// </summary> |
|
/// <param name="a">First JsonValue to compare.</param> |
|
/// <param name="b">Second JsonValue to compare.</param> |
|
public static bool operator ==(JsonValue a, JsonValue b) |
|
{ |
|
return (a.Type == b.Type) |
|
&& (a.value == b.value) |
|
&& Equals(a.reference, b.reference); |
|
} |
|
|
|
/// <summary> |
|
/// Returns a value indicating whether the two given JsonValues are unequal. |
|
/// </summary> |
|
/// <param name="a">First JsonValue to compare.</param> |
|
/// <param name="b">Second JsonValue to compare.</param> |
|
public static bool operator !=(JsonValue a, JsonValue b) |
|
{ |
|
return !(a == b); |
|
} |
|
|
|
/// <summary> |
|
/// Returns a JsonValue by parsing the given string. |
|
/// </summary> |
|
/// <param name="text">The JSON-formatted string to be parsed.</param> |
|
/// <returns>The <see cref="JsonValue"/> representing the parsed text.</returns> |
|
public static JsonValue Parse(string text) |
|
{ |
|
return JsonReader.Parse(text); |
|
} |
|
|
|
/// <inheritdoc/> |
|
public override bool Equals(object obj) |
|
{ |
|
if (obj == null) { |
|
return this.IsNull; |
|
} |
|
|
|
var jsonValue = obj as JsonValue?; |
|
if (jsonValue == null) { |
|
return false; |
|
} else { |
|
return this == jsonValue.Value; |
|
} |
|
} |
|
|
|
/// <inheritdoc/> |
|
public override int GetHashCode() |
|
{ |
|
if (this.IsNull) { |
|
return this.Type.GetHashCode(); |
|
} else { |
|
return this.Type.GetHashCode() |
|
^ this.value.GetHashCode() |
|
^ EqualityComparer<object>.Default.GetHashCode(this.reference); |
|
} |
|
} |
|
|
|
[ExcludeFromCodeCoverage] |
|
private class JsonValueDebugView |
|
{ |
|
private JsonValue jsonValue; |
|
|
|
public JsonValueDebugView(JsonValue jsonValue) |
|
{ |
|
this.jsonValue = jsonValue; |
|
} |
|
|
|
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] |
|
public JsonObject ObjectView { |
|
get { |
|
if (this.jsonValue.IsJsonObject) { |
|
return (JsonObject)this.jsonValue.reference; |
|
} else { |
|
return null; |
|
} |
|
} |
|
} |
|
|
|
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] |
|
public JsonArray ArrayView { |
|
get { |
|
if (this.jsonValue.IsJsonArray) { |
|
return (JsonArray)this.jsonValue.reference; |
|
} else { |
|
return null; |
|
} |
|
} |
|
} |
|
|
|
public JsonValueType Type { |
|
get { |
|
return this.jsonValue.Type; |
|
} |
|
} |
|
|
|
public object Value { |
|
get { |
|
if (this.jsonValue.IsJsonObject) { |
|
return (JsonObject)this.jsonValue.reference; |
|
} else if (this.jsonValue.IsJsonArray) { |
|
return (JsonArray)this.jsonValue.reference; |
|
} else { |
|
return this.jsonValue; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
}
|
|
|