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)
| 1727 | // SYS-REQ-007, SYS-REQ-030, SYS-REQ-031, SYS-REQ-032, SYS-REQ-054, SYS-REQ-084 |
| 1728 | // ObjectEach iterates over the key-value pairs of a JSON object, invoking a given callback for each such entry |
| 1729 | func ObjectEach(data []byte, callback func(key []byte, value []byte, dataType ValueType, offset int) error, keys ...string) (err error) { |
| 1730 | offset := 0 |
| 1731 | |
| 1732 | // Descend to the desired key, if requested |
| 1733 | if len(keys) > 0 { |
| 1734 | if off := searchKeys(data, keys...); off == -1 { |
| 1735 | return KeyPathNotFoundError |
| 1736 | } else { |
| 1737 | offset = off |
| 1738 | } |
| 1739 | } |
| 1740 | |
| 1741 | // Validate and skip past opening brace |
| 1742 | if off := nextToken(data[offset:]); off == -1 { |
| 1743 | return MalformedObjectError |
| 1744 | } else if offset += off; data[offset] != '{' { |
| 1745 | return MalformedObjectError |
| 1746 | } else { |
| 1747 | offset++ |
| 1748 | } |
| 1749 | |
| 1750 | // Skip to the first token inside the object, or stop if we find the ending brace |
| 1751 | if off := nextToken(data[offset:]); off == -1 { |
| 1752 | return MalformedJsonError |
| 1753 | } else if offset += off; data[offset] == '}' { |
| 1754 | return nil |
| 1755 | } |
| 1756 | |
| 1757 | // Loop pre-condition: data[offset] points to what should be either the next entry's key, |
| 1758 | // or the closing brace (if it's anything else, the JSON is malformed). |
| 1759 | // Every iteration either returns or advances offset past a token, so the loop |
| 1760 | // always exits via return; the former `offset < len(data)` guard was structurally |
| 1761 | // always true because internal nextToken/stringEnd calls return errors before |
| 1762 | // offset can reach len(data). |
| 1763 | for { |
| 1764 | // Step 1: find the next key |
| 1765 | var key []byte |
| 1766 | |
| 1767 | // Check what the the next token is: start of string, end of object, or something else (error) |
| 1768 | switch data[offset] { |
| 1769 | case '"': |
| 1770 | offset++ // accept as string and skip opening quote |
| 1771 | case '}': |
| 1772 | return nil // we found the end of the object; stop and return success |
| 1773 | default: |
| 1774 | return MalformedObjectError |
| 1775 | } |
| 1776 | |
| 1777 | // Find the end of the key string |
| 1778 | var keyEscaped bool |
| 1779 | if off, esc := stringEnd(data[offset:]); off == -1 { |
| 1780 | return MalformedJsonError |
| 1781 | } else { |
| 1782 | key, keyEscaped = data[offset:offset+off-1], esc |
| 1783 | offset += off |
| 1784 | } |
| 1785 | |
| 1786 | // Unescape the string if needed |