KGy SOFT

JsonValue Structure

KGy SOFT JSON Libraries Help
Represents a value that can be converted to JSON. It can hold JavaScript primitive types such as Null, Boolean, Number and String, and it can be assigned also from JsonArray and JsonObject types. Its default value represents the JavaScript Undefined value. Use the ToString or WriteTo methods to convert it to JSON.
See the Remarks section for details and examples.

Namespace:  KGySoft.Json
Assembly:  KGySoft.Json (in KGySoft.Json.dll) Version: 2.0.0
Syntax

[SerializableAttribute]
public readonly struct JsonValue : IEquatable<JsonValue>

The JsonValue type exposes the following members.

Constructors

  NameDescription
Public methodJsonValue(Boolean)
Initializes a new JsonValue struct that represents a boolean value. An implicit conversion from the bool type also exists.
Public methodJsonValue(Double)
Initializes a new JsonValue struct that represents a number. An implicit conversion from the double type also exists. Some .NET numeric types such as long and decimal are not recommended to be encoded as JSON numbers. Use the ToJson extension methods if you still want to do so.
See the Remarks section for details.
Public methodJsonValue(JsonArray)
Initializes a new JsonValue struct that represents an array.
Public methodJsonValue(JsonObject)
Initializes a new JsonValue struct that represents an object.
Public methodJsonValue(String)
Initializes a new JsonValue struct that represents a string. An implicit conversion from the string type also exists.
Top
Properties

  NameDescription
Public propertyAsArray
Gets this JsonValue instance as a JsonArray if it has Array Type; or , if its Type is not Array.
See the Remarks section for details.
Public propertyAsBoolean
Gets the bool value of this JsonValue instance if it has Boolean Type; or , if its Type is not Boolean. To interpret other types as boolean you can use the AsBoolean(JsonValue, JsonValueType) extension method instead.
Public propertyAsLiteral
If this JsonValue represents a primitive JavaScript type (Undefined, Null, Boolean, Number, String) or its Type is UnknownLiteral, then gets the underlying string literal; otherwise, gets .
Public propertyAsNumber
Gets the numeric value of this JsonValue instance if it has Number Type; or , if its Type is not Number. The returned value is a double to be conform with JSON Number type. To retrieve the actual stored raw value use the AsLiteral property. To retrieve the value as .NET numeric types use the methods in the JsonValueExtensions class.
See the Remarks section for details.
Public propertyAsObject
Gets this JsonValue instance as a JsonObject if it has Object Type; or , if its Type is not Object.
See the Remarks section for details.
Public propertyAsString
Gets the string value of this JsonValue instance if it has String Type; or , if its Type is not String.
See the Remarks section for details.
Public propertyIsNull
Gets whether this JsonValue instance has Null Type and equals to the Null instance.
Public propertyIsUndefined
Gets whether this JsonValue instance has Undefined Type and equals to the Undefined instance.
Public propertyItemInt32
If the type of this JsonValue is Array and arrayIndex is within the valid bounds, then gets the value at the specified arrayIndex; otherwise, returns Undefined. Just like in JavaScript, using an invalid index returns Undefined.
Public propertyItemString
If the type of this JsonValue is Object and propertyName denotes an existing property, then gets the value of the specified propertyName; otherwise, returns Undefined.
Public propertyType
Gets the JavaScript type of this JsonValue.
Top
Methods

  NameDescription
