json-utils.ts 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
  1. /**
  2. * Safely parses JSON from a string, handling markdown code blocks and leading/trailing text.
  3. */
  4. export function safeParseJson<T = any>(text: string): T | null {
  5. if (!text) return null;
  6. let jsonStr = text.trim();
  7. // 1. Try to extract JSON from markdown code blocks if they exist
  8. // Matches ```json ... ``` or just ``` ... ```
  9. const codeBlockRegex = /```(?:json)?\s*([\s\S]*?)\s*```/i;
  10. const match = jsonStr.match(codeBlockRegex);
  11. if (match && match[1]) {
  12. jsonStr = match[1].trim();
  13. } else {
  14. // 2. If no markdown block, try to find the start and end of JSON characters
  15. // This handles cases where the AI adds introductory or concluding text outside the block
  16. const firstOpenBrace = jsonStr.indexOf('{');
  17. const firstOpenBracket = jsonStr.indexOf('[');
  18. let startIndex = -1;
  19. if (firstOpenBrace !== -1 && (firstOpenBracket === -1 || firstOpenBrace < firstOpenBracket)) {
  20. startIndex = firstOpenBrace;
  21. } else if (firstOpenBracket !== -1) {
  22. startIndex = firstOpenBracket;
  23. }
  24. if (startIndex !== -1) {
  25. const lastCloseBrace = jsonStr.lastIndexOf('}');
  26. const lastCloseBracket = jsonStr.lastIndexOf(']');
  27. const endIndex = Math.max(lastCloseBrace, lastCloseBracket);
  28. if (endIndex !== -1 && endIndex > startIndex) {
  29. jsonStr = jsonStr.substring(startIndex, endIndex + 1);
  30. }
  31. }
  32. }
  33. try {
  34. return JSON.parse(jsonStr) as T;
  35. } catch (error) {
  36. console.error('[safeParseJson] Failed to parse JSON:', error);
  37. console.error('[safeParseJson] Original text:', text);
  38. console.error('[safeParseJson] Extracted string:', jsonStr);
  39. return null;
  40. }
  41. }