21
21
// / - a convention and helpers for mapping between json::Value and user-defined
22
22
// / types. See fromJSON(), ObjectMapper, and the class comment on Value.
23
23
// /
24
+ // / - an output API json::OStream which can emit JSON without materializing
25
+ // / all structures as json::Value.
26
+ // /
24
27
// / Typically, JSON data would be read from an external source, parsed into
25
28
// / a Value, and then converted into some native data structure before doing
26
29
// / real work on it. (And vice versa when writing).
@@ -437,11 +440,6 @@ class Value {
437
440
return LLVM_LIKELY (Type == T_Array) ? &as<json::Array>() : nullptr ;
438
441
}
439
442
440
- // / Serializes this Value to JSON, writing it to the provided stream.
441
- // / The formatting is compact (no extra whitespace) and deterministic.
442
- // / For pretty-printing, use the formatv() format_provider below.
443
- friend llvm::raw_ostream &operator <<(llvm::raw_ostream &, const Value &);
444
-
445
443
private:
446
444
void destroy ();
447
445
void copyFrom (const Value &M);
@@ -462,9 +460,7 @@ class Value {
462
460
return *static_cast <T *>(Storage);
463
461
}
464
462
465
- template <typename Indenter>
466
- void print (llvm::raw_ostream &, const Indenter &) const ;
467
- friend struct llvm ::format_provider<llvm::json::Value>;
463
+ friend class OStream ;
468
464
469
465
enum ValueType : char {
470
466
T_Null,
@@ -486,7 +482,6 @@ class Value {
486
482
487
483
bool operator ==(const Value &, const Value &);
488
484
inline bool operator !=(const Value &L, const Value &R) { return !(L == R); }
489
- llvm::raw_ostream &operator <<(llvm::raw_ostream &, const Value &);
490
485
491
486
// / ObjectKey is a used to capture keys in Object. Like Value but:
492
487
// / - only strings are allowed
@@ -699,6 +694,152 @@ class ParseError : public llvm::ErrorInfo<ParseError> {
699
694
return llvm::inconvertibleErrorCode ();
700
695
}
701
696
};
697
+
698
+ // / json::OStream allows writing well-formed JSON without materializing
699
+ // / all structures as json::Value ahead of time.
700
+ // / It's faster, lower-level, and less safe than OS << json::Value.
701
+ // /
702
+ // / Only one "top-level" object can be written to a stream.
703
+ // / Simplest usage involves passing lambdas (Blocks) to fill in containers:
704
+ // /
705
+ // / json::OStream J(OS);
706
+ // / J.array([&]{
707
+ // / for (const Event &E : Events)
708
+ // / J.object([&] {
709
+ // / J.attribute("timestamp", int64_t(E.Time));
710
+ // / J.attributeArray("participants", [&] {
711
+ // / for (const Participant &P : E.Participants)
712
+ // / J.string(P.toString());
713
+ // / });
714
+ // / });
715
+ // / });
716
+ // /
717
+ // / This would produce JSON like:
718
+ // /
719
+ // / [
720
+ // / {
721
+ // / "timestamp": 19287398741,
722
+ // / "participants": [
723
+ // / "King Kong",
724
+ // / "Miley Cyrus",
725
+ // / "Cleopatra"
726
+ // / ]
727
+ // / },
728
+ // / ...
729
+ // / ]
730
+ // /
731
+ // / The lower level begin/end methods (arrayBegin()) are more flexible but
732
+ // / care must be taken to pair them correctly:
733
+ // /
734
+ // / json::OStream J(OS);
735
+ // J.arrayBegin();
736
+ // / for (const Event &E : Events) {
737
+ // / J.objectBegin();
738
+ // / J.attribute("timestamp", int64_t(E.Time));
739
+ // / J.attributeBegin("participants");
740
+ // / for (const Participant &P : E.Participants)
741
+ // / J.value(P.toString());
742
+ // / J.attributeEnd();
743
+ // / J.objectEnd();
744
+ // / }
745
+ // / J.arrayEnd();
746
+ // /
747
+ // / If the call sequence isn't valid JSON, asserts will fire in debug mode.
748
+ // / This can be mismatched begin()/end() pairs, trying to emit attributes inside
749
+ // / an array, and so on.
750
+ // / With asserts disabled, this is undefined behavior.
751
+ class OStream {
752
+ public:
753
+ using Block = llvm::function_ref<void ()>;
754
+ // OStream does not buffer internally, and need never be flushed or destroyed.
755
+ // If IndentSize is nonzero, output is pretty-printed.
756
+ explicit OStream (llvm::raw_ostream &OS, unsigned IndentSize = 0 )
757
+ : OS(OS), IndentSize(IndentSize) {
758
+ Stack.emplace_back ();
759
+ }
760
+ ~OStream () {
761
+ assert (Stack.size () == 1 && " Unmatched begin()/end()" );
762
+ assert (Stack.back ().Ctx == Singleton);
763
+ assert (Stack.back ().HasValue && " Did not write top-level value" );
764
+ }
765
+
766
+ // High level functions to output a value.
767
+ // Valid at top-level (exactly once), in an attribute value (exactly once),
768
+ // or in an array (any number of times).
769
+
770
+ // / Emit a self-contained value (number, string, vector<string> etc).
771
+ void value (const Value &V);
772
+ // / Emit an array whose elements are emitted in the provided Block.
773
+ void array (Block Contents) {
774
+ arrayBegin ();
775
+ Contents ();
776
+ arrayEnd ();
777
+ }
778
+ // / Emit an object whose elements are emitted in the provided Block.
779
+ void object (Block Contents) {
780
+ objectBegin ();
781
+ Contents ();
782
+ objectEnd ();
783
+ }
784
+
785
+ // High level functions to output object attributes.
786
+ // Valid only within an object (any number of times).
787
+
788
+ // / Emit an attribute whose value is self-contained (number, vector<int> etc).
789
+ void attribute (llvm::StringRef Key, const Value& Contents) {
790
+ attributeImpl (Key, [&] { value (Contents); });
791
+ }
792
+ // / Emit an attribute whose value is an array with elements from the Block.
793
+ void attributeArray (llvm::StringRef Key, Block Contents) {
794
+ attributeImpl (Key, [&] { array (Contents); });
795
+ }
796
+ // / Emit an attribute whose value is an object with attributes from the Block.
797
+ void attributeObject (llvm::StringRef Key, Block Contents) {
798
+ attributeImpl (Key, [&] { object (Contents); });
799
+ }
800
+
801
+ // Low-level begin/end functions to output arrays, objects, and attributes.
802
+ // Must be correctly paired. Allowed contexts are as above.
803
+
804
+ void arrayBegin ();
805
+ void arrayEnd ();
806
+ void objectBegin ();
807
+ void objectEnd ();
808
+ void attributeBegin (llvm::StringRef Key);
809
+ void attributeEnd ();
810
+
811
+ private:
812
+ void attributeImpl (llvm::StringRef Key, Block Contents) {
813
+ attributeBegin (Key);
814
+ Contents ();
815
+ attributeEnd ();
816
+ }
817
+
818
+ void valueBegin ();
819
+ void newline ();
820
+
821
+ enum Context {
822
+ Singleton, // Top level, or object attribute.
823
+ Array,
824
+ Object,
825
+ };
826
+ struct State {
827
+ Context Ctx = Singleton;
828
+ bool HasValue = false ;
829
+ };
830
+ llvm::SmallVector<State, 16 > Stack; // Never empty.
831
+ llvm::raw_ostream &OS;
832
+ unsigned IndentSize;
833
+ unsigned Indent = 0 ;
834
+ };
835
+
836
+ // / Serializes this Value to JSON, writing it to the provided stream.
837
+ // / The formatting is compact (no extra whitespace) and deterministic.
838
+ // / For pretty-printing, use the formatv() format_provider below.
839
+ inline llvm::raw_ostream &operator <<(llvm::raw_ostream &OS, const Value &V) {
840
+ OStream (OS).value (V);
841
+ return OS;
842
+ }
702
843
} // namespace json
703
844
704
845
// / Allow printing json::Value with formatv().
0 commit comments