# How Neo.Json Implements JSON Serialization for Blockchain Entities and Contract Parameters

> Learn how Neo.Json serializes blockchain entities and contract parameters deterministically using a JToken hierarchy and ToJson methods for efficient RPC and smart contract communication.

- Repository: [The Neo Project/neo](https://github.com/neo-project/neo)
- Tags: internals
- Published: 2026-03-08

---

**Neo.Json uses a lightweight JToken hierarchy where blockchain entities like `Transaction` and `ContractParameter` implement `ToJson()` methods that assemble `JObject` instances, enabling deterministic, dependency-free JSON serialization for RPC responses and smart contract interactions.**

The `neo-project/neo` repository provides a custom JSON handling layer in the `Neo.Json` namespace. This infrastructure enables **JSON serialization for blockchain entities and contract parameters** by allowing each domain type to convert itself into a token-based representation. The resulting `JObject` graphs can be written to strings via `JToken.ToString()` or transmitted directly over the wire.

## Core JSON Infrastructure in Neo.Json

The serialization system rests on an abstract `JToken` hierarchy that mirrors the JSON data model. These classes handle parsing, property storage, and recursive writing without external dependencies.

### JToken Base Class

Located in [[`src/Neo.Json/JToken.cs`](https://github.com/neo-project/neo/blob/main/src/Neo.Json/JToken.cs)](https://github.com/neo-project/neo/blob/master-n3/src/Neo.Json/JToken.cs), the abstract `JToken` class provides the foundation for all JSON values. It exposes static methods for parsing raw JSON and instance methods for writing to a `Utf8JsonWriter`. The `ToString()` overload enables indented or compact string output, while `ToByteArray()` returns the UTF-8 encoded payload.

### JObject and Property Storage

[[`src/Neo.Json/JObject.cs`](https://github.com/neo-project/neo/blob/main/src/Neo.Json/JObject.cs)](https://github.com/neo-project/neo/blob/master-n3/src/Neo.Json/JObject.cs) represents JSON objects using an `OrderedDictionary<string, JToken?>`. This preserves insertion order during iteration. The class implements an indexer (`obj["name"]`) for property assignment and overrides `Write` to serialize each key-value pair to the underlying writer.

### JArray and Primitive Types

Arrays are handled by [[`src/Neo.Json/JArray.cs`](https://github.com/neo-project/neo/blob/main/src/Neo.Json/JArray.cs)](https://github.com/neo-project/neo/blob/master-n3/src/Neo.Json/JArray.cs), which stores elements in a `List<JToken?>`. Primitive JSON types reside in separate files:

- [`JString.cs`](https://github.com/neo-project/neo/blob/main/JString.cs) for strings
- [`JNumber.cs`](https://github.com/neo-project/neo/blob/main/JNumber.cs) for numeric values
- [`JBoolean.cs`](https://github.com/neo-project/neo/blob/main/JBoolean.cs) for booleans
- [`JNull.cs`](https://github.com/neo-project/neo/blob/main/JNull.cs) for null values

### Utility Helpers

[[`src/Neo.Json/Utility.cs`](https://github.com/neo-project/neo/blob/main/src/Neo.Json/Utility.cs)](https://github.com/neo-project/neo/blob/master-n3/src/Neo.Json/Utility.cs) provides `StrictUTF8` for consistent UTF-8 encoding and the ordered dictionary implementation used by `JObject`.

## Serializing Contract Parameters with Circular Reference Protection

Smart contract arguments use the `ContractParameter` type, which implements a robust `ToJson` method to handle complex nested structures including arrays and maps.

### The ToJson Implementation

In [[`src/Neo/SmartContract/ContractParameter.cs`](https://github.com/neo-project/neo/blob/main/src/Neo/SmartContract/ContractParameter.cs)](https://github.com/neo-project/neo/blob/master-n3/src/Neo/SmartContract/ContractParameter.cs), the `ToJson` method constructs a `JObject` containing a `type` field and an optional `value` field. The implementation distinguishes between primitive types and collections:

```csharp
public JObject ToJson()
{
    return ToJson(this, null);
}

private static JObject ToJson(ContractParameter parameter, HashSet<ContractParameter>? context)
{
    JObject json = new();
    json["type"] = parameter.Type;
    if (parameter.Value != null)
        switch (parameter.Type)
        {
            case ContractParameterType.Signature:
            case ContractParameterType.ByteArray:
                json["value"] = Convert.ToBase64String((byte[])parameter.Value);
                break;
            case ContractParameterType.Boolean:
                json["value"] = (bool)parameter.Value;
                break;
            case ContractParameterType.Integer:
            case ContractParameterType.Hash160:
            case ContractParameterType.Hash256:
            case ContractParameterType.PublicKey:
            case ContractParameterType.String:
                json["value"] = parameter.Value.ToString();
                break;
            case ContractParameterType.Array:
                // Detect circular references
                context ??= [];
                if (!context.Add(parameter)) throw new InvalidOperationException("Circular reference.");
                json["value"] = new JArray(((IList<ContractParameter>)parameter.Value)
                                        .Select(p => ToJson(p, context)));
                context.Remove(parameter);
                break;
            case ContractParameterType.Map:
                // Same guard as Array
                context ??= [];
                if (!context.Add(parameter)) throw new InvalidOperationException("Circular reference.");
                json["value"] = new JArray(((IList<KeyValuePair<ContractParameter, ContractParameter>>)parameter.Value)
                                        .Select(p => {
                                            var item = new JObject();
                                            item["key"]   = ToJson(p.Key, context);
                                            item["value"] = ToJson(p.Value, context);
                                            return item;
                                        }));
                context.Remove(parameter);
                break;
        }
    return json;
}

```

### Handling Complex Types

The method encodes binary data as **base-64** for `ByteArray` and `Signature` types. For `Array` and `Map` types, it recursively processes child parameters while using a `HashSet<ContractParameter>` to detect and prevent **circular references**. This ensures that deeply nested contract arguments serialize safely without infinite loops.

## Serializing Blockchain Entities: The Transaction Example

Full blockchain entities follow the same pattern, aggregating multiple nested objects into a single JSON representation. The `Transaction` class demonstrates this approach for complex payloads.

### Transaction.ToJson Implementation

In [[`src/Neo/Network/P2P/Payloads/Transaction.cs`](https://github.com/neo-project/neo/blob/main/src/Neo/Network/P2P/Payloads/Transaction.cs)](https://github.com/neo-project/neo/blob/master-n3/src/Neo/Network/P2P/Payloads/Transaction.cs), the `ToJson` method constructs a comprehensive JSON object containing transaction metadata, signer information, and witness data:

```csharp
public JObject ToJson(ProtocolSettings settings)
{
    JObject json = new();
    json["hash"]              = Hash.ToString();
    json["size"]              = Size;
    json["version"]           = Version;
    json["nonce"]             = Nonce;
    json["sender"]            = Sender.ToAddress(settings.AddressVersion);
    json["sysfee"]            = SystemFee.ToString();
    json["netfee"]            = NetworkFee.ToString();
    json["validuntilblock"]   = ValidUntilBlock;
    json["signers"]           = Signers.Select(p => p.ToJson()).ToArray();
    json["attributes"]        = Attributes.Select(p => p.ToJson()).ToArray();
    json["script"]            = Convert.ToBase64String(Script.Span);
    json["witnesses"]         = Witnesses.Select(p => p.ToJson()).ToArray();
    return json;
}

```

### Aggregating Nested Objects

The method aggregates **signers**, **attributes**, and **witnesses** by calling their respective `ToJson()` methods and collecting the results into `JArray` instances. Binary data such as the invocation script is encoded as **base-64** to ensure valid JSON output. This hierarchical approach allows entire blocks, transactions, and contract invocation parameters to serialize into complete, protocol-compliant JSON documents.

## Practical Usage Examples

### Example 1: Serializing a Contract Parameter

The following example demonstrates creating a nested array parameter and converting it to JSON:

```csharp
using Neo.SmartContract;
using Neo.Json;

// Create a nested parameter: [ "hello", 42, true ]
var arrayParam = new ContractParameter(ContractParameterType.Array)
{
    Value = new List<ContractParameter>
    {
        new ContractParameter(ContractParameterType.String) { Value = "hello" },
        new ContractParameter(ContractParameterType.Integer) { Value = new System.Numerics.BigInteger(42) },
        new ContractParameter(ContractParameterType.Boolean) { Value = true }
    }
};

JObject json = arrayParam.ToJson();      // ↳ builds a JObject
string pretty = json.ToString(true);      // indented JSON string
Console.WriteLine(pretty);

```

**Result (formatted):**

```json
{
  "type": "Array",
  "value": [
    { "type": "String", "value": "hello" },
    { "type": "Integer", "value": "42" },
    { "type": "Boolean", "value": true }
  ]
}

```

### Example 2: Serializing a Transaction

To serialize a complete transaction with all nested components:

```csharp
using Neo.Network.P2P.Payloads;
using Neo.Json;
using Neo.SmartContract;

// Assume `tx` is an already-deserialized Transaction instance
ProtocolSettings settings = ProtocolSettings.Default;
JObject txJson = tx.ToJson(settings);
string jsonString = txJson.ToString();   // compact representation
Console.WriteLine(jsonString);

```

The output contains the transaction hash, size, signer list, attributes, base-64 script, and witnesses.

## Key Source Files and Their Roles

- **[[`src/Neo.Json/JToken.cs`](https://github.com/neo-project/neo/blob/main/src/Neo.Json/JToken.cs)](https://github.com/neo-project/neo/blob/master-n3/src/Neo.Json/JToken.cs)** – Abstract base class; parsing, writing, and string conversion logic.
- **[[`src/Neo.Json/JObject.cs`](https://github.com/neo-project/neo/blob/main/src/Neo.Json/JObject.cs)](https://github.com/neo-project/neo/blob/master-n3/src/Neo.Json/JObject.cs)** – JSON object implementation; property storage, indexing, and serialization.
- **[[`src/Neo.Json/JArray.cs`](https://github.com/neo-project/neo/blob/main/src/Neo.Json/JArray.cs)](https://github.com/neo-project/neo/blob/master-n3/src/Neo.Json/JArray.cs)** – JSON array implementation.
- **[[`src/Neo.Json/JString.cs`](https://github.com/neo-project/neo/blob/main/src/Neo.Json/JString.cs), [`JNumber.cs`](https://github.com/neo-project/neo/blob/main/JNumber.cs), [`JBoolean.cs`](https://github.com/neo-project/neo/blob/main/JBoolean.cs), [`JNull.cs`](https://github.com/neo-project/neo/blob/main/JNull.cs)](https://github.com/neo-project/neo/tree/master-n3/src/Neo.Json)** – Primitive token types wrapping CLR values.
- **[[`src/Neo.Json/Utility.cs`](https://github.com/neo-project/neo/blob/main/src/Neo.Json/Utility.cs)](https://github.com/neo-project/neo/blob/master-n3/src/Neo.Json/Utility.cs)** – Helper utilities for UTF-8 handling and ordered dictionaries.
- **[[`src/Neo/SmartContract/ContractParameter.cs`](https://github.com/neo-project/neo/blob/main/src/Neo/SmartContract/ContractParameter.cs)](https://github.com/neo-project/neo/blob/master-n3/src/Neo/SmartContract/ContractParameter.cs)** – `ToJson` for contract arguments with circular-reference protection.
- **[[`src/Neo/Network/P2P/Payloads/Transaction.cs`](https://github.com/neo-project/neo/blob/main/src/Neo/Network/P2P/Payloads/Transaction.cs)](https://github.com/neo-project/neo/blob/master-n3/src/Neo/Network/P2P/Payloads/Transaction.cs)** – `ToJson` for full transaction objects aggregating nested representations.

## Summary

- **Type-centric architecture**: Every blockchain entity and contract parameter implements `ToJson()` to assemble `JObject` graphs, ensuring consistent **JSON serialization for blockchain entities and contract parameters** across the Neo platform.
- **Lightweight infrastructure**: The `JToken` hierarchy in `src/Neo.Json` provides parsing, property storage, and UTF-8 writing without external dependencies.
- **Circular reference safety**: `ContractParameter.ToJson` uses a `HashSet<ContractParameter>` context to detect and prevent infinite recursion in nested arrays and maps.
- **Binary data handling**: Byte arrays and transaction scripts are encoded as **base-64** strings to maintain valid JSON output while preserving exact byte sequences.
- **Hierarchical aggregation**: Complex types like `Transaction` aggregate nested `JObject` instances (signers, witnesses) into arrays, producing complete protocol-compliant JSON documents.

## Frequently Asked Questions

### How does Neo.Json prevent infinite recursion when serializing nested contract parameters?

The `ContractParameter.ToJson` method in [`src/Neo/SmartContract/ContractParameter.cs`](https://github.com/neo-project/neo/blob/main/src/Neo/SmartContract/ContractParameter.cs) accepts a `HashSet<ContractParameter>` context parameter. When serializing `Array` or `Map` types, the method attempts to add the current parameter to the set. If the add operation returns false (indicating the object is already present), the method throws an `InvalidOperationException` to break the circular reference. After processing children, the parameter is removed from the set to allow reuse in different branches of the object graph.

### What encoding does Neo.Json use for binary data in JSON output?

Binary data such as byte arrays, signatures, and transaction scripts are encoded using **base-64** encoding. This occurs in `ContractParameter.ToJson` for `ByteArray` and `Signature` types via `Convert.ToBase64String`, and in `Transaction.ToJson` for the `script` field. Base-64 encoding ensures that arbitrary binary data remains valid JSON strings while preserving exact byte sequences for cryptographic verification and script execution.

### Can Neo.Json serialize complex blockchain entities like full transactions?

Yes. The `Transaction.ToJson` method in [`src/Neo/Network/P2P/Payloads/Transaction.cs`](https://github.com/neo-project/neo/blob/main/src/Neo/Network/P2P/Payloads/Transaction.cs) demonstrates serialization of complex entities by aggregating multiple nested objects. It invokes `ToJson()` on each `Signer`, `TransactionAttribute`, and `Witness`, collecting the results into `JArray` instances. The method combines these with primitive fields (hash, size, fees) and base-64 encoded binary data to produce a complete JSON representation suitable for RPC responses and block explorers.

### How do I convert a JObject to a formatted JSON string in Neo?

Call the `ToString(bool indented)` method on any `JToken` instance. Pass `true` for pretty-printed output with line breaks and indentation, or `false` (or omit the parameter) for compact output. Internally, this method writes the token to a `Utf8JsonWriter` and returns the UTF-8 string via `StrictUTF8.GetString`, as implemented in [`src/Neo.Json/JToken.cs`](https://github.com/neo-project/neo/blob/main/src/Neo.Json/JToken.cs).