What this tool does
The converter reads the whole document and builds one type per object shape, with the root type first. All the objects in an array are merged into one type; a key that is missing from some of them or null in some of them becomes nullable: long? or bool? for value types, and string? or Address? for reference types when Nullable reference types is on. With it on, the file starts with #nullable enable and non-nullable reference properties of a class get an initializer (= "" or = new()), so the code compiles without CS8618 warnings. With it off, the output is plain C# 7.3 with a block namespace, for older projects and .NET Framework. Whole numbers become long, numbers written with a decimal point or exponent become double, and a field that mixes them is double. Integers above long become ulong, larger ones decimal, and anything beyond decimal becomes JsonElement for System.Text.Json (which cannot read BigInteger) or BigInteger for Newtonsoft. Arrays become List<T>, values whose kind varies become object, and an object with no keys becomes Dictionary<string, object?>. Every property gets an explicit name attribute, because System.Text.Json matches names case-sensitively by default. A property is renamed with a Value suffix when it would clash with its own type's name (error CS0542) or with a member every object or record already has, such as ToString. For Newtonsoft records whose keys differ only in case (id and ID), each constructor parameter is named too, so Newtonsoft does not mix them up.
Example input and output
{"id": 7, "name": "Ada", "email": null, "roles": ["admin"], "address": {"city": "London", "postcode": "N1"}}#nullable enable
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace MyApp.Models;
public class User
{
[JsonPropertyName("id")]
public long Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; } = "";
[JsonPropertyName("email")]
public object? Email { get; set; } // only null in the sample
[JsonPropertyName("roles")]
public List<string> Roles { get; set; } = new();
[JsonPropertyName("address")]
public Address Address { get; set; } = new();
}
public class Address
{
[JsonPropertyName("city")]
public string City { get; set; } = "";
[JsonPropertyName("postcode")]
public string Postcode { get; set; } = "";
}email was only ever null, so its type is unknown; change object? to string? once you know what it holds.
{"order_id": "A-1042", "total": 42.5, "lines": [{"sku": "TSH-S", "qty": 1}]}#nullable enable
using System.Collections.Generic;
using Newtonsoft.Json;
public record Order(
[property: JsonProperty("order_id")] string OrderId,
[property: JsonProperty("total")] double Total,
[property: JsonProperty("lines")] List<Line> Lines
);
public record Line(
[property: JsonProperty("sku")] string Sku,
[property: JsonProperty("qty")] long Qty
);The property: target puts the attribute on the property the record generates, which both serializers read. Records need C# 9 and .NET 5 or later.
{"tweet_id": 1850000000000000000, "views": 18446744073709551615, "score": 0.97, "attachments": []}#nullable enable
using System.Collections.Generic;
using System.Text.Json.Serialization;
public class Post
{
[JsonPropertyName("tweet_id")]
public long TweetId { get; set; }
[JsonPropertyName("views")]
public ulong Views { get; set; } // exceeds long in the sample
[JsonPropertyName("score")]
public double Score { get; set; }
[JsonPropertyName("attachments")]
public List<object?> Attachments { get; set; } = new(); // only empty arrays in the sample: element type unknown
}1850000000000000000 still fits in long. views is the largest ulong, so it needs ulong. The sample's only attachments array was empty, so convert again with a filled one to type its items.
Common errors and fixes
- Every property is null or 0 after JsonSerializer.Deserialize, with no exception
- System.Text.Json matches property names case-sensitively unless told otherwise, so a property named Name never sees the key name. The generated [JsonPropertyName] attributes fix this; for hand-written classes, set PropertyNameCaseInsensitive = true or use JsonSerializerDefaults.Web, as ASP.NET Core does.
- JsonException: The JSON value could not be converted to System.Int64. Path: $.qty
- The value is a decimal number such as 1.5, or a quoted number such as "3". Change the property to double, or allow quoted numbers with JsonNumberHandling.AllowReadingFromString on the options or as [JsonNumberHandling] on the property.
- CS8618: Non-nullable property 'Name' must contain a non-null value when exiting constructor
- The project has nullable reference types enabled and a string or object property has no value. Generated classes avoid this with initializers; if you remove them, either make the property nullable (string?) or add the required modifier (C# 11).
- CS0542: 'Address': member names cannot be the same as their enclosing type
- A class contains a key with its own name, such as an address object with an address key. The generator renames the property to AddressValue and keeps [JsonPropertyName("address")], so the JSON still maps.
- JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[...]'
- Newtonsoft found an object where the type expects an array: the API returns an object in some responses and a list in others, or wraps the list in an envelope such as {"data": [...]}. Convert a sample of the real envelope, or type the property as object or JToken and check its shape in code.
- The JSON value could not be converted to System.Numerics.BigInteger
- System.Text.Json has no built-in BigInteger support. Integers beyond decimal's range (about 7.9 × 10^28) are therefore generated as JsonElement, which keeps the digits; call GetRawText() and parse it with BigInteger.Parse. Newtonsoft.Json reads BigInteger directly.
Common use cases
- Creating DTOs for an ASP.NET Core controller or minimal API endpoint
- Typing a third-party API response for HttpClient with GetFromJsonAsync<T>()
- Generating Newtonsoft.Json models for an older .NET Framework project
- Modelling configuration or message payloads as immutable records
- Typing webhook payloads for Azure Functions or background workers
Privacy and security notes
- Conversion happens in JavaScript inside this browser tab. Your JSON is not sent to DevPrix, logged or stored.
- Values from the sample are used only to choose types; the generated C# contains your key names but no data.
- Share copies a link that carries the JSON after the # in the URL. Browsers do not send that part to servers, but anyone with the link can read it, so avoid sharing links built from responses that contain personal data.
Frequently asked questions
System.Text.Json or Newtonsoft.Json?
System.Text.Json is built into .NET Core 3.0 and later, and ASP.NET Core uses it by default; choose it for new code. Choose Newtonsoft.Json for .NET Framework projects, or when you depend on its extra features, such as JObject, more forgiving parsing or reading BigInteger. Switching only changes the attribute and the using directive.
Class or record?
Classes with { get; set; } work with every serializer and framework version and can be changed after reading. Positional records (C# 9, .NET 5+) are immutable and compare by value, which suits messages and API responses. Both System.Text.Json and Newtonsoft.Json create them through their constructor.
What changes when I turn off Nullable reference types?
Reference types lose their ? (string instead of string?), #nullable enable and the initializers are left out, and the namespace uses braces instead of the file-scoped form, so the classes compile as C# 7.3 on .NET Framework. Value types still use long? and bool? for values that can be missing or null.
Why does every property have a name attribute, even when the name matches?
Because whether a name matches depends on the serializer's settings. System.Text.Json is case-sensitive by default, so Id does not match id, while ASP.NET Core uses camelCase and ignores case. With an explicit attribute on every property, the mapping is the same in every setup and survives renaming the property.
How should I type dates and money?
They stay string and double, because JSON has no date or decimal type. Change a timestamp to DateTimeOffset (or DateTime), which both serializers parse from ISO 8601, and a price to decimal, which both read without rounding.