validate_json.py 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. #!/usr/bin/env python3
  2. import re
  3. import sys
  4. from pathlib import Path
  5. from pprint import pprint
  6. import json5
  7. import jstyleson
  8. import yaml
  9. # 'json', 'json5' or 'yaml'
  10. # json: strict, but doesn't preserve line numbers necessarily, since it strips comments before parsing
  11. # json5: strict and preserves line numbers even for files with line comments
  12. # yaml: less strict, allows e.g. leading zeros
  13. VALIDATION_TYPE = "json5"
  14. errors = []
  15. for path in sorted(Path(".").glob("**/*.json")):
  16. # because path is an object and not a string
  17. path_str = str(path)
  18. try:
  19. with open(path_str, "r") as file:
  20. if VALIDATION_TYPE == "json":
  21. jstyleson.load(file)
  22. elif VALIDATION_TYPE == "json5":
  23. json5.load(file)
  24. elif VALIDATION_TYPE == "yaml":
  25. file = file.read().replace("\t", " ")
  26. file = file.replace("//", "#")
  27. yaml.safe_load(file)
  28. print(f"Validation of {path_str} succeeded")
  29. except Exception as exc:
  30. print(f"Validation of {path_str} failed")
  31. pprint(exc)
  32. error_pos = path_str
  33. # create error position strings for each type of parser
  34. if hasattr(exc, "pos"):
  35. # 'json'
  36. # https://stackoverflow.com/a/72850269/2278742
  37. error_pos = f"{path_str}:{exc.lineno}:{exc.colno}"
  38. print(error_pos)
  39. elif VALIDATION_TYPE == "json5":
  40. # 'json5'
  41. pos = re.findall(r"\d+", str(exc))
  42. error_pos = f"{path_str}:{pos[0]}:{pos[-1]}"
  43. elif hasattr(exc, "problem_mark"):
  44. # 'yaml'
  45. mark = exc.problem_mark
  46. error_pos = f"{path_str}:{mark.line+1}:{mark.column+1}"
  47. print(error_pos)
  48. errors.append({"error_pos": error_pos, "error_msg": exc})
  49. if errors:
  50. print("The following JSON files are invalid:")
  51. pprint(errors)
  52. sys.exit(1)