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)
| 1387 | // SYS-REQ-007, SYS-REQ-030, SYS-REQ-031, SYS-REQ-032, SYS-REQ-054, SYS-REQ-084 |
| 1388 | // ObjectEach iterates over the key-value pairs of a JSON object, invoking a given callback for each such entry |
| 1389 | func ObjectEach(data []byte, callback func(key []byte, value []byte, dataType ValueType, offset int) error, keys ...string) (err error) { |
| 1390 | offset := 0 |
| 1391 | |
| 1392 | // Descend to the desired key, if requested |
| 1393 | if len(keys) > 0 { |
| 1394 | if off := searchKeys(data, keys...); off == -1 { |
| 1395 | return KeyPathNotFoundError |
| 1396 | } else { |
| 1397 | offset = off |
| 1398 | } |
| 1399 | } |
| 1400 | |
| 1401 | // Validate and skip past opening brace |
| 1402 | if off := nextToken(data[offset:]); off == -1 { |
| 1403 | return MalformedObjectError |
| 1404 | } else if offset += off; data[offset] != '{' { |
| 1405 | return MalformedObjectError |
| 1406 | } else { |
| 1407 | offset++ |
| 1408 | } |
| 1409 | |
| 1410 | // Skip to the first token inside the object, or stop if we find the ending brace |
| 1411 | if off := nextToken(data[offset:]); off == -1 { |
| 1412 | return MalformedJsonError |
| 1413 | } else if offset += off; data[offset] == '}' { |
| 1414 | return nil |
| 1415 | } |
| 1416 | |
| 1417 | // Loop pre-condition: data[offset] points to what should be either the next entry's key, |
| 1418 | // or the closing brace (if it's anything else, the JSON is malformed). |
| 1419 | // Every iteration either returns or advances offset past a token, so the loop |
| 1420 | // always exits via return; the former `offset < len(data)` guard was structurally |
| 1421 | // always true because internal nextToken/stringEnd calls return errors before |
| 1422 | // offset can reach len(data). |
| 1423 | for { |
| 1424 | // Step 1: find the next key |
| 1425 | var key []byte |
| 1426 | |
| 1427 | // Check what the the next token is: start of string, end of object, or something else (error) |
| 1428 | switch data[offset] { |
| 1429 | case '"': |
| 1430 | offset++ // accept as string and skip opening quote |
| 1431 | case '}': |
| 1432 | return nil // we found the end of the object; stop and return success |
| 1433 | default: |
| 1434 | return MalformedObjectError |
| 1435 | } |
| 1436 | |
| 1437 | // Find the end of the key string |
| 1438 | var keyEscaped bool |
| 1439 | if off, esc := stringEnd(data[offset:]); off == -1 { |
| 1440 | return MalformedJsonError |
| 1441 | } else { |
| 1442 | key, keyEscaped = data[offset:offset+off-1], esc |
| 1443 | offset += off |
| 1444 | } |
| 1445 | |
| 1446 | // Unescape the string if needed |