SYS-REQ-007, SYS-REQ-030, SYS-REQ-031, SYS-REQ-032, SYS-REQ-054, SYS-REQ-084 ObjectEach iterates over the key-value pairs of a JSON object, invoking a given callback for each such entry
(data []byte, callback func(key []byte, value []byte, dataType ValueType, offset int) error, keys ...string)
| 1111 | // SYS-REQ-007, SYS-REQ-030, SYS-REQ-031, SYS-REQ-032, SYS-REQ-054, SYS-REQ-084 |
| 1112 | // ObjectEach iterates over the key-value pairs of a JSON object, invoking a given callback for each such entry |
| 1113 | func ObjectEach(data []byte, callback func(key []byte, value []byte, dataType ValueType, offset int) error, keys ...string) (err error) { |
| 1114 | offset := 0 |
| 1115 | |
| 1116 | // Descend to the desired key, if requested |
| 1117 | if len(keys) > 0 { |
| 1118 | if off := searchKeys(data, keys...); off == -1 { |
| 1119 | return KeyPathNotFoundError |
| 1120 | } else { |
| 1121 | offset = off |
| 1122 | } |
| 1123 | } |
| 1124 | |
| 1125 | // Validate and skip past opening brace |
| 1126 | if off := nextToken(data[offset:]); off == -1 { |
| 1127 | return MalformedObjectError |
| 1128 | } else if offset += off; data[offset] != '{' { |
| 1129 | return MalformedObjectError |
| 1130 | } else { |
| 1131 | offset++ |
| 1132 | } |
| 1133 | |
| 1134 | // Skip to the first token inside the object, or stop if we find the ending brace |
| 1135 | if off := nextToken(data[offset:]); off == -1 { |
| 1136 | return MalformedJsonError |
| 1137 | } else if offset += off; data[offset] == '}' { |
| 1138 | return nil |
| 1139 | } |
| 1140 | |
| 1141 | // Loop pre-condition: data[offset] points to what should be either the next entry's key, |
| 1142 | // or the closing brace (if it's anything else, the JSON is malformed). |
| 1143 | // Every iteration either returns or advances offset past a token, so the loop |
| 1144 | // always exits via return; the former `offset < len(data)` guard was structurally |
| 1145 | // always true because internal nextToken/stringEnd calls return errors before |
| 1146 | // offset can reach len(data). |
| 1147 | for { |
| 1148 | // Step 1: find the next key |
| 1149 | var key []byte |
| 1150 | |
| 1151 | // Check what the the next token is: start of string, end of object, or something else (error) |
| 1152 | switch data[offset] { |
| 1153 | case '"': |
| 1154 | offset++ // accept as string and skip opening quote |
| 1155 | case '}': |
| 1156 | return nil // we found the end of the object; stop and return success |
| 1157 | default: |
| 1158 | return MalformedObjectError |
| 1159 | } |
| 1160 | |
| 1161 | // Find the end of the key string |
| 1162 | var keyEscaped bool |
| 1163 | if off, esc := stringEnd(data[offset:]); off == -1 { |
| 1164 | return MalformedJsonError |
| 1165 | } else { |
| 1166 | key, keyEscaped = data[offset:offset+off-1], esc |
| 1167 | offset += off |
| 1168 | } |
| 1169 | |
| 1170 | // Unescape the string if needed |
searching dependent graphs…