Public methodStatic memberCreateLiteralUnchecked
Creates a JsonValue that forcibly treats value as a JSON literal, even if it is invalid in JSON.
See the Remarks section for details.
Public methodStatic memberCreateNumberUnchecked
Creates a JsonValue that forcibly treats value as a JSON number, even if it cannot be represented as a valid number in JavaScript.
See the Remarks section for details.
Public methodEquals(JsonValue)
Indicates whether the current JsonValue instance is equal to another one specified in the other parameter.
Public methodEquals(Object)
Determines whether the specified object is equal to this instance. Allows comparing also to JsonArray, JsonObject, string, bool and .NET numeric types.
(Overrides ValueTypeEquals(Object).)
Public methodGetHashCode
Returns a hash code for this JsonValue instance.
(Overrides ValueTypeGetHashCode.)
Public methodStatic memberParse(String)
Reads a JsonValue from a string that contains JSON data.
Public methodStatic memberParse(TextReader)
Reads a JsonValue from a TextReader that contains JSON data.
Public methodStatic memberParse(Stream, Encoding)
Reads a JsonValue from a Stream that contains JSON data.
Public methodToString
Returns a minimized JSON string that represents this JsonValue.
(Overrides ValueTypeToString.)
Public methodToString(String)
Returns a JSON string that represents this JsonValue.
Public methodStatic memberTryParse(String, JsonValue)
Tries to read a JsonValue from a string that contains JSON data.
Public methodStatic memberTryParse(TextReader, JsonValue)
Tries to read a JsonValue from a TextReader that contains JSON data.
Public methodStatic memberTryParse(Stream, JsonValue, Encoding)
Tries to read a JsonValue from a Stream that contains JSON data.
Public methodWriteTo(StringBuilder, String)
Writes this JsonValue instance into a StringBuilder.
Public methodWriteTo(TextWriter, String)
Writes this JsonValue instance into a TextReader.
Public methodWriteTo(Stream, Encoding, String)
Writes this JsonValue instance into a Stream using the specified encoding.
Top
Operators

  NameDescription
Public operatorStatic memberEquality
Determines whether two specified JsonValue instances have the same value.
Public operatorStatic member(JsonValue to JsonArray)
Performs an explicit conversion from JsonValue to JsonArray. The conversion succeeds if the Type property is Null or Array; otherwise, an InvalidCastException is thrown.
Public operatorStatic member(JsonValue to JsonObject)
Performs an explicit conversion from JsonValue to JsonObject. The conversion succeeds if the Type property is Null or Object; otherwise, an InvalidCastException is thrown.
Public operatorStatic member(JsonValue to NullableBoolean)
Performs an explicit conversion from JsonValue to nullable bool. The conversion succeeds if the Type property is Null or Boolean; otherwise, an InvalidCastException is thrown.
Public operatorStatic member(JsonValue to NullableDouble)
Performs an explicit conversion from JsonValue to nullable double. The conversion succeeds if the Type property is Null or Number; otherwise, an InvalidCastException is thrown.
Public operatorStatic member(JsonValue to String)
Performs an explicit conversion from JsonValue to string. The conversion succeeds if the Type property is Null or String; otherwise, an InvalidCastException is thrown.
Public operatorStatic member(Boolean to JsonValue)
Performs an implicit conversion from bool to JsonValue.
Public operatorStatic member(Double to JsonValue)
Performs an implicit conversion from double to JsonValue.
Public operatorStatic member(Int32 to JsonValue)
Performs an implicit conversion from int to JsonValue.
Public operatorStatic member(Int64 to JsonValue) Obsolete.
Performs an implicit conversion from long to JsonValue. This operator exists only to produce a warning because otherwise the implicit conversion from double would also match Int64 values.
Public operatorStatic member(JsonArray to JsonValue)
Performs an implicit conversion from JsonArray to JsonValue.
Public operatorStatic member(JsonObject to JsonValue)
Performs an implicit conversion from JsonObject to JsonValue.
Public operatorStatic member(NullableBoolean to JsonValue)
Performs an implicit conversion from nullable bool to JsonValue.
Public operatorStatic member(NullableDouble to JsonValue)
Performs an implicit conversion from nullable double to JsonValue.
Public operatorStatic member(NullableInt32 to JsonValue)
Performs an implicit conversion from nullable int to JsonValue.
Public operatorStatic member(NullableInt64 to JsonValue) Obsolete.
Performs an implicit conversion from nullable long to JsonValue. This operator exists only to produce a warning because otherwise the implicit conversion from double would also match Int64 values.
Public operatorStatic member(NullableUInt32 to JsonValue)
Performs an implicit conversion from nullable uint to JsonValue.
Public operatorStatic member(NullableUInt64 to JsonValue) Obsolete.
Performs an implicit conversion from nullable ulong to JsonValue. This operator exists only to produce a warning because otherwise the implicit conversion from double would also match UInt64 values.
Public operatorStatic member(String to JsonValue)
Performs an implicit conversion from string to JsonValue.
Public operatorStatic member(UInt32 to JsonValue)
Performs an implicit conversion from uint to JsonValue.
Public operatorStatic member(UInt64 to JsonValue) Obsolete.
Performs an implicit conversion from ulong to JsonValue. This operator exists only to produce a warning because otherwise the implicit conversion from double would also match UInt64 values.
Public operatorStatic memberInequality
Determines whether two specified JsonValue instances have different values.
Top
Fields

  NameDescription
