Published on

Can Zod Parse JSON Files? Master JSON Validation with Zod

Authors
  • Name
    Ripal & Zalak
    Twitter

Can Zod Parse JSON Files? How to Use Zod for JSON Parsing and Validation

Zod is a popular TypeScript-first schema declaration and validation library. While Zod is powerful, you might wonder if it can directly parse JSON strings without requiring additional third-party libraries. The short answer: yes, with a bit of clever transformation.

Here, we explore how to handle JSON parsing with Zod, ensuring robust error handling and validation, all within the Zod framework.

The Problem

You have a function that:

  1. Receives a JSON string.
  2. Parses it into an object.
  3. Validates the object against a predefined Zod schema.

Solution: Parsing JSON with Zod

The trick is to use Zod's .transform() method to parse the JSON string and handle errors before passing the resulting object to your schema.

Example Code

import { z } from 'zod'

const configurationSchema = z.object({
  name: z.string(),
  version: z.string(),
  description: z.string(),
})

type Configuration = z.infer<typeof configurationSchema>

const createConfigurationFromJson = (content: string): Configuration => {
  return (
    z
      .string()
      .transform((_, ctx) => {
        try {
          // Attempt to parse the JSON string
          return JSON.parse(content)
        } catch (error) {
          // Add a custom Zod issue if JSON parsing fails
          ctx.addIssue({
            code: z.ZodIssueCode.custom,
            message: 'Invalid JSON format',
          })
          // Return `z.never` to indicate failure
          return z.never
        }
      })
      // Pipe the parsed object to the schema for validation
      .pipe(configurationSchema)
      .parse(content)
  )
}

// Test Cases
try {
  const config = createConfigurationFromJson(`{
    "name": "my-app",
    "version": "1.0.0",
    "description": "My awesome app"
  }`)
  console.log('Parsed and validated configuration:', config)
} catch (e) {
  console.error('Error:', e.errors)
}

try {
  const config = createConfigurationFromJson(`{
    "name": "my-app",
    "version": "1.0.0",
    "banana": "🍌"
  }`)
  console.log('Parsed and validated configuration:', config)
} catch (e) {
  console.error('Error:', e.errors)
}

How It Works

  1. transform():

    • Parses the JSON string using JSON.parse.
    • If parsing fails, it uses ctx.addIssue() to record a custom Zod error.
    • Returns z.never to indicate the parsing failure.
  2. .pipe():

    • Passes the parsed object to the configurationSchema for validation.
  3. Error Handling:

    • Any JSON parsing or schema validation errors are caught as Zod errors, providing a unified error-handling experience.

FAQs

Why not use JSON.parse twice?

Using JSON.parse twice can be redundant and expensive for large JSON strings. This approach minimizes unnecessary parsing while maintaining clean error handling.

Can this approach validate nested objects?

Yes! Zod excels at handling nested schemas, so you can extend the configurationSchema to validate deeply nested structures.