[{"data":1,"prerenderedAt":2224},["ShallowReactive",2],{"blog-batch-en-json-validation-syntax-vs-schema":3},[4,1094],{"id":5,"title":6,"author":7,"body":8,"category":1069,"date":1070,"description":1071,"draft":1072,"extension":1073,"h1":6,"image":1074,"lastmod":1070,"locales":1075,"meta":1078,"navigation":1079,"path":1080,"promo":1081,"seo":1085,"stem":1086,"tags":1087,"__hash__":1093},"blog\u002Fen\u002Fblog\u002Fjson-validation-syntax-vs-schema.md","JSON Validation Explained: Syntax Checks vs JSON Schema Validation","JSON Toolbox Team",{"type":9,"value":10,"toc":1019},"minimark",[11,15,19,26,29,34,41,51,57,91,94,98,104,109,114,146,153,157,160,166,173,179,182,210,214,224,232,243,247,255,259,262,266,275,294,297,301,313,319,323,428,431,475,483,506,510,516,521,524,615,623,627,630,634,679,682,686,689,700,704,710,714,720,730,734,740,744,750,753,757,760,764,770,774,780,784,790,793,797,801,806,810,817,821,827,831,847,851,854,858,861,865,868,909,913,917,927,931,940,944,947,954,963,967,1010,1013],[12,13,6],"h1",{"id":14},"json-validation-explained-syntax-checks-vs-json-schema-validation",[16,17,18],"p",{},"A JSON string can be parsed without errors and still break your API.",[16,20,21,25],{},[22,23,24],"code",{},"JSON.parse()"," only checks whether text follows JSON grammar. It does not check whether required fields exist, whether values match expected types, or whether extra fields should be rejected. For that, you need schema validation and, beyond that, business-rule validation.",[16,27,28],{},"This guide explains the three layers of JSON validation and shows how to implement each one in JavaScript.",[30,31,33],"h2",{"id":32},"the-short-answer-valid-json-is-not-always-valid-data","The Short Answer: Valid JSON Is Not Always Valid Data",[16,35,36,37,40],{},"Consider this payload sent to a ",[22,38,39],{},"POST \u002Fusers"," endpoint:",[42,43,49],"pre",{"className":44,"code":46,"language":47,"meta":48},[45],"language-json","{\n  \"email\": \"not-an-email\",\n  \"age\": -3,\n  \"role\": \"superadmin\",\n  \"marketingOptIn\": \"yes\",\n  \"debug\": true\n}\n","json","",[22,50,46],{"__ignoreMap":48},[16,52,53,54,56],{},"This is valid JSON — ",[22,55,24],{}," will succeed. But the data violates nearly every rule your API should enforce:",[58,59,60,67,73,79,85],"ul",{},[61,62,63,66],"li",{},[22,64,65],{},"email"," is not a valid email address.",[61,68,69,72],{},[22,70,71],{},"age"," is negative.",[61,74,75,78],{},[22,76,77],{},"role"," is not one of the allowed values.",[61,80,81,84],{},[22,82,83],{},"marketingOptIn"," should be a boolean, not a string.",[61,86,87,90],{},[22,88,89],{},"debug"," is an unknown field that should not be accepted.",[16,92,93],{},"This is why validation must go beyond syntax.",[30,95,97],{"id":96},"layer-1-syntax-validation-can-the-text-be-parsed","Layer 1: Syntax Validation — Can the Text Be Parsed?",[16,99,100,101,103],{},"Syntax validation checks whether a string conforms to the JSON grammar defined in RFC 8259. The simplest way to perform it in JavaScript is ",[22,102,24],{},".",[105,106,108],"h3",{"id":107},"what-jsonparse-checks","What JSON.parse() checks",[16,110,111,113],{},[22,112,24],{}," verifies that the text is structurally valid JSON:",[58,115,116,127,130,133,136,139],{},[61,117,118,119,122,123,126],{},"Strings use double quotes (",[22,120,121],{},"\"",", not ",[22,124,125],{},"'",").",[61,128,129],{},"Keys are double-quoted.",[61,131,132],{},"No trailing commas.",[61,134,135],{},"No comments.",[61,137,138],{},"Brackets and braces are balanced.",[61,140,141,142,145],{},"Numbers, booleans, and ",[22,143,144],{},"null"," are in correct form.",[16,147,148,149,152],{},"If any of these rules are violated, JavaScript throws a ",[22,150,151],{},"SyntaxError",". Common problems include trailing commas from JavaScript object literals, single-quoted strings, and unquoted keys.",[105,154,156],{"id":155},"common-syntax-errors","Common syntax errors",[16,158,159],{},"This JSON looks reasonable but fails to parse:",[42,161,164],{"className":162,"code":163,"language":47,"meta":48},[45],"{\n  \"email\": \"ada@example.com\",\n  \"age\": 36,\n}\n",[22,165,163],{"__ignoreMap":48},[16,167,168,169,172],{},"The trailing comma after ",[22,170,171],{},"36"," makes it invalid. Fix:",[42,174,177],{"className":175,"code":176,"language":47,"meta":48},[45],"{\n  \"email\": \"ada@example.com\",\n  \"age\": 36\n}\n",[22,178,176],{"__ignoreMap":48},[16,180,181],{},"Other frequent mistakes:",[58,183,184,191,197,204],{},[61,185,186,187,190],{},"Single quotes instead of double quotes: ",[22,188,189],{},"{ 'name': 'Ada' }"," → invalid.",[61,192,193,194,190],{},"Comments: ",[22,195,196],{},"{ \"name\": \"Ada\" \u002F\u002F developer note }",[61,198,199,200,203],{},"Unquoted keys: ",[22,201,202],{},"{ name: \"Ada\" }"," → invalid in JSON (valid in JavaScript object literals).",[61,205,206,207,190],{},"Unclosed brackets: ",[22,208,209],{},"{ \"items\": [1, 2, 3",[105,211,213],{"id":212},"a-safe-syntax-check-helper","A safe syntax-check helper",[16,215,216,217,219,220,223],{},"Instead of wrapping ",[22,218,24],{}," in a bare ",[22,221,222],{},"try\u002Fcatch",", return a structured result so callers can display meaningful error messages:",[42,225,230],{"className":226,"code":228,"language":229,"meta":48},[227],"language-ts","export type JsonSyntaxResult =\n  | { valid: true; value: unknown }\n  | { valid: false; error: string }\n\nexport function parseJsonSafely(text: string): JsonSyntaxResult {\n  try {\n    return { valid: true, value: JSON.parse(text) }\n  } catch (error) {\n    return {\n      valid: false,\n      error: error instanceof Error ? error.message : 'Invalid JSON',\n    }\n  }\n}\n","ts",[22,231,228],{"__ignoreMap":48},[16,233,234,235,237,238,103],{},"This only verifies JSON syntax. It does not verify required fields, types, allowed values, or application rules. If ",[22,236,24],{}," throws before you can inspect the data, start with our guide to ",[239,240,242],"a",{"href":241},"\u002Fblog\u002Fjson-parse-error-debug","debugging common JSON parse errors in API responses",[105,244,246],{"id":245},"when-to-use-an-online-json-validator","When to use an online JSON Validator",[16,248,249,250,254],{},"For quick checks during development or when reviewing API responses from logs, paste the raw text into a ",[239,251,253],{"href":252},"\u002Ftools\u002Fformat\u002Fjson-editor","JSON syntax validator"," to see whether it parses and where errors occur. This is especially useful when the JSON is minified or comes from an unfamiliar source.",[30,256,258],{"id":257},"layer-2-schema-validation-does-the-data-have-the-expected-shape","Layer 2: Schema Validation — Does the Data Have the Expected Shape?",[16,260,261],{},"Syntax validation tells you the text is JSON. Schema validation tells you the data matches the structure your application expects.",[105,263,265],{"id":264},"what-json-schema-validates","What JSON Schema validates",[16,267,268,274],{},[239,269,273],{"href":270,"rel":271},"https:\u002F\u002Fjson-schema.org\u002Fdocs",[272],"nofollow","JSON Schema"," is a declarative vocabulary for describing the structure, constraints, and data types of JSON documents. A schema can specify:",[58,276,277,282,285,288,291],{},[61,278,279,280,103],{},"The expected type: object, array, string, number, boolean, or ",[22,281,144],{},[61,283,284],{},"Which fields are required.",[61,286,287],{},"Allowed values for each field.",[61,289,290],{},"Numeric ranges, string patterns, and array lengths.",[61,292,293],{},"Whether extra fields are permitted.",[16,295,296],{},"The schema is itself a JSON document, which makes it portable across languages and tools.",[105,298,300],{"id":299},"the-user-creation-schema","The user creation schema",[16,302,303,304,306,307,312],{},"Here is a JSON Schema for the ",[22,305,39],{}," payload. It uses ",[239,308,311],{"href":309,"rel":310},"https:\u002F\u002Fjson-schema.org\u002Fdraft\u002F2020-12\u002Fschema",[272],"Draft 2020-12",":",[42,314,317],{"className":315,"code":316,"language":47,"meta":48},[45],"{\n  \"$schema\": \"https:\u002F\u002Fjson-schema.org\u002Fdraft\u002F2020-12\u002Fschema\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"email\", \"age\", \"role\", \"marketingOptIn\"],\n  \"properties\": {\n    \"email\": {\n      \"type\": \"string\",\n      \"format\": \"email\"\n    },\n    \"age\": {\n      \"type\": \"integer\",\n      \"minimum\": 0,\n      \"maximum\": 150\n    },\n    \"role\": {\n      \"type\": \"string\",\n      \"enum\": [\"user\", \"editor\", \"admin\"]\n    },\n    \"marketingOptIn\": {\n      \"type\": \"boolean\"\n    }\n  }\n}\n",[22,318,316],{"__ignoreMap":48},[105,320,322],{"id":321},"how-to-read-this-schema","How to read this schema",[324,325,326,339],"table",{},[327,328,329],"thead",{},[330,331,332,336],"tr",{},[333,334,335],"th",{},"Keyword",[333,337,338],{},"What it enforces",[340,341,342,353,363,373,383,399,412],"tbody",{},[330,343,344,350],{},[345,346,347],"td",{},[22,348,349],{},"type: \"object\"",[345,351,352],{},"The root value must be a JSON object.",[330,354,355,360],{},[345,356,357],{},[22,358,359],{},"required",[345,361,362],{},"These fields must be present.",[330,364,365,370],{},[345,366,367],{},[22,368,369],{},"properties",[345,371,372],{},"Defines the expected shape for each field.",[330,374,375,380],{},[345,376,377],{},[22,378,379],{},"format: \"email\"",[345,381,382],{},"Declares the expected format. Enforcement depends on the validator (see below).",[330,384,385,394],{},[345,386,387,390,391],{},[22,388,389],{},"minimum"," \u002F ",[22,392,393],{},"maximum",[345,395,396,397,103],{},"Numeric boundaries for ",[22,398,71],{},[330,400,401,406],{},[345,402,403],{},[22,404,405],{},"enum",[345,407,408,409,411],{},"Restricts ",[22,410,77],{}," to the listed values.",[330,413,414,419],{},[345,415,416],{},[22,417,418],{},"additionalProperties: false",[345,420,421,422,424,425,103],{},"Rejects any field not declared in ",[22,423,369],{}," or ",[22,426,427],{},"patternProperties",[16,429,430],{},"Two details that often cause confusion:",[432,433,434,455],"ol",{},[61,435,436,442,443,445,446,448,449,451,452,454],{},[437,438,439,441],"strong",{},[22,440,369],{}," does not make fields required."," Listing a field in ",[22,444,369],{}," only defines its schema. You must also list it in ",[22,447,359],{}," to make it mandatory. A field can appear in ",[22,450,369],{}," but be absent from ",[22,453,359],{},", making it optional.",[61,456,457,463,464,467,468,471,472,474],{},[437,458,459,462],{},[22,460,461],{},"additionalProperties"," defaults to allowing extra fields."," If you do not set it to ",[22,465,466],{},"false",", an object with unexpected fields like ",[22,469,470],{},"\"debug\": true"," will pass validation. Only an explicit ",[22,473,418],{}," rejects undeclared fields.",[105,476,478,479,482],{"id":477},"the-format-keyword-caveat","The ",[22,480,481],{},"format"," keyword caveat",[16,484,478,485,487,488,493,494,501,502,505],{},[22,486,481],{}," keyword communicates an intended format, but whether it is enforced depends on the JSON Schema validator and its configuration. For example, ",[239,489,492],{"href":490,"rel":491},"https:\u002F\u002Fajv.js.org\u002Fapi.html",[272],"Ajv"," v7+ provides common format validators through the optional ",[239,495,498],{"href":496,"rel":497},"https:\u002F\u002Fajv.js.org\u002Fguide\u002Fformats.html",[272],[22,499,500],{},"ajv-formats"," package. Without it, ",[22,503,504],{},"\"format\": \"email\""," is treated as an annotation, not a validation rule. Always verify your validator's configuration before relying on format checks in production.",[105,507,509],{"id":508},"a-valid-json-document-that-fails-schema-validation","A valid JSON document that fails schema validation",[16,511,512,513,515],{},"The payload from the introduction passes ",[22,514,24],{}," but fails the schema:",[42,517,519],{"className":518,"code":46,"language":47,"meta":48},[45],[22,520,46],{"__ignoreMap":48},[16,522,523],{},"A schema validator would report these errors:",[324,525,526,536],{},[327,527,528],{},[330,529,530,533],{},[333,531,532],{},"Path",[333,534,535],{},"Problem",[340,537,538,550,567,580,597],{},[330,539,540,545],{},[345,541,542],{},[22,543,544],{},"\u002Femail",[345,546,547,548,103],{},"Value does not match format ",[22,549,65],{},[330,551,552,557],{},[345,553,554],{},[22,555,556],{},"\u002Fage",[345,558,559,560,563,564,103],{},"Value ",[22,561,562],{},"-3"," is less than minimum ",[22,565,566],{},"0",[330,568,569,574],{},[345,570,571],{},[22,572,573],{},"\u002Frole",[345,575,559,576,579],{},[22,577,578],{},"superadmin"," is not one of the allowed enum values.",[330,581,582,587],{},[345,583,584],{},[22,585,586],{},"\u002FmarketingOptIn",[345,588,589,590,593,594,103],{},"Expected ",[22,591,592],{},"boolean",", got ",[22,595,596],{},"string",[330,598,599,604],{},[345,600,601],{},[22,602,603],{},"\u002Fdebug",[345,605,606,607,609,610,612,613,103],{},"Property ",[22,608,89],{}," is not allowed when ",[22,611,461],{}," is ",[22,614,466],{},[16,616,617,618,622],{},"You can test this interactively with the ",[239,619,621],{"href":620},"\u002Ftools\u002Fformat\u002Fjson-schema-validator","JSON Schema Validator"," — paste the payload and the schema side by side to see the errors.",[30,624,626],{"id":625},"layer-3-business-validation-can-your-application-accept-this-data","Layer 3: Business Validation — Can Your Application Accept This Data?",[16,628,629],{},"Schema validation is powerful, but it cannot express rules that depend on your system's state. Business validation handles those cases with application logic.",[105,631,633],{"id":632},"examples-json-schema-cannot-fully-decide","Examples JSON Schema cannot fully decide",[58,635,636,646,656,665,676],{},[61,637,638,639,642,643,645],{},"The email address ",[22,640,641],{},"ada@example.com"," is syntactically valid and matches the ",[22,644,65],{}," format, but it may already exist in your database.",[61,647,648,649,651,652,655],{},"An ",[22,650,71],{}," of 200 passes the ",[22,653,654],{},"minimum: 0"," check but may be rejected by a business rule capping realistic ages.",[61,657,658,659,661,662,103],{},"A user's ",[22,660,77],{}," may be valid per the enum, but the authenticated caller may not have permission to assign ",[22,663,664],{},"admin",[61,666,667,668,671,672,675],{},"A ",[22,669,670],{},"startDate"," and ",[22,673,674],{},"endDate"," may both be valid ISO 8601 strings, but the start may fall after the end.",[61,677,678],{},"A product ID may exist in the schema, but the product may be out of stock.",[16,680,681],{},"These checks require querying a database, verifying permissions, or running application-specific logic. No JSON Schema can replace them.",[105,683,685],{"id":684},"client-side-versus-server-side-validation","Client-side versus server-side validation",[16,687,688],{},"Run schema validation on both the client and the server when it improves user feedback. But always validate again on the backend. Client-side checks:",[58,690,691,694,697],{},[61,692,693],{},"Can be bypassed by modifying network requests.",[61,695,696],{},"Cannot safely enforce permissions or database constraints.",[61,698,699],{},"Should be treated as a UX convenience, not a security measure.",[30,701,703],{"id":702},"how-to-validate-json-in-javascript","How to Validate JSON in JavaScript",[16,705,706,707,709],{},"Here is a complete validation pipeline for the ",[22,708,39],{}," endpoint.",[105,711,713],{"id":712},"step-1-syntax-validation","Step 1: Syntax validation",[42,715,718],{"className":716,"code":717,"language":229,"meta":48},[227],"import Ajv from 'ajv'\nimport addFormats from 'ajv-formats'\n\nconst ajv = new Ajv({ allErrors: true, strict: true })\naddFormats(ajv)\n\nconst createUserSchema = {\n  type: 'object',\n  additionalProperties: false,\n  required: ['email', 'age', 'role', 'marketingOptIn'],\n  properties: {\n    email: { type: 'string', format: 'email' },\n    age: { type: 'integer', minimum: 0, maximum: 150 },\n    role: { type: 'string', enum: ['user', 'editor', 'admin'] },\n    marketingOptIn: { type: 'boolean' },\n  },\n} as const\n\nconst validateCreateUser = ajv.compile(createUserSchema)\n",[22,719,717],{"__ignoreMap":48},[16,721,722,723,726,727,103],{},"Ajv compiles the schema into a reusable validation function. It supports multiple JSON Schema drafts and reports all errors when ",[22,724,725],{},"allErrors"," is set to ",[22,728,729],{},"true",[105,731,733],{"id":732},"step-2-schema-validation","Step 2: Schema validation",[42,735,738],{"className":736,"code":737,"language":229,"meta":48},[227],"export function validateCreateUserPayload(value: unknown) {\n  const valid = validateCreateUser(value)\n  return {\n    valid: Boolean(valid),\n    errors: validateCreateUser.errors ?? [],\n  }\n}\n",[22,739,737],{"__ignoreMap":48},[105,741,743],{"id":742},"step-3-the-full-pipeline","Step 3: The full pipeline",[42,745,748],{"className":746,"code":747,"language":229,"meta":48},[227],"export function validateIncomingUserJson(text: string) {\n  \u002F\u002F Layer 1: Syntax\n  const parsed = parseJsonSafely(text)\n  if (!parsed.valid) {\n    return { ok: false as const, stage: 'syntax' as const, errors: [parsed.error] }\n  }\n\n  \u002F\u002F Layer 2: Schema\n  const schemaResult = validateCreateUserPayload(parsed.value)\n  if (!schemaResult.valid) {\n    return { ok: false as const, stage: 'schema' as const, errors: schemaResult.errors }\n  }\n\n  \u002F\u002F Layer 3: Business (example — you would implement this with your DB\u002Flogic)\n  \u002F\u002F if (await emailAlreadyExists(parsed.value.email)) {\n  \u002F\u002F   return { ok: false, stage: 'business', errors: ['Email is already registered'] }\n  \u002F\u002F }\n\n  return { ok: true as const, value: parsed.value }\n}\n",[22,749,747],{"__ignoreMap":48},[16,751,752],{},"This function runs the cheapest check first. If syntax fails, there is no point running schema validation. If schema fails, there is no point querying the database.",[30,754,756],{"id":755},"api-error-response-design","API Error Response Design",[16,758,759],{},"A well-designed API returns different error shapes depending on which validation layer failed.",[105,761,763],{"id":762},"syntax-error-400-bad-request","Syntax error → 400 Bad Request",[42,765,768],{"className":766,"code":767,"language":47,"meta":48},[45],"{\n  \"error\": {\n    \"code\": \"INVALID_JSON\",\n    \"message\": \"Request body is not valid JSON.\"\n  }\n}\n",[22,769,767],{"__ignoreMap":48},[105,771,773],{"id":772},"schema-error-422-unprocessable-entity","Schema error → 422 Unprocessable Entity",[42,775,778],{"className":776,"code":777,"language":47,"meta":48},[45],"{\n  \"error\": {\n    \"code\": \"VALIDATION_FAILED\",\n    \"message\": \"Request data does not match the expected schema.\",\n    \"fields\": [\n      { \"path\": \"\u002Femail\", \"message\": \"must match format \\\"email\\\"\" },\n      { \"path\": \"\u002Fage\", \"message\": \"must be >= 0\" },\n      { \"path\": \"\u002FmarketingOptIn\", \"message\": \"must be boolean\" }\n    ]\n  }\n}\n",[22,779,777],{"__ignoreMap":48},[105,781,783],{"id":782},"business-rule-error-409-conflict-or-422","Business-rule error → 409 Conflict or 422",[42,785,788],{"className":786,"code":787,"language":47,"meta":48},[45],"{\n  \"error\": {\n    \"code\": \"EMAIL_ALREADY_EXISTS\",\n    \"message\": \"An account already uses this email address.\"\n  }\n}\n",[22,789,787],{"__ignoreMap":48},[16,791,792],{},"Many APIs use 400 for malformed JSON and 422 for structurally valid data that fails validation, but your API should follow one documented and consistent convention.",[30,794,796],{"id":795},"common-json-validation-mistakes","Common JSON Validation Mistakes",[105,798,800],{"id":799},"mistake-1-treating-jsonparse-success-as-api-validation","Mistake 1: Treating JSON.parse() success as API validation",[16,802,803,805],{},[22,804,24],{}," succeeding only means the text is valid JSON. It says nothing about whether the data matches your API contract. Always run schema validation after parsing.",[105,807,809],{"id":808},"mistake-2-relying-on-content-type-alone","Mistake 2: Relying on Content-Type alone",[16,811,812,813,816],{},"A request with ",[22,814,815],{},"Content-Type: application\u002Fjson"," header may still contain a body that is not valid JSON. Always parse and validate the body, not just the header.",[105,818,820],{"id":819},"mistake-3-allowing-unknown-fields-accidentally","Mistake 3: Allowing unknown fields accidentally",[16,822,823,824,826],{},"If your schema does not include ",[22,825,418],{},", unexpected fields pass through silently. This can leak internal debug data into your system or cause subtle bugs when clients send fields you did not expect.",[105,828,830],{"id":829},"mistake-4-coercing-values-without-documenting-it","Mistake 4: Coercing values without documenting it",[16,832,833,834,837,838,424,841,837,844,846],{},"Some frameworks silently convert ",[22,835,836],{},"\"42\""," to ",[22,839,840],{},"42",[22,842,843],{},"\"true\"",[22,845,729],{},". If your API does this, document it clearly. Silent coercion can hide client bugs and make debugging harder. When in strict mode, Ajv rejects type mismatches rather than coercing them.",[105,848,850],{"id":849},"mistake-5-validating-only-in-the-browser","Mistake 5: Validating only in the browser",[16,852,853],{},"Client-side validation improves UX but can be bypassed. Every request that modifies data must be validated again on the server.",[105,855,857],{"id":856},"mistake-6-logging-raw-invalid-payloads-with-secrets","Mistake 6: Logging raw invalid payloads with secrets",[16,859,860],{},"When logging validation failures, strip or redact sensitive fields like tokens, passwords, and API keys before writing to logs.",[30,862,864],{"id":863},"a-practical-api-validation-workflow","A Practical API Validation Workflow",[16,866,867],{},"For each incoming JSON request:",[432,869,870,876,885,891,897,903],{},[61,871,872,875],{},[437,873,874],{},"Enforce a body-size limit."," Reject payloads that exceed your expected maximum before parsing.",[61,877,878,881,882,884],{},[437,879,880],{},"Parse the JSON."," Use ",[22,883,24],{}," or an equivalent. If it fails, return a 400 error with the parse error message.",[61,886,887,890],{},[437,888,889],{},"Validate against a schema."," Check required fields, types, ranges, and additional properties. If it fails, return a 422 error with the list of schema violations.",[61,892,893,896],{},[437,894,895],{},"Apply business rules."," Check database constraints, permissions, and application logic. If it fails, return a 409 or 422 error with a specific error code.",[61,898,899,902],{},[437,900,901],{},"Return structured errors."," Include a machine-readable error code, a human-readable message, and field-level details for schema errors.",[61,904,905,908],{},[437,906,907],{},"Log safely."," Redact sensitive fields before writing to logs.",[30,910,912],{"id":911},"faq","FAQ",[105,914,916],{"id":915},"does-jsonparse-validate-json-schema","Does JSON.parse() validate JSON Schema?",[16,918,919,920,922,923,926],{},"No. ",[22,921,24],{}," only checks whether a string follows JSON syntax and converts it into a JavaScript value. It does not check whether required fields exist, whether values have expected types, or whether extra fields are allowed. Use a schema validator like ",[239,924,492],{"href":490,"rel":925},[272]," for that.",[105,928,930],{"id":929},"is-valid-json-always-safe-to-use-as-api-data","Is valid JSON always safe to use as API data?",[16,932,933,934,936,937,939],{},"No. A payload may be syntactically valid JSON but still fail your API contract. For example, an ",[22,935,71],{}," field may be negative, a required field may be missing, or a ",[22,938,77],{}," may not be one of the allowed values. Syntax validation is only the first layer.",[105,941,943],{"id":942},"should-i-validate-json-in-the-frontend-or-backend","Should I validate JSON in the frontend or backend?",[16,945,946],{},"Validate on both when it improves user feedback, but always validate again on the backend. Client-side checks can be bypassed and cannot safely enforce permissions, database constraints, or other server-side business rules.",[105,948,950,951,953],{"id":949},"does-format-email-always-validate-email-addresses","Does ",[22,952,504],{}," always validate email addresses?",[16,955,956,957,962],{},"Not necessarily. JSON Schema validators differ in how they implement and enable format checks. With Ajv, common formats are provided through the ",[239,958,960],{"href":496,"rel":959},[272],[22,961,500],{}," package, so confirm your validator configuration before relying on format validation in production.",[30,964,966],{"id":965},"whats-next","What's Next?",[58,968,969,979,988,999],{},[61,970,971,974,975,978],{},[437,972,973],{},"Already have a broken payload?"," Start with ",[239,976,977],{"href":241},"JSON Parse Failed: 10 Common API Errors and How to Debug Them"," to find and fix the syntax error.",[61,980,981,984,985,987],{},[437,982,983],{},"Need to validate JSON against a schema?"," Use the ",[239,986,621],{"href":620}," to paste your data and schema side by side.",[61,989,990,993,994,998],{},[437,991,992],{},"Want to inspect complex JSON structure?"," ",[239,995,997],{"href":996},"\u002Ftools\u002Fformat\u002Fjson-path-tester","View your JSON as an interactive tree"," to explore nested fields.",[61,1000,1001,1004,1005,1009],{},[437,1002,1003],{},"Building an API from scratch?"," Read our guide to ",[239,1006,1008],{"href":1007},"\u002Fblog\u002Fjson-best-practices","JSON best practices"," for error handling, validation, and response design.",[1011,1012],"hr",{},[16,1014,1015],{},[1016,1017,1018],"em",{},"All tools on JSON Toolbox run entirely in your browser. Your data never leaves your device.",{"title":48,"searchDepth":1020,"depth":1020,"links":1021},2,[1022,1023,1030,1038,1042,1047,1052,1060,1061,1068],{"id":32,"depth":1020,"text":33},{"id":96,"depth":1020,"text":97,"children":1024},[1025,1027,1028,1029],{"id":107,"depth":1026,"text":108},3,{"id":155,"depth":1026,"text":156},{"id":212,"depth":1026,"text":213},{"id":245,"depth":1026,"text":246},{"id":257,"depth":1020,"text":258,"children":1031},[1032,1033,1034,1035,1037],{"id":264,"depth":1026,"text":265},{"id":299,"depth":1026,"text":300},{"id":321,"depth":1026,"text":322},{"id":477,"depth":1026,"text":1036},"The format keyword caveat",{"id":508,"depth":1026,"text":509},{"id":625,"depth":1020,"text":626,"children":1039},[1040,1041],{"id":632,"depth":1026,"text":633},{"id":684,"depth":1026,"text":685},{"id":702,"depth":1020,"text":703,"children":1043},[1044,1045,1046],{"id":712,"depth":1026,"text":713},{"id":732,"depth":1026,"text":733},{"id":742,"depth":1026,"text":743},{"id":755,"depth":1020,"text":756,"children":1048},[1049,1050,1051],{"id":762,"depth":1026,"text":763},{"id":772,"depth":1026,"text":773},{"id":782,"depth":1026,"text":783},{"id":795,"depth":1020,"text":796,"children":1053},[1054,1055,1056,1057,1058,1059],{"id":799,"depth":1026,"text":800},{"id":808,"depth":1026,"text":809},{"id":819,"depth":1026,"text":820},{"id":829,"depth":1026,"text":830},{"id":849,"depth":1026,"text":850},{"id":856,"depth":1026,"text":857},{"id":863,"depth":1020,"text":864},{"id":911,"depth":1020,"text":912,"children":1062},[1063,1064,1065,1066],{"id":915,"depth":1026,"text":916},{"id":929,"depth":1026,"text":930},{"id":942,"depth":1026,"text":943},{"id":949,"depth":1026,"text":1067},"Does \"format\": \"email\" always validate email addresses?",{"id":965,"depth":1020,"text":966},"json_tools","2026-09-03T00:00:00.000Z","Learn the difference between JSON syntax validation and JSON Schema validation. Understand three validation layers with practical JavaScript examples for validating API data safely.",false,"md","\u002Fblog\u002Fcover\u002Fen\u002Fjson-validation-syntax-vs-schema-cover.svg",[1076,1077],"en","zh",{},true,"\u002Fen\u002Fblog\u002Fjson-validation-syntax-vs-schema",{"slug":1082,"text":1083,"btn":1084},"json-schema-validator","Validate your JSON data against a JSON Schema:","Open JSON Schema Validator",{"title":6,"description":1071},"en\u002Fblog\u002Fjson-validation-syntax-vs-schema",[1088,1089,273,1090,1091,1092],"JSON","JSON Validation","JavaScript","API Validation","Data Validation","ZVmiUdPnjatrK0u75M9aYD2o6oxI2AkNmzhzFuX_CVs",{"id":1095,"title":1096,"author":7,"body":1097,"category":1069,"date":1070,"description":2209,"draft":1072,"extension":1073,"h1":1096,"image":2210,"lastmod":1070,"locales":2211,"meta":2212,"navigation":1079,"path":2213,"promo":2214,"seo":2217,"stem":2218,"tags":2219,"__hash__":2223},"blog\u002Fzh\u002Fblog\u002Fjson-validation-syntax-vs-schema.md","JSON 校验详解：语法校验、JSON Schema 与业务规则的区别",{"type":9,"value":1098,"toc":2164},[1099,1102,1108,1115,1118,1123,1126,1156,1159,1179,1189,1199,1203,1206,1268,1271,1279,1282,1286,1289,1295,1300,1306,1315,1319,1324,1330,1334,1340,1344,1349,1353,1359,1362,1374,1380,1386,1434,1440,1444,1447,1450,1456,1459,1464,1470,1473,1591,1594,1618,1624,1627,1633,1636,1660,1663,1679,1685,1689,1692,1695,1700,1703,1728,1731,1734,1740,1744,1748,1758,1764,1771,1775,1781,1784,1792,1795,1801,1804,1810,1813,1817,1824,1828,1834,1840,1844,1855,1861,1864,1867,1873,1876,1880,1887,1892,1899,1904,1912,1916,1919,1925,1928,1932,1934,1940,1946,1949,1953,1956,1970,1973,1977,1980,1983,1997,2001,2004,2010,2020,2023,2029,2035,2039,2042,2048,2061,2065,2068,2072,2075,2078,2081,2099,2104,2114,2117,2157,2159],[12,1100,1096],{"id":1101},"json-校验详解语法校验json-schema-与业务规则的区别",[16,1103,1104,1105,1107],{},"很多开发者会把\"JSON 能被 ",[22,1106,24],{}," 解析\"理解成\"JSON 数据已经有效\"。但在真实接口开发中，这两件事并不相同。",[16,1109,1110,1111,1114],{},"一段文本能够被解析，只能说明它符合 JSON 的",[437,1112,1113],{},"语法规则","。它仍可能缺少必填字段、字段类型不正确、包含不允许的值，或者在当前业务场景中根本不能被系统接受。",[16,1116,1117],{},"例如，下面这段内容是合法 JSON：",[42,1119,1121],{"className":1120,"code":46,"language":47,"meta":48},[45],[22,1122,46],{"__ignoreMap":48},[16,1124,1125],{},"但它很可能不符合\"创建用户\"接口的要求：",[58,1127,1128,1133,1138,1146,1151],{},[61,1129,1130,1132],{},[22,1131,65],{}," 不符合预期格式；",[61,1134,1135,1137],{},[22,1136,71],{}," 不应该是负数；",[61,1139,1140,1142,1143,1145],{},[22,1141,77],{}," 不一定允许 ",[22,1144,578],{},"；",[61,1147,1148,1150],{},[22,1149,83],{}," 应该是布尔值，而不是字符串；",[61,1152,1153,1155],{},[22,1154,89],{}," 可能是接口不允许接收的额外字段。",[16,1157,1158],{},"JSON 校验通常至少包括三个层次：",[432,1160,1161,1167,1173],{},[61,1162,1163,1166],{},[437,1164,1165],{},"语法校验","：这段文本是否为合法 JSON？",[61,1168,1169,1172],{},[437,1170,1171],{},"结构校验","：字段、类型、范围和嵌套结构是否符合接口契约？",[61,1174,1175,1178],{},[437,1176,1177],{},"业务规则校验","：即使结构正确，当前业务是否允许这份数据？",[16,1180,1181,1182,1184,1185,1188],{},"如果你的代码在 ",[22,1183,24],{}," 阶段就报错，可以先阅读 ",[239,1186,1187],{"href":241},"JSON 解析失败：10 个常见 API 错误与排查方法","。",[1190,1191,1192],"blockquote",{},[16,1193,1194,1195,1198],{},"想先确认一段文本是否为合法 JSON，可以使用 ",[239,1196,1197],{"href":620},"JSON Schema 校验器"," 检查语法错误位置。",[30,1200,1202],{"id":1201},"json-校验不是单一步骤","JSON 校验不是单一步骤",[16,1204,1205],{},"下面这张表可以快速区分三种常见校验。",[324,1207,1208,1224],{},[327,1209,1210],{},[330,1211,1212,1215,1218,1221],{},[333,1213,1214],{},"校验层级",[333,1216,1217],{},"核心问题",[333,1219,1220],{},"常见实现方式",[333,1222,1223],{},"常见失败示例",[340,1225,1226,1241,1255],{},[330,1227,1228,1230,1233,1238],{},[345,1229,1165],{},[345,1231,1232],{},"文本能否被解析为 JSON？",[345,1234,1235,1237],{},[22,1236,24],{},"、JSON Validator",[345,1239,1240],{},"尾逗号、单引号、注释、缺少括号",[330,1242,1243,1246,1249,1252],{},[345,1244,1245],{},"Schema 结构校验",[345,1247,1248],{},"数据是否符合预期字段、类型与约束？",[345,1250,1251],{},"JSON Schema、Ajv、Zod",[345,1253,1254],{},"缺少必填字段、类型错误、枚举值不合法",[330,1256,1257,1259,1262,1265],{},[345,1258,1177],{},[345,1260,1261],{},"当前系统是否允许接受这份数据？",[345,1263,1264],{},"服务端业务逻辑、权限系统、数据库查询",[345,1266,1267],{},"邮箱已注册、没有资源权限、库存不足",[16,1269,1270],{},"这三层应按顺序执行：",[42,1272,1277],{"className":1273,"code":1275,"language":1276,"meta":48},[1274],"language-text","原始文本\n→ JSON 语法校验\n→ JSON Schema 结构校验\n→ 业务规则校验\n→ 执行业务逻辑\n","text",[22,1278,1275],{"__ignoreMap":48},[16,1280,1281],{},"不要跳过前两层，直接假设客户端或第三方系统传来的数据可信。",[30,1283,1285],{"id":1284},"第一层json-语法校验","第一层：JSON 语法校验",[16,1287,1288],{},"语法校验解决的问题最基础：",[42,1290,1293],{"className":1291,"code":1292,"language":1276,"meta":48},[1274],"这段文本是不是符合 JSON 语法？\n",[22,1294,1292],{"__ignoreMap":48},[16,1296,1297,1298,1188],{},"JavaScript 中最常用的方法是 ",[22,1299,24],{},[42,1301,1304],{"className":1302,"code":1303,"language":229,"meta":48},[227],"export type JsonSyntaxResult =\n  | {\n      valid: true\n      value: unknown\n    }\n  | {\n      valid: false\n      error: string\n    }\n\nexport function parseJsonSafely(text: string): JsonSyntaxResult {\n  try {\n    return {\n      valid: true,\n      value: JSON.parse(text)\n    }\n  } catch (error) {\n    return {\n      valid: false,\n      error: error instanceof Error ? error.message : \"无效 JSON\"\n    }\n  }\n}\n",[22,1305,1303],{"__ignoreMap":48},[16,1307,1308,1309,1311,1312,1314],{},"如果输入不符合 JSON grammar，",[22,1310,24],{}," 会抛出 ",[22,1313,151],{},"。常见问题包括尾逗号、使用单引号、对象键未加双引号、注释、未闭合的对象或数组等。",[105,1316,1318],{"id":1317},"常见的无效-json","常见的无效 JSON",[1320,1321,1323],"h4",{"id":1322},"_1-尾逗号","1. 尾逗号",[42,1325,1328],{"className":1326,"code":1327,"language":47,"meta":48},[45],"{\n  \"name\": \"Ada\",\n  \"age\": 36,\n}\n",[22,1329,1327],{"__ignoreMap":48},[1320,1331,1333],{"id":1332},"_2-单引号","2. 单引号",[42,1335,1338],{"className":1336,"code":1337,"language":47,"meta":48},[45],"{\n  \"name\": \"Ada\"\n}\n",[22,1339,1337],{"__ignoreMap":48},[1320,1341,1343],{"id":1342},"_3-注释","3. 注释",[42,1345,1347],{"className":1346,"code":1337,"language":47,"meta":48},[45],[22,1348,1337],{"__ignoreMap":48},[1320,1350,1352],{"id":1351},"_4-未加引号的对象键","4. 未加引号的对象键",[42,1354,1357],{"className":1355,"code":1356,"language":47,"meta":48},[45],"{\n  name: \"Ada\"\n}\n",[22,1358,1356],{"__ignoreMap":48},[16,1360,1361],{},"这些写法在 JavaScript 对象字面量、JSONC 或某些配置格式中可能可以出现，但它们不是标准 JSON。",[1190,1363,1364],{},[16,1365,1366,1367,1370,1371,1373],{},"如果只是想快速查看缩进、括号和嵌套结构，可以先使用 ",[239,1368,1369],{"href":252},"JSON Editor"," 格式化合法 JSON；如果内容本身无法解析，应先使用 ",[239,1372,1197],{"href":620}," 定位语法问题。",[105,1375,1377,1379],{"id":1376},"jsonparse-没有检查什么",[22,1378,24],{}," 没有检查什么？",[16,1381,1382,1383,1385],{},"下面这些内容即使解析成功，",[22,1384,24],{}," 也不会判断它们是否符合接口要求：",[58,1387,1388,1401,1406,1420,1425,1428,1431],{},[61,1389,1390,1391,1393,1394,1393,1397,1400],{},"是否缺少 ",[22,1392,65],{},"、",[22,1395,1396],{},"id",[22,1398,1399],{},"name"," 等必填字段；",[61,1402,1403,1405],{},[22,1404,71],{}," 是否应该为非负整数；",[61,1407,1408,1410,1411,1393,1414,1417,1418,1145],{},[22,1409,77],{}," 是否只能是 ",[22,1412,1413],{},"user",[22,1415,1416],{},"editor"," 或 ",[22,1419,664],{},[61,1421,1422,1424],{},[22,1423,65],{}," 是否符合邮箱格式；",[61,1426,1427],{},"是否出现了接口未声明的额外字段；",[61,1429,1430],{},"当前用户是否有权限提交这份数据；",[61,1432,1433],{},"数据库中是否已经存在相同邮箱。",[16,1435,1436,1437,1439],{},"因此，",[22,1438,24],{}," 成功只是校验的起点，不是终点。",[30,1441,1443],{"id":1442},"第二层json-schema-结构校验","第二层：JSON Schema 结构校验",[16,1445,1446],{},"JSON Schema 是一种声明 JSON 数据结构、类型和约束的规则语言。它可以描述\"一个合法请求体应该长什么样\"，并让校验器根据这些规则检查输入。",[16,1448,1449],{},"例如，一个创建用户接口可能希望接收：",[42,1451,1454],{"className":1452,"code":1453,"language":47,"meta":48},[45],"{\n  \"email\": \"ada@example.com\",\n  \"age\": 36,\n  \"role\": \"editor\",\n  \"marketingOptIn\": true\n}\n",[22,1455,1453],{"__ignoreMap":48},[16,1457,1458],{},"对应的 JSON Schema 可以写成：",[42,1460,1462],{"className":1461,"code":316,"language":47,"meta":48},[45],[22,1463,316],{"__ignoreMap":48},[16,1465,1466,1469],{},[239,1467,273],{"href":270,"rel":1468},[272]," 用于声明与验证 JSON 的结构、类型和约束。",[105,1471,1472],{"id":1472},"关键字段说明",[324,1474,1475,1488],{},[327,1476,1477],{},[330,1478,1479,1482,1485],{},[333,1480,1481],{},"Schema 关键字",[333,1483,1484],{},"作用",[333,1486,1487],{},"示例",[340,1489,1490,1505,1519,1533,1547,1563,1578],{},[330,1491,1492,1497,1500],{},[345,1493,1494],{},[22,1495,1496],{},"type",[345,1498,1499],{},"限制值的数据类型",[345,1501,1502],{},[22,1503,1504],{},"\"type\": \"object\"",[330,1506,1507,1511,1514],{},[345,1508,1509],{},[22,1510,369],{},[345,1512,1513],{},"定义对象中已知字段的校验规则",[345,1515,1516],{},[22,1517,1518],{},"\"email\": { \"type\": \"string\" }",[330,1520,1521,1525,1528],{},[345,1522,1523],{},[22,1524,359],{},[345,1526,1527],{},"声明哪些字段必须存在",[345,1529,1530],{},[22,1531,1532],{},"[\"email\", \"age\"]",[330,1534,1535,1539,1542],{},[345,1536,1537],{},[22,1538,405],{},[345,1540,1541],{},"限制字段只能取指定值之一",[345,1543,1544],{},[22,1545,1546],{},"[\"user\", \"editor\", \"admin\"]",[330,1548,1549,1555,1558],{},[345,1550,1551,390,1553],{},[22,1552,389],{},[22,1554,393],{},[345,1556,1557],{},"限制数字范围",[345,1559,1560],{},[22,1561,1562],{},"\"minimum\": 0",[330,1564,1565,1570,1573],{},[345,1566,1567],{},[22,1568,1569],{},"items",[345,1571,1572],{},"定义数组元素的校验规则",[345,1574,1575],{},[22,1576,1577],{},"\"items\": { \"type\": \"string\" }",[330,1579,1580,1584,1587],{},[345,1581,1582],{},[22,1583,461],{},[345,1585,1586],{},"是否允许未声明的额外字段",[345,1588,1589],{},[22,1590,466],{},[16,1592,1593],{},"有两个常见误解需要特别注意：",[432,1595,1596,1604],{},[61,1597,1598,1600,1601,1603],{},[22,1599,369],{}," 中出现字段，并不表示字段自动必填；必须通过 ",[22,1602,359],{}," 单独声明。",[61,1605,1606,1607,1610,1611,1417,1615,1617],{},"默认情况下，JSON Schema 允许额外字段。只有显式设置 ",[22,1608,1609],{},"\"additionalProperties\": false","，才会拒绝未被 ",[239,1612,369],{"href":1613,"rel":1614},"https:\u002F\u002Fjson-schema.org\u002Funderstanding-json-schema\u002Freference\u002Fobject",[272],[22,1616,427],{}," 声明的属性。",[105,1619,1621,1623],{"id":1620},"format-email-不一定总会强制校验",[22,1622,379],{}," 不一定总会强制校验",[16,1625,1626],{},"很多 Schema 示例会写：",[42,1628,1631],{"className":1629,"code":1630,"language":47,"meta":48},[45],"{\n  \"type\": \"string\",\n  \"format\": \"email\"\n}\n",[22,1632,1630],{"__ignoreMap":48},[16,1634,1635],{},"但不要假设所有 JSON Schema 校验器都会自动把它当成严格错误。",[16,1637,1638,1640,1641,1644,1645,1650,1651,1393,1653,1393,1656,1659],{},[22,1639,481],{}," 的具体执行方式取决于你使用的校验器和配置。例如 ",[239,1642,492],{"href":490,"rel":1643},[272]," 从 v7 开始不再默认内置常见 format 校验，需要通过 ",[239,1646,1648],{"href":496,"rel":1647},[272],[22,1649,500],{}," 提供 ",[22,1652,65],{},[22,1654,1655],{},"date-time",[22,1657,1658],{},"uri"," 等格式支持。",[16,1661,1662],{},"因此，生产环境中应确认：",[58,1664,1665,1668,1671,1676],{},[61,1666,1667],{},"你使用的是哪一个 JSON Schema draft；",[61,1669,1670],{},"所使用的 validator 是否启用了 format 校验；",[61,1672,1673,1675],{},[22,1674,481],{}," 是 annotation、warning，还是会直接导致校验失败；",[61,1677,1678],{},"邮箱、URL、日期等规则是否需要更严格的业务层验证。",[16,1680,1681,1682,1684],{},"你可以把上面的 Schema 和请求体粘贴到 ",[239,1683,1197],{"href":620}," 中，实际查看校验结果。",[30,1686,1688],{"id":1687},"第三层业务规则校验","第三层：业务规则校验",[16,1690,1691],{},"即使输入是合法 JSON，也通过了 JSON Schema，它仍然可能无法在当前系统中执行。",[16,1693,1694],{},"例如：",[42,1696,1698],{"className":1697,"code":1453,"language":47,"meta":48},[45],[22,1699,1453],{"__ignoreMap":48},[16,1701,1702],{},"结构完全正确，但服务端仍然可能拒绝它，因为：",[58,1704,1705,1710,1716,1719,1722,1725],{},[61,1706,1707,1709],{},[22,1708,641],{}," 已被其他账户使用；",[61,1711,1712,1713,1715],{},"当前管理员无权创建 ",[22,1714,1416],{}," 角色；",[61,1717,1718],{},"当前租户不允许新增用户；",[61,1720,1721],{},"用户数量已达到套餐上限；",[61,1723,1724],{},"关联组织不存在或已停用；",[61,1726,1727],{},"请求中的资源 ID 不属于当前用户。",[16,1729,1730],{},"这类规则依赖数据库、权限、租户上下文、库存、时间、状态机或第三方服务结果，通常不能仅凭 JSON Schema 静态判断。",[16,1732,1733],{},"因此，一个可靠的 API 校验流程通常应当是：",[42,1735,1738],{"className":1736,"code":1737,"language":1276,"meta":48},[1274],"1. 限制请求体大小\n2. 解析 JSON\n3. 校验 Schema\n4. 检查权限、资源状态和业务规则\n5. 执行业务逻辑\n6. 返回结构化响应\n",[22,1739,1737],{"__ignoreMap":48},[30,1741,1743],{"id":1742},"如何在-javascript-中校验-json","如何在 JavaScript 中校验 JSON",[105,1745,1747],{"id":1746},"只检查-json-语法","只检查 JSON 语法",[16,1749,1750,1751,1753,1754,1757],{},"如果你的目标只是判断文本能否被解析，使用 ",[22,1752,24],{}," 和 ",[22,1755,1756],{},"try...catch"," 即可：",[42,1759,1762],{"className":1760,"code":1761,"language":229,"meta":48},[227],"export function isValidJson(text: string): boolean {\n  try {\n    JSON.parse(text)\n    return true\n  } catch {\n    return false\n  }\n}\n",[22,1763,1761],{"__ignoreMap":48},[16,1765,1766,1767,1417,1769,1188],{},"但在应用代码中，通常更建议保留错误信息，而不是只返回 ",[22,1768,729],{},[22,1770,466],{},[105,1772,1774],{"id":1773},"使用-ajv-进行-json-schema-校验","使用 Ajv 进行 JSON Schema 校验",[16,1776,1777,1780],{},[239,1778,492],{"href":490,"rel":1779},[272]," 是 JavaScript 生态中常用的 JSON Schema validator，支持多个 JSON Schema draft，并将 Schema 编译为可复用的校验函数。",[16,1782,1783],{},"安装依赖：",[42,1785,1790],{"className":1786,"code":1788,"language":1789,"meta":48},[1787],"language-bash","npm install ajv ajv-formats\n","bash",[22,1791,1788],{"__ignoreMap":48},[16,1793,1794],{},"定义并编译 Schema：",[42,1796,1799],{"className":1797,"code":1798,"language":229,"meta":48},[227],"import Ajv from \"ajv\"\nimport addFormats from \"ajv-formats\"\n\nconst ajv = new Ajv({\n  allErrors: true,\n  strict: true\n})\n\naddFormats(ajv)\n\nconst createUserSchema = {\n  type: \"object\",\n  additionalProperties: false,\n  required: [\"email\", \"age\", \"role\", \"marketingOptIn\"],\n  properties: {\n    email: {\n      type: \"string\",\n      format: \"email\"\n    },\n    age: {\n      type: \"integer\",\n      minimum: 0,\n      maximum: 150\n    },\n    role: {\n      type: \"string\",\n      enum: [\"user\", \"editor\", \"admin\"]\n    },\n    marketingOptIn: {\n      type: \"boolean\"\n    }\n  }\n} as const\n\nconst validateCreateUser = ajv.compile(createUserSchema)\n\nexport function validateCreateUserPayload(value: unknown) {\n  const valid = validateCreateUser(value)\n\n  return {\n    valid: Boolean(valid),\n    errors: validateCreateUser.errors ?? []\n  }\n}\n",[22,1800,1798],{"__ignoreMap":48},[16,1802,1803],{},"将语法校验和 Schema 校验组合起来：",[42,1805,1808],{"className":1806,"code":1807,"language":229,"meta":48},[227],"export function validateIncomingUserJson(text: string) {\n  const parsed = parseJsonSafely(text)\n\n  if (!parsed.valid) {\n    return {\n      ok: false as const,\n      stage: \"syntax\" as const,\n      errors: [parsed.error]\n    }\n  }\n\n  const schemaResult = validateCreateUserPayload(parsed.value)\n\n  if (!schemaResult.valid) {\n    return {\n      ok: false as const,\n      stage: \"schema\" as const,\n      errors: schemaResult.errors\n    }\n  }\n\n  return {\n    ok: true as const,\n    value: parsed.value\n  }\n}\n",[22,1809,1807],{"__ignoreMap":48},[16,1811,1812],{},"这段流程仍然没有完成业务规则校验。通过 Schema 后，服务端仍应继续检查权限、唯一性和资源状态。",[30,1814,1816],{"id":1815},"api-应如何返回校验错误","API 应如何返回校验错误？",[16,1818,1819,1820,1823],{},"对调用方而言，能看懂错误比只收到一个泛泛的 ",[22,1821,1822],{},"400 Bad Request"," 更有帮助。",[105,1825,1827],{"id":1826},"json-语法错误","JSON 语法错误",[16,1829,1830,1831,1833],{},"许多 API 会将无法解析的请求体视为 ",[22,1832,1822],{},"：",[42,1835,1838],{"className":1836,"code":1837,"language":47,"meta":48},[45],"{\n  \"error\": {\n    \"code\": \"INVALID_JSON\",\n    \"message\": \"请求体不是合法的 JSON。\"\n  }\n}\n",[22,1839,1837],{"__ignoreMap":48},[105,1841,1843],{"id":1842},"schema-结构错误","Schema 结构错误",[16,1845,1846,1847,1850,1851,1854],{},"很多团队会对\"JSON 可解析但不符合请求契约\"的情况使用 ",[22,1848,1849],{},"422 Unprocessable Content","，也有团队统一使用 ",[22,1852,1853],{},"400","。没有一种状态码适合所有 API；更重要的是在整个 API 中保持一致，并在文档中说明规则。",[42,1856,1859],{"className":1857,"code":1858,"language":47,"meta":48},[45],"{\n  \"error\": {\n    \"code\": \"VALIDATION_FAILED\",\n    \"message\": \"请求数据不符合预期的 Schema。\",\n    \"fields\": [\n      {\n        \"path\": \"\u002Femail\",\n        \"message\": \"必须匹配 email 格式\"\n      },\n      {\n        \"path\": \"\u002Fage\",\n        \"message\": \"必须 >= 0\"\n      },\n      {\n        \"path\": \"\u002FmarketingOptIn\",\n        \"message\": \"必须是布尔值\"\n      }\n    ]\n  }\n}\n",[22,1860,1858],{"__ignoreMap":48},[105,1862,1863],{"id":1863},"业务规则错误",[16,1865,1866],{},"对于资源冲突或当前状态不允许操作的情况，可以返回更明确的业务错误：",[42,1868,1871],{"className":1869,"code":1870,"language":47,"meta":48},[45],"{\n  \"error\": {\n    \"code\": \"EMAIL_ALREADY_EXISTS\",\n    \"message\": \"该邮箱地址已被注册。\"\n  }\n}\n",[22,1872,1870],{"__ignoreMap":48},[16,1874,1875],{},"不要在错误响应中暴露密码、访问令牌、数据库连接串、完整第三方响应、内部堆栈或其他敏感信息。",[30,1877,1879],{"id":1878},"常见-json-校验误区","常见 JSON 校验误区",[105,1881,1883,1884,1886],{"id":1882},"误区-1jsonparse-成功等于接口数据有效","误区 1：",[22,1885,24],{}," 成功等于接口数据有效",[16,1888,1889,1891],{},[22,1890,24],{}," 只解决语法问题。它不会验证必填字段、类型、枚举值、数值范围或业务规则。",[105,1893,1895,1896],{"id":1894},"误区-2只看-content-type","误区 2：只看 ",[22,1897,1898],{},"Content-Type",[16,1900,1901,1903],{},[22,1902,815],{}," 说明服务端声称响应是 JSON，但不保证 body 一定能被解析，也不保证解析后的数据符合预期结构。",[16,1905,1906,1907,1909,1910,1188],{},"如果 API 响应在 ",[22,1908,24],{}," 阶段失败，可以先阅读 ",[239,1911,1187],{"href":241},[105,1913,1915],{"id":1914},"误区-3没有明确处理额外字段","误区 3：没有明确处理额外字段",[16,1917,1918],{},"如果接口只应接受固定字段，应考虑：",[42,1920,1923],{"className":1921,"code":1922,"language":47,"meta":48},[45],"{\n  \"additionalProperties\": false\n}\n",[22,1924,1922],{"__ignoreMap":48},[16,1926,1927],{},"但这并非所有 API 都适合。对于需要向前兼容、允许客户端传递扩展字段，或使用动态字段映射的接口，拒绝所有额外字段可能会过于严格。",[105,1929,1931],{"id":1930},"误区-4自动类型转换却没有记录规则","误区 4：自动类型转换，却没有记录规则",[16,1933,1694],{},[42,1935,1938],{"className":1936,"code":1937,"language":1276,"meta":48},[1274],"\"age\": \"36\"\n",[22,1939,1937],{"__ignoreMap":48},[16,1941,1942,1943,1945],{},"有的框架或工具会把它自动转换为数字 ",[22,1944,171],{},"，有的则直接校验失败。",[16,1947,1948],{},"自动转换可以改善部分表单体验，但也会让接口契约变模糊。若要启用，必须写入 API 文档并在前后端保持一致；对于安全、财务或关键业务字段，通常应更严格。",[105,1950,1952],{"id":1951},"误区-5只在前端校验","误区 5：只在前端校验",[16,1954,1955],{},"前端校验可以快速提示用户，但不能作为信任边界：",[58,1957,1958,1961,1964,1967],{},[61,1959,1960],{},"客户端 JavaScript 可以被绕过；",[61,1962,1963],{},"API 可以被脚本、curl、Postman 或其他客户端直接调用；",[61,1965,1966],{},"请求可能来自旧版本应用；",[61,1968,1969],{},"攻击者可以构造任意请求体。",[16,1971,1972],{},"因此，后端必须重新校验所有不可信输入。",[105,1974,1976],{"id":1975},"误区-6将完整无效请求体写入日志","误区 6：将完整无效请求体写入日志",[16,1978,1979],{},"排查问题时记录上下文很重要，但完整 payload 可能包含邮箱、电话、地址、Cookie、Authorization header、API Key 或其他隐私数据。",[16,1981,1982],{},"建议：",[58,1984,1985,1988,1991,1994],{},[61,1986,1987],{},"限制日志长度；",[61,1989,1990],{},"对 token、密码、邮箱等字段脱敏；",[61,1992,1993],{},"只记录必要的错误路径和请求 ID；",[61,1995,1996],{},"为敏感数据设置更严格的访问控制与保留策略。",[30,1998,2000],{"id":1999},"一个实用的-api-校验流程","一个实用的 API 校验流程",[16,2002,2003],{},"下面是一个适合大多数 JSON API 的基本流程：",[42,2005,2008],{"className":2006,"code":2007,"language":1276,"meta":48},[1274],"接收请求\n→ 限制 body 大小\n→ 以 UTF-8 解码请求体\n→ 尝试解析 JSON\n→ 返回语法错误，或继续\n→ 用 Schema 校验数据结构\n→ 返回字段级错误，或继续\n→ 执行业务与权限校验\n→ 返回业务错误，或继续\n→ 执行业务操作并返回成功响应\n",[22,2009,2007],{"__ignoreMap":48},[16,2011,2012,2013,2015,2016,2019],{},"如果请求数据很复杂，可以先用 ",[239,2014,1369],{"href":252}," 整理结构，再用 ",[239,2017,2018],{"href":996},"JSONPath 测试工具"," 检查嵌套字段和数组层级。",[30,2021,2022],{"id":2022},"常见问题",[105,2024,2026,2028],{"id":2025},"jsonparse-能校验-json-schema-吗",[22,2027,24],{}," 能校验 JSON Schema 吗？",[16,2030,2031,2032,2034],{},"不能。",[22,2033,24],{}," 只将符合 JSON 语法的字符串转换为 JavaScript 值，不会验证必填字段、字段类型、允许值、数据范围或额外字段。",[105,2036,2038],{"id":2037},"合法-json-一定能被-api-接受吗","合法 JSON 一定能被 API 接受吗？",[16,2040,2041],{},"不一定。它可能语法正确，但缺少字段、类型不对、数值超范围、枚举值不允许，或者不符合权限、唯一性、库存和资源状态等业务规则。",[105,2043,2045,2047],{"id":2044},"format-email-一定会校验邮箱吗",[22,2046,379],{}," 一定会校验邮箱吗？",[16,2049,2050,2051,2054,2055,2060],{},"不一定。是否强制校验取决于 JSON Schema validator 及其配置。以 ",[239,2052,492],{"href":490,"rel":2053},[272]," 为例，常见 formats 由 ",[239,2056,2058],{"href":496,"rel":2057},[272],[22,2059,500],{}," 提供，因此应确认项目已经正确注册对应插件。",[105,2062,2064],{"id":2063},"前端和后端都需要校验-json-吗","前端和后端都需要校验 JSON 吗？",[16,2066,2067],{},"建议两端都做，但职责不同。前端校验主要改善交互体验，帮助用户更早发现问题；后端校验负责安全和数据完整性，必须始终执行。",[105,2069,2071],{"id":2070},"json-schema-和-typescript-有什么区别","JSON Schema 和 TypeScript 有什么区别？",[16,2073,2074],{},"TypeScript 类型主要帮助开发阶段的静态检查，通常在编译后不会自动校验运行时传入的数据。JSON Schema 描述的是运行时数据应满足的结构与约束，可用于验证 HTTP 请求、Webhook、配置文件和第三方 API 数据。",[30,2076,2077],{"id":2077},"总结",[16,2079,2080],{},"JSON 校验不是单一的\"能否解析\"检查：",[432,2082,2083,2088,2094],{},[61,2084,2085,2087],{},[437,2086,1165],{},"：确认文本是否为合法 JSON；",[61,2089,2090,2093],{},[437,2091,2092],{},"Schema 校验","：确认字段、类型、约束和嵌套结构是否符合接口契约；",[61,2095,2096,2098],{},[437,2097,1177],{},"：确认数据在当前权限、资源和系统状态下是否可被接受。",[16,2100,2101,2103],{},[22,2102,24],{}," 成功只是第一步。对于 API、Webhook、配置文件和第三方输入，应该将语法、Schema 和业务规则校验组合起来，并在后端重新执行关键校验。",[1190,2105,2106],{},[16,2107,2108,2109,2111,2112,1188],{},"想快速确认一段文本是否为合法 JSON，可以先使用 ",[239,2110,1197],{"href":620},"。如果错误发生在 API 响应解析阶段，查看 ",[239,2113,1187],{"href":241},[30,2115,2116],{"id":2116},"下一步",[58,2118,2119,2129,2138,2147],{},[61,2120,2121,2124,2125,2128],{},[437,2122,2123],{},"已有解析失败的 JSON？"," 先看",[239,2126,2127],{"href":241},"接口调试中的 10 个典型 JSON 错误","，找到并修复语法错误。",[61,2130,2131,2134,2135,2137],{},[437,2132,2133],{},"需要用 Schema 校验 JSON？"," 用 ",[239,2136,1197],{"href":620}," 把数据和 Schema 分别粘贴，查看校验结果。",[61,2139,2140,993,2143,2146],{},[437,2141,2142],{},"想查看复杂 JSON 的结构？",[239,2144,2145],{"href":996},"用交互式树形视图浏览 JSON","，探索嵌套字段。",[61,2148,2149,2152,2153,2156],{},[437,2150,2151],{},"从零构建 API？"," 看我们的 ",[239,2154,2155],{"href":1007},"JSON 最佳实践"," 指南，涵盖错误处理、校验和响应设计。",[1011,2158],{},[16,2160,2161],{},[1016,2162,2163],{},"JSON Toolbox 的所有工具完全在浏览器中运行，数据不会离开你的设备。",{"title":48,"searchDepth":1020,"depth":1020,"links":2165},[2166,2167,2172,2177,2178,2182,2187,2197,2198,2207,2208],{"id":1201,"depth":1020,"text":1202},{"id":1284,"depth":1020,"text":1285,"children":2168},[2169,2170],{"id":1317,"depth":1026,"text":1318},{"id":1376,"depth":1026,"text":2171},"JSON.parse() 没有检查什么？",{"id":1442,"depth":1020,"text":1443,"children":2173},[2174,2175],{"id":1472,"depth":1026,"text":1472},{"id":1620,"depth":1026,"text":2176},"format: \"email\" 不一定总会强制校验",{"id":1687,"depth":1020,"text":1688},{"id":1742,"depth":1020,"text":1743,"children":2179},[2180,2181],{"id":1746,"depth":1026,"text":1747},{"id":1773,"depth":1026,"text":1774},{"id":1815,"depth":1020,"text":1816,"children":2183},[2184,2185,2186],{"id":1826,"depth":1026,"text":1827},{"id":1842,"depth":1026,"text":1843},{"id":1863,"depth":1026,"text":1863},{"id":1878,"depth":1020,"text":1879,"children":2188},[2189,2191,2193,2194,2195,2196],{"id":1882,"depth":1026,"text":2190},"误区 1：JSON.parse() 成功等于接口数据有效",{"id":1894,"depth":1026,"text":2192},"误区 2：只看 Content-Type",{"id":1914,"depth":1026,"text":1915},{"id":1930,"depth":1026,"text":1931},{"id":1951,"depth":1026,"text":1952},{"id":1975,"depth":1026,"text":1976},{"id":1999,"depth":1020,"text":2000},{"id":2022,"depth":1020,"text":2022,"children":2199},[2200,2202,2203,2205,2206],{"id":2025,"depth":1026,"text":2201},"JSON.parse() 能校验 JSON Schema 吗？",{"id":2037,"depth":1026,"text":2038},{"id":2044,"depth":1026,"text":2204},"format: \"email\" 一定会校验邮箱吗？",{"id":2063,"depth":1026,"text":2064},{"id":2070,"depth":1026,"text":2071},{"id":2077,"depth":1020,"text":2077},{"id":2116,"depth":1020,"text":2116},"了解 JSON 语法校验、JSON Schema 结构校验与业务规则校验的区别，并通过 JavaScript 示例构建更可靠的 API 数据校验流程。","\u002Fblog\u002Fcover\u002Fzh\u002Fjson-validation-syntax-vs-schema-cover.svg",[1077,1076],{},"\u002Fzh\u002Fblog\u002Fjson-validation-syntax-vs-schema",{"slug":1082,"text":2215,"btn":2216},"先检查 JSON 语法是否正确：","打开 JSON 校验工具",{"title":1096,"description":2209},"zh\u002Fblog\u002Fjson-validation-syntax-vs-schema",[1088,2220,273,1090,2221,2222],"JSON 校验","API 校验","数据校验","Xp9In5hPd63uHo5ihbSQBR6uwtqyava5GnL8TeMtZkQ",1791273858399]