1 /** 2 * This module takes a `std.json` JSONValue and generates a `stdx.data.json` token stream. 3 * It provides Phobos backwards compatibility for `std_data_json`. 4 */ 5 module text.json.JsonValueRange; 6 7 import std.algorithm : count; 8 import std.json; 9 import std.range : drop; 10 import stdx.data.json.lexer; 11 import stdx.data.json.parser; 12 13 static assert(isJSONParserNodeInputRange!JsonValueRange); 14 15 struct JsonValueRange 16 { 17 public JSONParserNode!string front; 18 19 public bool empty; 20 21 private ValueIterator[] iterators; 22 23 private int level; 24 25 invariant(this.level < cast(int) this.iterators.length); 26 27 public this(JSONValue value) 28 { 29 this.empty = false; 30 this.level = -1; 31 stepInto(value); 32 } 33 34 private void stepInto(JSONValue value) 35 { 36 alias Token = JSONToken!string; 37 38 with (JSONType) final switch (value.type) 39 { 40 case null_: 41 this.front.literal = Token(null); 42 break; 43 case string: 44 this.front.literal = Token(value.str); 45 break; 46 case integer: 47 this.front.literal = Token(value.integer); 48 break; 49 case uinteger: 50 this.front.literal = Token(value.uinteger); 51 break; 52 case float_: 53 this.front.literal = Token(value.floating); 54 break; 55 case array: 56 this.front.kind = JSONParserNodeKind.arrayStart; 57 pushState(value); 58 break; 59 case object: 60 this.front.kind = JSONParserNodeKind.objectStart; 61 pushState(value); 62 break; 63 case true_: 64 this.front.literal = Token(true); 65 break; 66 case false_: 67 this.front.literal = Token(false); 68 break; 69 } 70 } 71 72 private void pushState(JSONValue value) 73 { 74 if (this.level + 1 == this.iterators.length) 75 { 76 this.iterators ~= ValueIterator(value); 77 } 78 else 79 { 80 this.iterators[this.level + 1] = ValueIterator(value); 81 } 82 this.level++; 83 } 84 85 public void popFront() 86 { 87 if (outOfValues) 88 { 89 empty = true; 90 return; 91 } 92 93 if (current.value.type == JSONType.object) 94 { 95 if (current.nextIndex == current.value.objectNoRef.length) 96 { 97 this.front.kind = JSONParserNodeKind.objectEnd; 98 popState; 99 return; 100 } 101 102 if (!current.usedKey) 103 { 104 this.front.key = current.value.objectNoRef.byKeyValue.drop(current.nextIndex).front.key; 105 current.usedKey = true; 106 return; 107 } 108 109 auto value = current.value.objectNoRef.byKeyValue.drop(current.nextIndex).front.value; 110 111 current.usedKey = false; 112 current.nextIndex++; 113 stepInto(value); 114 return; 115 } 116 else if (current.value.type == JSONType.array) 117 { 118 if (current.nextIndex == current.value.arrayNoRef.length) 119 { 120 this.front.kind = JSONParserNodeKind.arrayEnd; 121 popState; 122 return; 123 } 124 stepInto(current.value.arrayNoRef[current.nextIndex++]); 125 } 126 else 127 { 128 assert(false, "unexpected value type"); 129 } 130 } 131 132 private void popState() 133 { 134 this.level--; 135 } 136 137 private ref ValueIterator current() 138 in (!outOfValues) 139 { 140 return this.iterators[this.level]; 141 } 142 143 public bool outOfValues() const 144 { 145 return this.level == -1; 146 } 147 } 148 149 private struct ValueIterator 150 { 151 JSONValue value; 152 153 invariant(value.type == JSONType.array || value.type == JSONType.object); 154 155 // if `value` is an array or object, indicates the next element to be selected. 156 size_t nextIndex = 0; 157 158 bool usedKey; // objects are key, value, key, value => false, true, false, true... 159 }