Public fieldStatic memberFalse
Represents the JavaScript false value. The Type of the value is Boolean.
Public fieldStatic memberNull
Represents the JavaScript null value. The Type of the value is also Null.
Public fieldStatic memberTrue
Represents the JavaScript true value. The Type of the value is Boolean.
Public fieldStatic memberUndefined
Represents the JavaScript undefined value. The Type of the value is also Undefined. This is the value of a default JsonValue instance.
Top
Remarks

A JsonValue instance represents any JavaScript type that can appear in JSON (see also the Type property and the JsonValueType enumeration), including the Undefined value, which is not valid in a JSON document.

The default value of a JsonValue instance equals to the Undefined field, which represents the undefined type in JavaScript. Just like in JavaScript, you can add Undefined values to arrays and objects but when you "stringify" them by the ToString or WriteTo methods, they will be either replaced with Null (in a JsonArray) or simply ignored (in a JsonObject).

C#
JsonValue value = default; // = JsonValue.Undefined; = new JsonValue();

Console.WriteLine(value); // undefined
Console.WriteLine(value.Type); // Undefined
Console.WriteLine(value.IsUndefined); // True
Console.WriteLine(value == JsonValue.Undefined); // True
Note Note
ToString and WriteTo behave differently for a standalone Undefined value. Whereas ToString produces the string undefined, WriteTo will not write anything into the output.

The Null field represents the null type in JavaScript. Conversion from a .NET is also possible but only with an explicit type cast.

C#
JsonValue value = JsonValue.Null; // = (string)null; = (bool?)null; = (JsonObject)null; etc.

Console.WriteLine(value); // null
Console.WriteLine(value.Type); // Null
Console.WriteLine(value.IsNull); // True
Console.WriteLine(value == JsonValue.Null); // True

A JsonValue can also store a Boolean value, which can be either the value of the True or False fields. An implicit conversion from the .NET Boolean type also exists.

C#
JsonValue value = true; // = JsonValue.True; = new JsonValue(true);

Console.WriteLine(value); // true
Console.WriteLine(value.Type); // Boolean
Console.WriteLine(value.AsBoolean); // True
Console.WriteLine(value == JsonValue.True); // True
Console.WriteLine(value == true); // True

A JsonValue can also store a Number. Though a JavaScript number is always a double-precision 64-bit binary format IEEE 754 value, which is the same as the .NET Double type, a JsonValue can hold any number with any precision. You can use the AsNumber property to get the same value as JavaScript would also get and the AsLiteral property, which returns the actual value as it will be dumped when converting the value to JSON. An implicit conversion from the .NET Double type also exists.

Caution note Caution
It is not recommended to write wide numeric .NET types (Int64, Decimal, etc.) as a JSON Number because when processed by JavaScript, the precision of these values might be lost without any warning. If you are sure that you want to store such values as numbers, use the CreateNumberUnchecked(String) method or the JsonValueExtensions.ToJson overloads with asString: false parameter.
C#
JsonValue value = 1.25; // = new JsonValue(1.25);

Console.WriteLine(value); // 1.25
Console.WriteLine(value.Type); // Number
Console.WriteLine(value.AsNumber); // 1.25

// Using a long value beyond double precision
long longValue = (1L << 53) + 1;
value = longValue; // this produces a compile-time warning about possible loss of precision
Console.WriteLine(value); // 9007199254740993
Console.WriteLine($"{value.AsNumber:R}"); // 9007199254740992 - this is what JavaScript will see
Console.WriteLine(value.AsLiteral); // 9007199254740993 - this is the actual stored value

value = longValue.ToJson(asString: false); // this is how the compile-time warning can be avoided
Console.WriteLine(value); // 9007199254740993

