Skip to content

Instantly share code, notes, and snippets.

@TiagoGouvea
Last active February 1, 2026 18:21
Show Gist options
  • Select an option

  • Save TiagoGouvea/6ddd3e15ce1552d199f1fd1e1dd1e575 to your computer and use it in GitHub Desktop.

Select an option

Save TiagoGouvea/6ddd3e15ce1552d199f1fd1e1dd1e575 to your computer and use it in GitHub Desktop.
Extracts Json Objects from strings, fix missing root atributes, and so
import { jsonrepair } from 'jsonrepair';
/**
* Extracts and parses the first JSON object from a string.
* Extra text: trims prefix/suffix chatter around a JSON payload.
* Concatenated JSONs: returns the first balanced object in a sequence.
* Minor glitches: attempts repair for missing quotes/commas via jsonrepair.
*/
export function extractJsonObjectFromString(inputString: string) {
let cleanedInputString, repaired;
try {
cleanedInputString = extractOnlyJson(inputString);
// Strategy 1: Try JSON.parse directly first (fast path for valid JSON).
const possibleJsons = findAllPossibleJsons(cleanedInputString);
for (const jsonCandidate of possibleJsons) {
// Try native JSON.parse first (much faster, no compiled code).
try {
const json = JSON.parse(jsonCandidate);
return json;
} catch (parseError) {
// If native parse fails, try jsonrepair (slower, last resort).
try {
repaired = jsonrepair(jsonCandidate);
const json = JSON.parse(repaired);
return json;
} catch (repairError) {
continue;
}
}
}
// Strategy 2: Fallback to the original approach if no candidates worked.
const originalResult = findFirstCompleteJson(cleanedInputString);
if (originalResult) {
try {
const json = JSON.parse(originalResult);
return json;
} catch (parseError) {
repaired = jsonrepair(originalResult);
const json = JSON.parse(repaired);
return json;
}
}
throw new Error('No JSON found in the string.');
} catch (error: any) {
throw new Error('Failed to parse JSON into object: ' + error.message);
}
}
/** Extracts the substring between the first and last braces. */
function extractOnlyJson(str: string) {
const start = str.indexOf('{');
const end = str.lastIndexOf('}') + 1; // Include the closing brace.
if (start !== -1 && end !== -1) {
return str.slice(start, end);
} else {
return 'Invalid input: no braces found.';
}
}
/** Checks whether the string may contain JSON braces. */
export const hasPossibleJson = (str: string) => {
const start = str.indexOf('{');
const end = str.lastIndexOf('}') + 1; // Include the closing brace.
return start > -1 && end > 1;
};
/** Validates whether the string is valid JSON. */
export function isJsonString(str: string) {
try {
JSON.parse(str);
return true;
} catch (e) {
return false;
}
}
/** Finds every complete JSON object in the input. */
function findAllPossibleJsons(input: string): string[] {
const candidates: string[] = [];
// Look for all occurrences of opening braces.
for (let i = 0; i < input.length; i++) {
if (input[i] === '{') {
const jsonCandidate = findCompleteJsonStartingAt(input, i);
if (jsonCandidate) {
candidates.push(jsonCandidate);
}
}
}
return candidates;
}
/** Returns a balanced JSON object starting at a given index. */
function findCompleteJsonStartingAt(
input: string,
startIndex: number,
): string | null {
let braceCount = 0;
let inString = false;
let escapeNext = false;
for (let i = startIndex; i < input.length; i++) {
const char = input[i];
if (escapeNext) {
escapeNext = false;
continue;
}
if (char === '\\') {
escapeNext = true;
continue;
}
if (char === '"' && !escapeNext) {
inString = !inString;
continue;
}
if (!inString) {
if (char === '{') {
braceCount++;
} else if (char === '}') {
braceCount--;
if (braceCount === 0) {
return input.substring(startIndex, i + 1);
}
}
}
}
return null; // No complete JSON found.
}
/** Finds the first complete JSON object in the input. */
function findFirstCompleteJson(input: string): string | null {
let braceCount = 0;
let startIndex = -1;
for (let i = 0; i < input.length; i++) {
const char = input[i];
if (char === '{') {
if (startIndex === -1) startIndex = i;
braceCount++;
} else if (char === '}') {
braceCount--;
if (braceCount === 0 && startIndex !== -1) {
return input.substring(startIndex, i + 1);
}
}
}
return null;
}
import { z } from 'zod';
export function wrapRootIfMissing(parsed: any, schema: z.ZodTypeAny): any {
if (!(schema instanceof z.ZodObject)) return parsed;
const shape = schema.shape;
const rootKeys = Object.keys(shape);
if (rootKeys.length !== 1) return parsed;
const rootKey = rootKeys[0]!;
const rootSchema = shape[rootKey];
if (!(rootSchema instanceof z.ZodObject)) return parsed;
if (parsed && typeof parsed === 'object' && rootKey in parsed) return parsed;
const childShape = rootSchema.shape;
const childKeys = Object.keys(childShape);
const hasAllChildren =
parsed && typeof parsed === 'object' && childKeys.every((k) => k in parsed);
if (hasAllChildren) {
console.log(
'🤩 🧱 wrapRootIfMissing: wrapped root',
rootKey,
'with child keys',
childKeys,
);
return { [rootKey]: parsed };
}
return parsed;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment