JsonhPy 2.3__tar.gz → 2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: JsonhPy
3
- Version: 2.3
3
+ Version: 2.4
4
4
  Summary: JSON for Humans in Python.
5
5
  Author-email: Joyless <joyless.mod@gmail.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "JsonhPy"
3
- version = "2.3"
3
+ version = "2.4"
4
4
  authors = [
5
5
  { name="Joyless", email="joyless.mod@gmail.com" },
6
6
  ]
@@ -1,6 +1,7 @@
1
1
  import math
2
2
  from enum import Enum
3
3
  from typing import Iterator, Iterable
4
+ import json
4
5
 
5
6
  class JsonhResult[T, E]:
6
7
  is_error: bool
@@ -557,6 +558,132 @@ class JsonhReader:
557
558
 
558
559
  return next_element
559
560
 
561
+ def parse_json(self, include_comments: bool = False, indent: str | None = None) -> JsonhResult[str, str]:
562
+ """
563
+ Parses a single element as JSON from the reader.
564
+
565
+ If `include_comments` is true, comments are included (`/*` and `*/` are escaped as `/ *` and `* /`).
566
+
567
+ If `indent` is not null, the output is pretty-printed with the given indentation.
568
+
569
+ The result is not safe to embed in HTML.
570
+ """
571
+
572
+ current_depth: int = 0
573
+ isStartOfStructure: bool = True
574
+ is_property_value: bool = False
575
+
576
+ resultBuilder: str = ""
577
+
578
+ for token_result in self.read_element():
579
+ # Check error
580
+ if (token_result.is_error):
581
+ return JsonhResult.from_error(token_result.error())
582
+ token: JsonhToken = token_result.value()
583
+
584
+ # Add comments and indents
585
+ if not is_property_value:
586
+ # Add comma before property/item
587
+ if (token.json_type not in [JsonTokenType.NONE, JsonTokenType.COMMENT]) and (current_depth > 0) and (not isStartOfStructure):
588
+ # Don't add trailing comma
589
+ if token.json_type not in [JsonTokenType.END_OBJECT, JsonTokenType.END_ARRAY]:
590
+ resultBuilder += ','
591
+
592
+ # Apply indentation
593
+ if indent != None:
594
+ # Don't indent inside empty structures
595
+ if not (token.json_type in [JsonTokenType.END_OBJECT, JsonTokenType.END_ARRAY] and isStartOfStructure):
596
+ # Don't indent comment if not included
597
+ if (not (token.json_type == JsonTokenType.COMMENT and (not include_comments))):
598
+ # Don't indent root elements
599
+ if current_depth > 0:
600
+ # Add newline before element
601
+ resultBuilder += '\n'
602
+
603
+ # Get current indent count
604
+ indent_count: int = current_depth
605
+ if token.json_type in [JsonTokenType.END_OBJECT, JsonTokenType.END_ARRAY]:
606
+ indent_count -= 1
607
+
608
+ # Add indent
609
+ for _ in range(0, indent_count):
610
+ resultBuilder += indent
611
+ # Track start of structure to avoid adding leading comma
612
+ if token.json_type not in [JsonTokenType.NONE, JsonTokenType.COMMENT]:
613
+ isStartOfStructure = False
614
+ if token.json_type in [JsonTokenType.START_OBJECT, JsonTokenType.START_ARRAY]:
615
+ isStartOfStructure = True
616
+
617
+ match token.json_type:
618
+ # Null
619
+ case JsonTokenType.NONE:
620
+ resultBuilder += "null"
621
+ if current_depth == 0:
622
+ return JsonhResult.from_value(resultBuilder)
623
+ # True
624
+ case JsonTokenType.TRUE:
625
+ resultBuilder += "true"
626
+ if current_depth == 0:
627
+ return JsonhResult.from_value(resultBuilder)
628
+ # False
629
+ case JsonTokenType.FALSE:
630
+ resultBuilder += "false"
631
+ if current_depth == 0:
632
+ return JsonhResult.from_value(resultBuilder)
633
+ # String
634
+ case JsonTokenType.STRING:
635
+ resultBuilder += json.dumps(token.value, ensure_ascii=False)
636
+ if current_depth == 0:
637
+ return JsonhResult.from_value(resultBuilder)
638
+ # Number
639
+ case JsonTokenType.NUMBER:
640
+ result: JsonhResult[float] = JsonhNumberParser.parse(token_result.value().value)
641
+ if (result.is_error):
642
+ return JsonhResult.from_error(result.error())
643
+ resultBuilder += str(result.value())
644
+ if current_depth == 0:
645
+ return JsonhResult.from_value(resultBuilder)
646
+ # Start Object
647
+ case JsonTokenType.START_OBJECT:
648
+ resultBuilder += '{'
649
+ current_depth += 1
650
+ # Start Array
651
+ case JsonTokenType.START_ARRAY:
652
+ resultBuilder += '['
653
+ current_depth += 1
654
+ # End Object
655
+ case JsonTokenType.END_OBJECT:
656
+ resultBuilder += '}'
657
+ current_depth -= 1
658
+ if current_depth == 0:
659
+ return JsonhResult.from_value(resultBuilder)
660
+ # End Array
661
+ case JsonTokenType.END_ARRAY:
662
+ resultBuilder += ']'
663
+ current_depth -= 1
664
+ if current_depth == 0:
665
+ return JsonhResult.from_value(resultBuilder)
666
+ # Property Name
667
+ case JsonTokenType.PROPERTY_NAME:
668
+ resultBuilder += json.dumps(token.value, ensure_ascii=False)
669
+ resultBuilder += ':'
670
+ if indent != None:
671
+ resultBuilder += ' '
672
+ # Comment
673
+ case JsonTokenType.COMMENT:
674
+ if (include_comments):
675
+ resultBuilder += "/*"
676
+ resultBuilder += token.value.replace("/*", "/ *").replace("*/", "* /")
677
+ resultBuilder += "*/"
678
+ # Not implemented
679
+ case _:
680
+ return JsonhResult.from_error("Token type not implemented")
681
+
682
+ is_property_value = token.json_type == JsonTokenType.PROPERTY_NAME
683
+
684
+ # End of input
685
+ return JsonhResult.from_error("Expected token, got end of input")
686
+
560
687
  def find_property_value(self, property_name: str) -> bool:
561
688
  """
562
689
  Tries to find the given property name in the reader.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: JsonhPy
3
- Version: 2.3
3
+ Version: 2.4
4
4
  Summary: JSON for Humans in Python.
5
5
  Author-email: Joyless <joyless.mod@gmail.com>
6
6
  License-Expression: MIT
File without changes
File without changes
File without changes
File without changes