JSON (JavaScript Object Notation) has become the de facto standard for data exchange in modern web development. Its simplicity, readability, and versatility have made it an indispensable tool for any developer. Let's figure out how to work with JSON correctly and what pitfalls await us when serializing data.
Why did JSON conquer the world?
In the early 2000s, web developers used XML to exchange data between the client and the server. XML was cumbersome, required a lot of space and time for parsing. In 2001, Douglas Crockford proposed JSON as a lightweight alternative, and this changed the rules of the game.
JSON is based on a subset of JavaScript, making it a natural choice for web applications. But its benefits go far beyond JavaScript: JSON is supported by almost all modern programming languages, from Python to Go, from Java to Rust.
Basics of working with JSON in different languages
In Python, working with JSON is extremely simple thanks to the built-in json module. Serialization of Python objects into a JSON string is performed by the dumps method, and deserialization is performed by loads. It is important to understand that not all Python data types can be directly serialized in JSON. For example, datetime objects, sets, or custom classes require additional processing.
In JavaScript, JSON is a built-in object, and working with it is intuitive. The JSON.stringify method converts JavaScript objects into a string, and JSON.parse parses the JSON string back into an object. However, there are nuances here too: functions, undefined and symbols are lost during serialization, which can lead to unexpected results.
In strongly typed languages such as Java or C#, working with JSON requires the use of special libraries like Jackson, Gson, or System.Text.Json. These libraries provide mapping mechanisms between JSON and class objects, which makes the process type-safe, but requires additional configuration.
Advanced serialization techniques
One common task is the serialization of complex objects that are not supported by JSON directly. In Python, you can create a custom encoder for this, inheriting from JSONEncoder and overriding the default method. This allows you to elegantly process dates, times, Decimal, and even custom classes.
Let's consider a practical example: you have a user object with a registration date and a balance in the form of Decimal. The standard json.dumps will cause a TypeError. The solution is to create a custom encoder that knows how to convert these types into JSON-compatible formats. The date can be converted to an ISO string, and Decimal can be converted to a regular number or string to maintain accuracy.
In JavaScript, you can use the second JSON.stringify parameter — the replacer function, which allows you to transform values before serialization. This is a powerful tool for filtering sensitive data or converting specific types.
Performance and optimization
When it comes to large amounts of data, serialization performance becomes critical. In Python, there are alternatives to the standard json module, such as ujson (UltraJSON) or orjson, which can be several times faster due to the implementation in C.
An important point: a beautifully formatted JSON with indents and line breaks takes up more space and is slower to parse. In production, it is worth using a compact format without unnecessary spaces. In Python, this is achieved by the separators parameters and the absence of indent, in JavaScript — simply by calling JSON.stringify without additional parameters.
JSON streaming is another optimization technique for working with large files. Instead of loading all JSON into memory, you can process it in parts using special libraries like ijson in Python or stream-json in Node.js.

JSON security
JSON can become an attack vector if it is processed incorrectly. One of the classic problems is JSON injection, when an attacker injects malicious data through unprotected fields. Always validate and sanitize incoming data before parsing.
Another danger is that deserializing unreliable data can lead to the execution of arbitrary code. In Python, never use eval to parse JSON, even if it seems like a simple solution. Always use json.loads, which safely handles strings.
Limiting the size of incoming JSON is a mandatory measure to protect against DoS attacks. An attacker can send a huge JSON file that will exhaust the server's memory. Set limits on the size of the request body at the web server or application level.
JSON Schemas and Validation
JSON Schema is a standard for describing the structure of JSON data. It allows you to determine which fields are required, which data types are expected, and which restrictions are imposed on the values. This is especially important for APIs, where clients and servers must agree on the data format.
In Python, the popular jsonschema library allows you to validate JSON against a schema. In JavaScript, you can use Ajv, one of the fastest JSON Schema validators. Input validation saves hours of debugging, preventing incorrect data from entering the system.
Modern tools such as TypeScript or Pydantic take another step forward, allowing you to automatically generate types from JSON Schema or vice versa. This creates a single source of truth for the data structure, reducing the likelihood of a mismatch between the documentation and the actual code.
JSON alternatives
Despite the popularity of JSON, it is not always optimal. For high-load systems, it is worth considering binary formats such as Protocol Buffers, MessagePack, or BSON. They take up less space and are faster to parse, although they sacrifice human readability.
MessagePack is particularly interesting because it is fully compatible with JSON in terms of data types, but uses a binary representation. Switching to MessagePack often does not require changing the application logic, only replacing the serialization library. This can give a productivity gain of up to 50 percent when working with large amounts of data.
YAML is another alternative that sacrifices speed for expressiveness and readability. It is great for configuration files, but it is better not to use it for real-time data transfer due to slower parsing.
Practical patterns and best practices
When designing a JSON API, it is important to follow naming conventions. In JavaScript, it is customary to use camelCase, in Python — snake_case. It is important to choose one style and stick to it throughout the project. Some commands solve this problem by automatically converting names at the boundary between layers.
Nested structures are a double-edged sword. Deeply nested JSON objects are difficult to read and process. If you find that you regularly access data through long chains like data.user.profile.settings.notifications.email, you may want to reconsider the data structure and make it flatter.
API versioning via JSON is a critical practice. Include the version in the JSON itself or use HTTP headers. This will allow you to make breaking changes without breaking existing clients. For example, the version field at the root of the object can indicate what structure to expect.
Working with big data
When JSON files are measured in gigabytes, classic approaches stop working. JSON Lines (JSONL) is a format where each line of the file contains a separate JSON object. This allows you to process the file line by line without loading it entirely into memory.
JSON streaming is also important. Instead of accumulating all the data in memory and the final record, you can write JSON as the data is generated. This is especially true for exporting data from databases, where the result may not fit into RAM.
JSON compression can dramatically reduce the size of transmitted data. Gzip is great with JSON because of its textual nature and key repeatability. Most web servers support gzip compression automatically, but it is important to make sure it is enabled.
Code — an educational platform with courses in Python, JavaScript and web development for beginner developers.
Join our Telegram channel, where you will find useful materials, analysis of complex topics and support from an active community of programmers.
Learn with us and turn knowledge into working code!
