|
243 | 243 | skippreencode = false; |
244 | 244 | if (opt.formatversion >= 4 && ~opt.messagepack && ~opt.ubjson) |
245 | 245 | if (isa(obj, 'table') && size(obj, 1) > 1) |
246 | | - [cansoa, ~] = table2soa(obj, opt); |
| 246 | + cansoa = table2soa(obj, opt); |
247 | 247 | skippreencode = cansoa; |
248 | 248 | end |
249 | 249 | end |
250 | 250 |
|
251 | 251 | if (~skippreencode && jsonopt('PreEncode', 1, opt)) |
252 | | - obj = jdataencode(obj, 'Base64', 0, 'UseArrayZipSize', opt.messagepack, opt); |
| 252 | + obj = jdataencode(obj, 'Base64', 0, 'UseArrayZipSize', opt.messagepack, 'DateTime', 0, opt); |
253 | 253 | end |
254 | 254 |
|
255 | 255 | dozip = opt.compression; |
|
321 | 321 | txt = struct2ubjson(name, item, level, opt); |
322 | 322 | elseif (isnumeric(item) || islogical(item)) |
323 | 323 | txt = mat2ubjson(name, item, level, opt); |
| 324 | +elseif (isa(item, 'datetime')) |
| 325 | + txt = ext2ubjson(name, item, 'datetime', opt); |
| 326 | +elseif (isa(item, 'duration')) |
| 327 | + txt = ext2ubjson(name, item, 'duration', opt); |
| 328 | +elseif (isa(item, 'jdict')) |
| 329 | + txt = jdict2ubjson(name, item, level, opt); |
324 | 330 | elseif (ischar(item)) |
325 | 331 | if (numel(item) >= opt.compressstringsize) |
326 | 332 | txt = mat2ubjson(name, item, level, opt); |
|
339 | 345 | txt = struct2ubjson(name, jdataencode(item), level, opt); |
340 | 346 | elseif (isobject(item)) |
341 | 347 | txt = matlabobject2ubjson(name, item, level, opt); |
| 348 | +elseif (~isreal(item) && isnumeric(item)) |
| 349 | + txt = ext2ubjson(name, item, 'complex', opt); |
342 | 350 | else |
343 | 351 | txt = any2ubjson(name, item, level, opt); |
344 | 352 | end |
|
1744 | 1752 | else |
1745 | 1753 | newdata = data; |
1746 | 1754 | end |
| 1755 | + |
| 1756 | +%% ------------------------------------------------------------------------- |
| 1757 | +function txt = ext2ubjson(name, item, dtype, opt) |
| 1758 | +% Unified extension encoder for datetime, duration, complex |
| 1759 | +if opt.messagepack || opt.ubjson |
| 1760 | + switch dtype |
| 1761 | + case 'datetime' |
| 1762 | + txt = str2ubjson(name, char(item), 0, opt); |
| 1763 | + case 'duration' |
| 1764 | + txt = mat2ubjson(name, seconds(item), 0, opt); |
| 1765 | + case 'complex' |
| 1766 | + txt = struct2ubjson(name, struct('re', real(item), 'im', imag(item)), 0, opt); |
| 1767 | + end |
| 1768 | + return |
| 1769 | +end |
| 1770 | + |
| 1771 | +if numel(item) > 1 |
| 1772 | + parts = cell(1, numel(item) + 2); |
| 1773 | + parts{1} = opt.AM_{1}; |
| 1774 | + for i = 1:numel(item) |
| 1775 | + parts{i + 1} = encode_ext_scalar(item(i), dtype, opt); |
| 1776 | + end |
| 1777 | + parts{end} = opt.AM_{2}; |
| 1778 | + txt = [parts{:}]; |
| 1779 | +else |
| 1780 | + txt = encode_ext_scalar(item, dtype, opt); |
| 1781 | +end |
| 1782 | +if ~isempty(name) |
| 1783 | + txt = [N_(decodevarname(name, opt.unpackhex), opt) txt]; |
| 1784 | +end |
| 1785 | + |
| 1786 | +%% ------------------------------------------------------------------------- |
| 1787 | +function txt = encode_ext_scalar(val, dtype, opt) |
| 1788 | +% Unified encoder for datetime, duration, complex scalars |
| 1789 | +% Returns 'Z' for NaT/NaN, otherwise [E][typeid][len][payload] |
| 1790 | +switch dtype |
| 1791 | + case 'datetime' |
| 1792 | + if isnat(val) |
| 1793 | + txt = opt.ZM_; |
| 1794 | + return |
| 1795 | + end |
| 1796 | + tz = val.TimeZone; |
| 1797 | + if isempty(tz) |
| 1798 | + val.TimeZone = 'UTC'; |
| 1799 | + end |
| 1800 | + pt = posixtime(val); |
| 1801 | + hasTime = hour(val) || minute(val) || second(val); |
| 1802 | + if ~hasTime && isempty(tz) |
| 1803 | + typeid = 4; |
| 1804 | + payload = [typecast(int16(year(val)), 'uint8'), uint8([month(val), day(val)])]; |
| 1805 | + elseif mod(second(val), 1) ~= 0 || pt < 0 || pt > 4294967295 |
| 1806 | + typeid = 6; |
| 1807 | + payload = typecast(int64(round(pt * 1e6)), 'uint8'); |
| 1808 | + else |
| 1809 | + typeid = 1; |
| 1810 | + payload = typecast(uint32(pt), 'uint8'); |
| 1811 | + end |
| 1812 | + case 'duration' |
| 1813 | + if isnan(val) |
| 1814 | + txt = opt.ZM_; |
| 1815 | + return |
| 1816 | + end |
| 1817 | + typeid = 7; |
| 1818 | + payload = typecast(int64(round(seconds(val) * 1e6)), 'uint8'); |
| 1819 | + case 'complex' |
| 1820 | + if isa(val, 'single') |
| 1821 | + typeid = 8; |
| 1822 | + payload = [typecast(single(real(val)), 'uint8'), typecast(single(imag(val)), 'uint8')]; |
| 1823 | + else |
| 1824 | + typeid = 9; |
| 1825 | + payload = [typecast(double(real(val)), 'uint8'), typecast(double(imag(val)), 'uint8')]; |
| 1826 | + end |
| 1827 | +end |
| 1828 | +% Apply endian swap based on typeid |
| 1829 | +if opt.flipendian_ |
| 1830 | + n = length(payload); |
| 1831 | + if typeid == 4 % date: only swap first 2 bytes (int16) |
| 1832 | + payload(1:2) = payload(2:-1:1); |
| 1833 | + elseif typeid == 8 % complex64: swap two 4-byte floats |
| 1834 | + payload = [payload(4:-1:1), payload(8:-1:5)]; |
| 1835 | + elseif typeid == 9 % complex128: swap two 8-byte floats |
| 1836 | + payload = [payload(8:-1:1), payload(16:-1:9)]; |
| 1837 | + elseif n > 1 % all others: simple reversal |
| 1838 | + payload = payload(n:-1:1); |
| 1839 | + end |
| 1840 | +end |
| 1841 | +txt = ['E' I_(uint8(typeid), opt) I_(uint8(length(payload)), opt) char(payload)]; |
| 1842 | + |
| 1843 | +%% ------------------------------------------------------------------------- |
| 1844 | +function txt = jdict2ubjson(name, item, level, opt) |
| 1845 | +% Handle jdict objects - check schema for special types |
| 1846 | +if ~isa(item, 'jdict') |
| 1847 | + txt = struct2ubjson(name, item, level, opt); |
| 1848 | + return |
| 1849 | +end |
| 1850 | +s = item.schema; |
| 1851 | +if item.getattr('$', '') && strcmp(s.format, 'uuid') |
| 1852 | + % UUID string |
| 1853 | + if opt.messagepack || opt.ubjson |
| 1854 | + txt = str2ubjson(name, char(item), level, opt); |
| 1855 | + return |
| 1856 | + end |
| 1857 | + uuidstr = char(item); |
| 1858 | + hexstr = strrep(uuidstr, '-', ''); |
| 1859 | + payload = uint8(zeros(1, 16)); |
| 1860 | + for i = 1:16 |
| 1861 | + payload(i) = hex2dec(hexstr(2 * i - 1:2 * i)); |
| 1862 | + end |
| 1863 | + txt = ['E' I_(uint8(10), opt) I_(uint8(16), opt) char(payload)]; |
| 1864 | + if ~isempty(name) |
| 1865 | + txt = [N_(decodevarname(name, opt.unpackhex), opt) txt]; |
| 1866 | + end |
| 1867 | +elseif isfield(s, 'type') && strcmp(s.type, 'bytes') |
| 1868 | + % Raw extension bytes |
| 1869 | + payload = uint8(item.data); |
| 1870 | + typeid = uint32(0); |
| 1871 | + if isfield(s, 'exttype') |
| 1872 | + typeid = uint32(s.exttype); |
| 1873 | + end |
| 1874 | + txt = ['E' I_(typeid, opt) I_(uint32(length(payload)), opt) char(payload)]; |
| 1875 | + if ~isempty(name) |
| 1876 | + txt = [N_(decodevarname(name, opt.unpackhex), opt) txt]; |
| 1877 | + end |
| 1878 | +else |
| 1879 | + % Generic jdict - encode as struct |
| 1880 | + txt = struct2ubjson(name, struct(item), level, opt); |
| 1881 | +end |
0 commit comments