In this section · 01 Turn a document into codeLanguages
01 · Turn a document into code
The other nine languages
Python, JSON Schema, Java, C#, Go, Zod, Rust, Kotlin and Swift from one document, and what each does with it.
Generate writes ten languages. TypeScript interfaces from a JSON API response has a page of its own; this one runs the other nine over one small document, so that the only thing changing from block to block is the language. On the page, the tabs show TypeScript, Python, JSON Schema, Java and C#, and More holds Go, Zod, Rust, Kotlin and Swift. It is part of the Generate section.
The document is two books, books.json, and each of its fields tests something a language has to decide. Every book has an id, a whole number. The prices are 9.5 and 7, so the field holds a decimal even though one value looks whole. Both have a list of tags, one of them empty. Only Emma has a series, so that field is optional. And the document is itself a list, so each language also has to say what the whole file is.
Every block below uses the options at their defaults, and the steps name each one, because all of them are remembered from your last visit.
Python
Input
books.json
[
{ "id": 1, "title": "Dune", "price": 9.5, "tags": ["sf"] },
{ "id": 2, "title": "Emma", "price": 7, "tags": [], "series": "Classics" }
]Do
- Set From to JSON, and under Language choose Python.
- In Options, under Python, set Style to Pydantic v2.
- Paste the input into the source pane, and type
Booksinto Root name, since a paste has no file name to take it from.
Result
# Generated by myjsoneditor.com — Python 3.11+
# Requires pydantic>=2
from pydantic import BaseModel
class Book(BaseModel):
id: int
title: str
price: float
tags: list[str]
series: str | None = None
Books = list[Book]Python splits the numbers the way the data does: id only ever held whole numbers, so it is an int, while price held 9.5, so it is a float. A list mixing the two would be a list of floats. The optional series becomes str | None = None, which lets the key be missing. Style switches the classes from Pydantic models to plain dataclasses or TypedDicts, and the comment at the top says which Python and which Pydantic the code expects.
JSON Schema
Input
books.json
[
{ "id": 1, "title": "Dune", "price": 9.5, "tags": ["sf"] },
{ "id": 2, "title": "Emma", "price": 7, "tags": [], "series": "Classics" }
]Do
- Set From to JSON, and under Language choose JSON Schema.
- In Options, under JSON Schema, set Draft to 2020-12, and leave allow extra properties unticked.
- Paste the input into the source pane, and type
Booksinto Root name, since a paste has no file name to take it from.
Result
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "array",
"items": {
"$ref": "#/$defs/Book"
},
"$defs": {
"Book": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "string"
},
"price": {
"type": "number"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"series": {
"type": "string"
}
},
"required": [
"id",
"title",
"price",
"tags"
],
"additionalProperties": false
}
}
}JSON Schema describes the data rather than a program’s types. The book is defined once under $defs and referred to from the list. A field joins required only when every record had it, which is why series is missing from it, and no other keys are allowed until allow extra properties is ticked. The id is an integer, a distinction JSON Schema can make and TypeScript cannot. What to do with a schema once you have one is the subject of A JSON Schema, and checking the next file.
Java
Input
books.json
[
{ "id": 1, "title": "Dune", "price": 9.5, "tags": ["sf"] },
{ "id": 2, "title": "Emma", "price": 7, "tags": [], "series": "Classics" }
]Do
- Set From to JSON, and under Language choose Java.
- In Options, under Java, set Shape to Record (Java 17+) and Package to
generated, and tick Ignore unknown properties. - Paste the input into the source pane, and type
Booksinto Root name, since a paste has no file name to take it from.
Result
// Generated by myjsoneditor.com for Jackson 2.15+ (Java 17+).
// A missing key and a null both read as null; a primitive (long, double, boolean) is never
// absent or null in the document, so it has no null to hold.
package generated;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.util.List;
@JsonIgnoreProperties(ignoreUnknown = true)
record Book(
long id,
String title,
double price,
List<String> tags,
String series
) {}
// Root: List<Book> — read with mapper.readValue(json, new TypeReference<List<Book>>() {})Java gets a record per shape, annotated for Jackson. A field every book has and that was never null uses a primitive, long or double, and the optional series is a String that may be null, as the comment at the top explains. Java has no type alias, so the list the file holds is described in a closing comment, with the line that reads it. Shape swaps the records for classes with getters and setters, for code that predates Java 17, and Ignore unknown properties lets a later field in the response through without failing.
C#
Input
books.json
[
{ "id": 1, "title": "Dune", "price": 9.5, "tags": ["sf"] },
{ "id": 2, "title": "Emma", "price": 7, "tags": [], "series": "Classics" }
]Do
- Set From to JSON, and under Language choose C#.
- In Options, under C#, set Shape to record and Namespace to
Generated. - Paste the input into the source pane, and type
Booksinto Root name, since a paste has no file name to take it from.
Result
// Generated by myjsoneditor.com for System.Text.Json (.NET 8).
#nullable enable
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Generated;
public sealed record Book
{
[JsonPropertyName("id")]
public required long Id { get; init; }
[JsonPropertyName("title")]
public required string Title { get; init; }
[JsonPropertyName("price")]
public required double Price { get; init; }
[JsonPropertyName("tags")]
public required List<string> Tags { get; init; }
[JsonPropertyName("series")]
public string? Series { get; init; }
}
// Books: List<Book> — JsonSerializer.Deserialize<List<Book>>(json)C# gets a sealed record for System.Text.Json, with the property names in PascalCase and the JSON names kept in attributes. Every field the sample always had is marked required, so a response without it fails to deserialise rather than leaving a default behind, and the optional series is a nullable string?. As in Java, the list at the top level becomes a comment showing the call to make. Namespace sets the namespace line, and Shape chooses between a record and a class.
Go
Input
books.json
[
{ "id": 1, "title": "Dune", "price": 9.5, "tags": ["sf"] },
{ "id": 2, "title": "Emma", "price": 7, "tags": [], "series": "Classics" }
]Do
- Set From to JSON, then open More and choose Go.
- In Options, under Go, set Package to
main, and Optional fields to its first choice,*T, omitempty. - Paste the input into the source pane, and type
Booksinto Root name, since a paste has no file name to take it from.
Result
package main
type Book struct {
ID int64 `json:"id"`
Title string `json:"title"`
Price float64 `json:"price"`
Tags []string `json:"tags"`
Series *string `json:"series,omitempty"`
}
type Books []BookGo needs exported field names, so title becomes Title and a struct tag carries the JSON name back; id becomes ID, following Go’s habit for initialisms. By default the optional series is a pointer with omitempty, which is how Go tells a missing value from an empty string. Optional fields can drop the pointer and keep the tag, which is shorter and loses that difference. The file is one named slice type, and Package sets the first line.
Zod
Input
books.json
[
{ "id": 1, "title": "Dune", "price": 9.5, "tags": ["sf"] },
{ "id": 2, "title": "Emma", "price": 7, "tags": [], "series": "Classics" }
]Do
- Set From to JSON, then open More and choose Zod.
- In Options, under Zod, tick both import line and z.infer type exports.
- Paste the input into the source pane, and type
Booksinto Root name, since a paste has no file name to take it from.
Result
import { z } from "zod";
export const BookSchema = z.object({
id: z.number().int(),
title: z.string(),
price: z.number(),
tags: z.array(z.string()),
series: z.string().optional(),
});
export const BooksSchema = z.array(BookSchema);
export type Book = z.infer<typeof BookSchema>;
export type Books = z.infer<typeof BooksSchema>;Zod brings the check into TypeScript itself: it tests data while your program runs, rather than only describing it to the compiler. Each schema is a value you can call parse on, and a response that does not match throws. The whole-number id is checked with .int(), and the optional series with .optional(). The types at the bottom are inferred from the schemas, so there is one source of truth. Untick z.infer type exports to leave them out, or import line when your file imports Zod already.
Rust
Input
books.json
[
{ "id": 1, "title": "Dune", "price": 9.5, "tags": ["sf"] },
{ "id": 2, "title": "Emma", "price": 7, "tags": [], "series": "Classics" }
]Do
- Set From to JSON, then open More and choose Rust.
- In Options, under Rust, leave Distinguish null from absent (serde_with) unticked.
- Paste the input into the source pane, and type
Booksinto Root name, since a paste has no file name to take it from.
Result
// Generated by myjsoneditor.com for serde and serde_json (Rust 2021).
// Cargo.toml: serde = { version = "1", features = ["derive"] }, serde_json = "1"
// An absent key and a null both read as None. Turn on "Distinguish null from absent" to keep them apart.
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Book {
pub id: i64,
pub title: String,
pub price: f64,
pub tags: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub series: Option<String>,
}
pub type Books = Vec<Book>;Rust gets structs that derive serde’s traits, with the dependencies to add named in a comment. The optional series is an Option<String> with a serde attribute, so a missing key reads as None and None is left out when the struct is written back. A missing key and a null both read as None here. When your API gives the two different meanings, tick Distinguish null from absent (serde_with), and the field keeps them apart at the cost of a second crate.
Kotlin
Input
books.json
[
{ "id": 1, "title": "Dune", "price": 9.5, "tags": ["sf"] },
{ "id": 2, "title": "Emma", "price": 7, "tags": [], "series": "Classics" }
]Do
- Set From to JSON, then open More and choose Kotlin.
- In Options, under Kotlin, tick Serialization annotations and set Package to
generated. - Paste the input into the source pane, and type
Booksinto Root name, since a paste has no file name to take it from.
Result
// Generated by myjsoneditor.com for kotlinx.serialization (Kotlin 2.0+).
// Decode with the default Json: explicitNulls = true, so a `T?` with no default is a
// required key that may be null, and `T? = null` is a key that may be absent.
package generated
import kotlinx.serialization.Serializable
@Serializable
data class Book(
val id: Long,
val title: String,
val price: Double,
val tags: List<String>,
val series: String? = null,
)
typealias Books = List<Book>Kotlin gets data classes for kotlinx.serialization. The optional series is written String? = null: the question mark lets it hold null, and the default lets the key be missing, which the comment at the top spells out because the library treats the two separately. Whole numbers are Long and decimals Double, and the file is a typealias for a list of books. Serialization annotations can be turned off for plain data classes, and Package sets the package line.
Swift
Input
books.json
[
{ "id": 1, "title": "Dune", "price": 9.5, "tags": ["sf"] },
{ "id": 2, "title": "Emma", "price": 7, "tags": [], "series": "Classics" }
]Do
- Set From to JSON, then open More and choose Swift.
- In Options, under Swift, leave Hashable, Sendable unticked.
- Paste the input into the source pane, and type
Booksinto Root name, since a paste has no file name to take it from.
Result
// Generated by myjsoneditor.com for Codable (Swift 5.9+).
// A missing key and a null both decode as nil, and nil encodes as a missing key.
struct Book: Codable {
let id: Int
let title: String
let price: Double
let tags: [String]
let series: String?
}
typealias Books = [Book]Swift gets structs that conform to Codable, the standard library’s encoding protocol, so JSONDecoder reads them with no extra code. The optional series is a String?, which decodes a missing key and a null alike as nil. Whole numbers are Int and decimals Double, and the file is a typealias for an array of books. Hashable, Sendable adds those two conformances, for values you will put in a set or pass between concurrent tasks.
Choosing between them
The language is usually settled by the code base, so the options are where the choice lies. Three of them matter most. The first is how a missing field is spelled, which differs between a key that may be absent and a key that may be null. The second is how a whole number is held apart from a decimal, which every target on this page does and TypeScript, with its one number type, cannot. The third is whether the output is only a type or also a check that runs.
Whichever you choose, the settings stay with you across visits, and the editor’s Generate panel shares them. Open Generate to try a document of your own, or Open the editor to type one you already have open.