⚠️ Malware / Trojaner / VirenAndroid-Malware blockiert Google Play per VPN-Trick(24.08.2026 um 13:00 Uhr)
🪟 Windows TippsWindows 11: Falsche Defender-Warnungen und kaputte Mauszeiger(31.08.2026 um 11:58 Uhr)
🕵️ SicherheitslückenDropbox-Hack: Tausende Konten kompromittiert(02.09.2026 um 11:02 Uhr)
⚠️ Malware / Trojaner / VirenAtombomben-Frage trickst KI-Malware-Scanner aus(02.09.2026 um 12:46 Uhr)
🪟 Windows TippsMehr Sicherheit in Windows 11(03.09.2026 um 11:51 Uhr)
⚠️ Malware / Trojaner / VirenNeue Android-Malware schreit Sie an, wenn Sie nicht zahlen(11.09.2026 um 10:33 Uhr)
🐧 Linux TippsMehrere Probleme in freerdp2 (Fedora)(11.09.2026 um 23:24 Uhr)
🐧 Linux TippsMehrere Probleme in kamailio (Debian)(11.09.2026 um 23:28 Uhr)
🐧 Linux TippsAusführen beliebiger Kommandos in dokuwiki (Fedora)(11.09.2026 um 23:28 Uhr)
🐧 Linux TippsAusführen beliebiger Kommandos in python-asteval (Fedora)(11.09.2026 um 23:28 Uhr)
⚠️ Malware / Trojaner / VirenAndroid-Malware blockiert Google Play per VPN-Trick(24.08.2026 um 13:00 Uhr)
🪟 Windows TippsWindows 11: Falsche Defender-Warnungen und kaputte Mauszeiger(31.08.2026 um 11:58 Uhr)
🕵️ SicherheitslückenDropbox-Hack: Tausende Konten kompromittiert(02.09.2026 um 11:02 Uhr)
⚠️ Malware / Trojaner / VirenAtombomben-Frage trickst KI-Malware-Scanner aus(02.09.2026 um 12:46 Uhr)
🪟 Windows TippsMehr Sicherheit in Windows 11(03.09.2026 um 11:51 Uhr)
⚠️ Malware / Trojaner / VirenNeue Android-Malware schreit Sie an, wenn Sie nicht zahlen(11.09.2026 um 10:33 Uhr)
🐧 Linux TippsMehrere Probleme in freerdp2 (Fedora)(11.09.2026 um 23:24 Uhr)
🐧 Linux TippsMehrere Probleme in kamailio (Debian)(11.09.2026 um 23:28 Uhr)
🐧 Linux TippsAusführen beliebiger Kommandos in dokuwiki (Fedora)(11.09.2026 um 23:28 Uhr)
🐧 Linux TippsAusführen beliebiger Kommandos in python-asteval (Fedora)(11.09.2026 um 23:28 Uhr)

🔧 Programmierung 🕛 vor 2 Monaten 6 Min Lesezeit
0

I Built a Browser-Only JSON Schema Validator — Draft-07, $ref, allOf/anyOf/oneOf, if/then/else, 173 Tests

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht




I Built a Browser-Only JSON Schema Validator — Draft-07, $ref, allOf/anyOf/oneOf, if/then/else, 173 Tests



JSON Schema validation usually means pulling in ajv or a similar library. That's entirely reasonable for production code — but for quick schema checking, debugging, or learning how Draft-07 keywords work, you don't want to spin up a Node project just to paste in some JSON.



So I built a zero-dependency browser tool that implements the Draft-07 spec from scratch.



Live tool →







What it validates



The tool implements a solid subset of JSON Schema Draft-07:




















































