Draft C# classes from a representative JSON sample, then review the inferred types, the verbatim keyword names and the [JsonPropertyName] mappings before compiling.
Runs locally in your browserPaste a JSON sample, an API response, a configuration file or a webhook body, and this page writes the C# classes that match it: one class per object shape, with public properties and { get; set; } accessors, all in a single file you can copy. The conversion runs in your browser, the payload is never uploaded, and the page keeps working offline once it has loaded.
Every property name is a legal C# identifier: a key that collides with a keyword becomes a verbatim identifier (@class), and a key holding characters C# rejects is rebuilt while the original key is kept in a [JsonPropertyName] attribute so System.Text.Json still maps the payload. Types, class names and nesting are inferred from the sample you pasted, which is why the result is a draft to review rather than a finished model.
Numbers are typed by range: an integer that fits in Int32 becomes int, a larger whole number becomes long up to 2^53, and anything fractional or beyond that becomes double, so 2147483648 and 1e10 no longer land in an int. true and false become bool, and a value that is null in every sample becomes string. A string stays string unless it is shaped like a date and parses as one: 2024-05-06, 2024/05/06 10:00, 12/31/2024 and May 6, 2024 become DateTime, while 20240506, 1-2, R2D2 and numeric ids keep their exact text.
When one field carries different value types across the elements of an array, the wider numeric type wins (int with long becomes long, int with 2.5 becomes double) and any other disagreement becomes object. A field that is null in one element and typed in another takes the type of the non-null element, so one null does not erase what the other rows show.
The property keeps the JSON key wherever C# allows it. A key that is a reserved word is written as a verbatim identifier, so class becomes @class, which compiles and still maps the key of the same name. A key with characters C# rejects is rebuilt: user-name becomes userName, first name becomes firstName, 1st becomes _1st, a.b becomes aB, and an empty key becomes value. Two keys that collapse onto the same identifier get a numeric suffix instead of overwriting each other, so a.b and a&b end up as aB and aB2.
Whenever a property name differs from its JSON key, the property carries [JsonPropertyName("...")] and the file gains a using System.Text.Json.Serialization; the attribute is omitted when it would only repeat the property name, and a keyword key such as class needs none because the verbatim name already matches. That attribute is the only configuration written into the file: no serializer options, no schema, no annotations for keys that did not change.
Each distinct object shape becomes one class, named after its JSON key in PascalCase and never repeated inside the file: a second data becomes Data2, and a nested data inside wrap becomes WrapData, so two branches can no longer both claim Data. A class is also never named after the types the file itself uses (List, String, Object, DateTime), because that name would shadow them, and a member is never allowed to repeat the name of its own class.
Arrays are read as a whole rather than from their first element: [1, 2, 3] becomes List<int>, [1, 2.5] becomes List<double>, an array of objects becomes a list of one merged class holding every key that appears in any element, and an array that mixes objects with scalars becomes List<object>. An empty array becomes List<string> as a placeholder. The root object is always called Root; an array at the root is noted in a comment and modelled from its merged elements, and a root that is a scalar produces an empty class with a comment saying no properties could be inferred.
The output is a single C# source file: a generated-by comment with the timestamp, the using directives the file actually needs, and then the classes. It is plain C# with { get; set; } properties, no constructor and no namespace, so it drops into any project from .NET Core 3.0 onwards; nothing here compiles it, the page only writes the text.
The types come from the one sample you paste, so a field that is always 0 in the sample is typed int even if production sends a decimal, and an array that happens to be empty is typed List<string>. Treat the result as a draft: check the types you rely on, rename the root class as you like, and keep the JSON comments in the sample, because they are the only part of the input that becomes documentation.