value = longValue.ToJson(); // this is the recommended solution to prevent losing precision
Console.WriteLine(value); // "9007199254740993" - note that ToJson produces strings for wide numeric types by default

A JsonValue can also store a String. An implicit conversion from the .NET String type also exists.

C#
JsonValue value = "some \"value\""; // = new JsonValue("some \"value\"");

Console.WriteLine(value); // "some \"value\""
Console.WriteLine(value.Type); // String
Console.WriteLine(value.AsString); // some "value"

A JsonValue can also store an Array, which is represented by the JsonArray type. An implicit conversion from JsonArray also exists. If a JsonValue represents an array, then you can use the int indexer to access the array elements. Just like in JavaScript, accessing an invalid index returns Undefined. To change array values use the JsonArray instance returned by the AsArray property.

C#
JsonValue value = new JsonArray { true, 1, 2.35, JsonValue.Null, "value" };
// which is the shorthand of: new JsonValue(new JsonArray { JsonValue.True, new JsonValue(1), new JsonValue(2.35), JsonValue.Null, new JsonValue("value") });

Console.WriteLine(value); // [true,1,2.35,null,"value"]
Console.WriteLine(value.Type); // Array
Console.WriteLine(value[2]); // 2.35
Console.WriteLine(value[42]); // undefined

A JsonValue can also store an Object, which is represented by the JsonObject type. An implicit conversion from JsonObject also exists. If a JsonValue represents an object, then you can use the string indexer to access the properties by name. Just like in JavaScript, accessing a nonexistent property returns Undefined. To change object properties use the JsonObject instance returned by the AsObject property.

C#
JsonValue value = new JsonObject
{
    ("Bool", true), // which is the shorthand of new JsonProperty("Bool", new JsonValue(true))
    // { "Bool", true }, // alternative syntax on platforms where ValueTuple types are not available
    ("Number", 1.23),
    ("String", "value"),
    ("Object", new JsonObject
    {
       ("Null", JsonValue.Null),
       ("Array", new JsonArray { 42 }),
    })
};

Console.WriteLine(value); // {"Bool":true,"Number":1.23,"String":"value","Object":{"Null":null,"Array":[42]}}
Console.WriteLine(value.Type); // Object
Console.WriteLine(value["Object"]); // {"Null":null,"Array":[42]}
Console.WriteLine(value["Object"]["Array"]); // [42]
Console.WriteLine(value["Object"]["Array"][0]); // 42
Console.WriteLine(value["UnknownProperty"]); // undefined

Tip Tip
JsonValue members provide conversions to and from types that have their counterpart in JavaScript. If you want to treat the JSON Number and String types as specific .NET types such as Int64, Decimal, Enum, DateTime, Guid and more, then use the extension methods of the JsonValueExtensions class. It also has a bunch of ToJson methods that can convert the common .NET types to JsonValue.
Examples

The following examples demonstrate how to serialize and deserialize objects to and from JsonValue.

Note Note
KGySoft.Json has no built-in automated ways to serialize and deserialize C# types. But in practice even when using other serializers you either need to decorate the C# classes with attributes or you have to define converters where you specify the exact mapping because .NET classes and JSON objects usually cannot be just automatically mapped to each other if they both follow the usual naming conventions. The following examples show how to manually do the serialization and deserialization, which actually can be more effective than letting a serializer resolve attributes or expressions by reflection.

Consider the following JSON document:

JSON
{
  "id": "a4a5b192-fac9-4d7c-a826-1653761fe200",
  "firstName": "John",
  "lastName": "Smith",
  "birth": "19780611",
  "active": true,
  "lastLogin": 1579955315,
  "status": "fully-trusted",
  "balances": [
    {
      "currency": "USD",
      "balance": "23462.4527"
    },
    {
      "currency": "BTC",
      "balance": "0.0567521461"
    }
  ]
}

And the corresponding C# model:

C#
public class Account
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string? MiddleName { get; set; } // optional
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; } // yyyyMMdd
    public bool IsActive { get; set; }
    public DateTime? LastLogin { get; set; } // Stored as Unix seconds
    public AccountStatus Status { get; set; } // Stored as lowercase values with hyphens
    public IList<AccountBalance> Balances { get; } = new Collection<AccountBalance>();
}