Keyword group Keywords
Type system
type (string, number, integer, boolean, array, object, null, arrays of types)
Enumeration
enum, const
String
minLength, maxLength, pattern, format
Number
minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf
Array
items (schema + tuple), additionalItems, minItems, maxItems, uniqueItems, contains
Object
required, properties, additionalProperties, patternProperties, minProperties, maxProperties, dependencies, propertyNames
Combining
allOf, anyOf, oneOf, not
Conditionals
if / then / else
References
$ref (local: #/$defs/..., #/definitions/...)
Formats
email, uri, date, date-time, ipv4, uuid


Every error includes:





  • JSON path to the failing value (e.g. user.address.zipCode)


  • Human-readable message (e.g. String does not match pattern "^\d{5}$")


  • Keyword badge so you know exactly which schema rule failed







Architecture: pure recursive validator



The entire validator is a single recursive function validate(data, schema, path, rootSchema). It takes a value, a schema object, the current path string, and the root schema (for $ref resolution), and returns an array of ValidationError objects.




CODE
/**
* @typedef {{ path: string, message: string, keyword: string }} ValidationError
*/


function validate(data, schema, path, rootSchema) {
path = path || '';
rootSchema = rootSchema || schema;

if (schema === true) return [];
if (schema === false) return [{ path, message: 'Schema is false', keyword: 'false schema' }];

const errors = [];

// $ref short-circuits all other keywords (Draft-07 spec)
if (schema.$ref) {
const resolved = resolveRef(schema.$ref, rootSchema);
if (resolved) return validate(data, resolved, path, rootSchema);
return [{ path, message: `Cannot resolve $ref "${schema.$ref}"`, keyword: '$ref' }];
}

// Type check first — no point checking string keywords on a number
if (schema.type !== undefined && !typeMatches(data, schema.type)) {
const expected = Array.isArray(schema.type) ? schema.type.join(' | ') : schema.type;
return [{ path, message: `Expected "${expected}" but got "${getType(data)}"`, keyword: 'type' }];
}

if (typeof data === 'string') errors.push(...validateString(data, schema, path));
if (typeof data === 'number') errors.push(...validateNumber(data, schema, path));
if (Array.isArray(data)) errors.push(...validateArray(data, schema, path, rootSchema));
if (isPlainObject(data)) errors.push(...validateObject(data, schema, path, rootSchema));

// Combiners
if (schema.allOf) schema.allOf.forEach(s => errors.push(...validate(data, s, path, rootSchema)));
if (schema.anyOf && !schema.anyOf.some(s => validate(data, s, path, rootSchema).length === 0))
errors.push({ path, message: 'Does not match any schema in "anyOf"', keyword: 'anyOf' });
// ...oneOf, not, if/then/else similarly

return errors;
}






The early return on type mismatch is important: there's no value in reporting "minLength" requires 5 characters when the value isn't even a string.









Handling exclusiveMinimum/Maximum across drafts



Draft-04 uses boolean exclusiveMinimum / exclusiveMaximum paired with minimum / maximum. Draft-06/07 changed them to be standalone numbers. The validator handles both:




CODE
function validateNumber(val, schema, path) {
const errors = [];

if (typeof schema.minimum === 'number') {
if (schema.exclusiveMinimum === true) {
// Draft-04 style
if (val <= schema.minimum)
errors.push({ path, message: `${val} must be > ${schema.minimum}`, keyword: 'exclusiveMinimum' });
} else {
if (val < schema.minimum)
errors.push({ path, message: `${val} must be >= ${schema.minimum}`, keyword: 'minimum' });
}
}

// Draft-06/07 style: exclusiveMinimum as a number
if (typeof schema.exclusiveMinimum === 'number') {
if (val <= schema.exclusiveMinimum)
errors.push({ path, message: `${val} must be > ${schema.exclusiveMinimum}`, keyword: 'exclusiveMinimum' });
}

// ... same pattern for maximum / exclusiveMaximum

return errors;
}






This means schemas from older tooling still work correctly.









$ref resolution



Local $ref values like #/$defs/Address or #/definitions/UUID are resolved by walking the JSON pointer path from the root schema:




CODE
function resolveRef(ref, rootSchema) {
if (!ref.startsWith('#')) return null; // external refs not supported
const fragment = ref.slice(1);
if (fragment === '' || fragment === '/') return rootSchema; // bare # → root
const parts = fragment.slice(1)
.split('/')
.map(p => p.replace(/~1/g, '/').replace(/~0/g, '~')); // JSON Pointer unescaping
let cur = rootSchema;
for (const part of parts) {
if (cur === null || typeof cur !== 'object') return null;
cur = cur[part];
}
return cur !== undefined ? cur : null;
}






Per the Draft-07 spec, when a schema has a $ref, all sibling keywords are ignored — $ref short-circuits everything else.









if/then/else



One of the more useful Draft-07 additions is conditional validation:




CODE
if (schema.if !== undefined) {
const ifErrors = validate(data, schema.if, path, rootSchema);
if (ifErrors.length === 0) {
// condition passed → apply then
if (schema.then !== undefined)
errors.push(...validate(data, schema.then, path, rootSchema));
} else {
// condition failed → apply else
if (schema.else !== undefined)
errors.push(...validate(data, schema.else, path, rootSchema));
}
}






This lets you write schemas like "if the type field is 'credit_card', then cardNumber is required" without splitting into separate schemas.









Testing: 173 tests, zero frameworks



The same vm-based extraction technique I use across all DevNest.io tools:




CODE
const vm = require('vm');
const modObj = { exports: {} };
vm.runInContext(extractedScript, vm.createContext({
module: modObj, exports: modObj.exports,
window: { addEventListener: () => {} },
document: { getElementById: () => ({}), querySelectorAll: () => [], addEventListener: () => {} },
Date, JSON, RegExp, Number, Math, Array, Object, String, Boolean, console
}));

const { validate, getType, typeMatches, isValidEmail, ... } = modObj.exports;






Tests cover:




































































Function Tests
getType 10 — null, boolean, integer vs number, string, array, object
typeMatches 15 — single types, type arrays, integer/number coercion
isValidEmail 10 — valid addresses, missing parts, spaces, double @
isValidUri 8 — schemes, missing scheme, bad scheme start
isValidDate 10 — valid dates, leap years, out-of-range months/days
isValidDateTime 8 — ISO 8601, offsets, milliseconds, invalid formats
isValidIPv4 8 — valid IPs, out-of-range octets, wrong part count
isValidUUID 8 — v1/v4, wrong variant, no hyphens
validateString 15 — minLength, maxLength, pattern, all formats
validateNumber 15 — min/max, exclusive variants (bool + number), multipleOf
validateArray 12 — minItems, maxItems, uniqueItems, items schema, tuple, additionalItems, contains
validateObject 15 — required, properties, additionalProperties, minProperties, maxProperties, patternProperties, dependencies
resolveRef 8 — $defs, definitions, # root, ~1 escaping

validate (integration)
30 — all major keywords end-to-end


All 173 pass with node test/test.js.









UI design decisions



Split pane layout: JSON on the left, Schema on the right, results spanning the full width below. Ctrl+Enter triggers validation from either pane.



Parse errors are shown per-pane: if your JSON is malformed, you see the parse error under the JSON pane immediately — before even clicking Validate.



Error cards show three pieces of information: the JSON path (in monospace, highlighted), the human message, and the failing keyword as a chip. This mirrors how ajv formats errors, making it easier to cross-reference against the spec.



Three built-in examples: User (a well-formed object), Product (nested object + pattern properties), and Invalid (the User schema with deliberate violations) — so you can explore the tool immediately without having to write a schema from scratch.









Try it



— a growing collection of browser-only developer tools. Currently 27 tools and counting.

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
Stealing AI Reasoning Traces
1 Quelle
AIs as Modern Genies
1 Quelle
Bitcoin: KI-Hacker räumen Millionen ab! Wird Künstliche Intelligenz zum Problem? - ftd.de
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten I Built a Browser-Only JSON Schema Validator — Draft-07, $ref, allOf/anyOf/oneOf, if/then/else, 173 Tests

Thematisch verwandte Begriffe: Built, BrowserOnly, JSON, Schema · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...