public enum AccountStatus { Basic, Confirmed, FullyTrusted, Disabled } // It follows the .NET naming conventions

public class AccountBalance
{
   public string Currency { get; set; }
   public decimal Balance { get; set; }
}

Serialization: When serializing, we just build a JsonValue. The actual serialization is done by the ToString or WriteTo methods.

C#
// in this example conversion is separated from the model as an extension method
public static JsonValue ToJson(this Account acc)
{
    var result = new JsonObject
    {
        ("id", acc.Id.ToJson()), // using ToJson(Guid)
        ("firstName", acc.FirstName), // implicit conversion from string
        ("lastName", acc.LastName), // implicit conversion from string
        ("birth", acc.DateOfBirth.ToJson("yyyyMMdd")), // ToJson(DateTime, string) with exact format
        ("active", acc.IsActive), // implicit conversion from bool
        ("lastLogin", acc.LastLogin.ToJson(JsonDateTimeFormat.UnixSeconds, asString: false)), // Unix seconds as number
        ("status", acc.Status.ToJson(JsonEnumFormat.LowerCaseWithHyphens)) // using ToJson<TEnum>()
    };

    // adding the optional middle name (it wasn't defined in the example JSON above)
    if (acc.MiddleName != null)
        result.Add("middleName", acc.MiddleName); // or: result["middleName"] = acc.MiddleName;

    // for collections and nested objects we can delegate the job to other extension methods
    if (acc.Balances.Count > 0)
        result["balances"] = new JsonArray(acc.Balances.Select(b => b.ToJson()));

    return result; // now it will be converted to JsonValue but we can also change the return type to JsonObject
}

public static JsonValue ToJson(this AccountBalance balance) => new JsonObject
{
    ("currency", balance.Currency), // implicit conversion from string
    ("balance", balance.Balance.ToJson(asString: true)), // the default of ToJson(decimal) would be a string anyway
};

Deserialization: Once you have a parsed JsonValue (see the Parse and TryParse methods), retrieving the values becomes very straightforward:

C#
// as above, this is now an extension method but could be even a constructor with JsonValue or JsonObject parameter
public static Account ToAccount(this JsonValue json)
{
    // Here we mainly use the As... methods that return null if the conversion fails
    // but you can also use the TryGet... or Get...OrDefault methods.
    var result = new Account
    {
        Id = json["id"].AsGuid() ?? throw new ArgumentException("'id' is missing or invalid"),
        FirstName = json["firstName"].AsString ?? throw new ArgumentException("'firstName' is missing or invalid"),
        MiddleName = json["middleName"].AsString, // simply returns null if missing (json["middleName"].IsUndefined)
        LastName = json["lastName"].AsString ?? throw new ArgumentException("'lastName' is missing or invalid"),
        DateOfBirth = json["birth"].AsDateTime("yyyyMMdd") ?? throw new ArgumentException("'birth' is missing or invalid"),
        IsActive = json["active"].GetBooleanOrDefault(false), // it will be false if missing (could be AsBoolean ?? false)
        LastLogin = json["lastLogin"].AsDateTime(JsonDateTimeFormat.UnixSeconds), // will be null if missing or invalid
        Status = json["status"].GetEnumOrDefault(true, AccountStatus.Disabled) // true to ignore case and hyphens
    };

    var balances = json["balances"];

    // a missing 'balances' is accepted
    if (balances.IsUndefined) // or: balances == JsonValue.Undefined
        return result;

    // but if exists, must be an array
    if (balances.Type != JsonValueType.Array) // or: balances.AsArray is not JsonArray
        throw new ArgumentException("'balances' is invalid");

    foreach (JsonValue balance in balances.AsArray)
        result.Balances.Add(balance.ToBalance());

    return result;
}

public static AccountBalance ToBalance(this JsonValue json) => new AccountBalance
{
    Currency = json["currency"].AsString ?? throw new ArgumentException("'currency' is missing or invalid"),
    Balance = json["balance"].AsDecimal() ?? 0m // or AsDecimal(JsonValueType.String) to disallow JSON numbers
};

See Also

Reference