1 //
2 // objc_json_serde.h
3 // util
4 //
5 // Copyright © 2024 Apple Inc. All rights reserved.
6 //
7 // Please refer to the license found in the LICENSE file in the root directory of the source tree.
8
9 #pragma once
10
11 #import <Foundation/Foundation.h>
12
13 #import <map>
14 #import <string>
15 #import <unordered_map>
16 #import <vector>
17
18 #import <objc_safe_cast.h>
19
20 namespace executorchcoreml {
21 namespace serde {
22 namespace json {
23
to_string(std::string_view view)24 inline NSString* to_string(std::string_view view) { return @(std::string(view).c_str()); }
25
26 template <typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
27 T to_scalar(NSNumber* value);
28
to_scalar(NSNumber * value)29 template <> inline size_t to_scalar(NSNumber* value) { return value.unsignedLongLongValue; }
30
to_scalar(NSNumber * value)31 template <> inline int64_t to_scalar(NSNumber* value) { return value.longLongValue; }
32
33 id to_json_object(const std::string& json_string);
34
35 id to_json_object(NSData* data);
36
37 std::string to_json_string(id json_object);
38
39 template <typename T, typename Enable = void> struct Converter { };
40
41 template <typename T>
42 using is_vector =
43 std::is_same<std::decay_t<T>,
44 std::vector<typename std::decay_t<T>::value_type, typename std::decay_t<T>::allocator_type>>;
45
46 template <typename T>
47 using is_unordered_map = std::is_same<std::decay_t<T>,
48 std::unordered_map<typename std::decay_t<T>::key_type,
49 typename std::decay_t<T>::mapped_type,
50 typename std::decay_t<T>::hasher,
51 typename std::decay_t<T>::key_equal,
52 typename std::decay_t<T>::allocator_type>>;
53
54 template <typename T> struct Converter<T, typename std::enable_if<std::is_arithmetic_v<T>>::type> {
55 template <typename U = T> static id to_json(U&& value) { return @(value); }
56
57 static void from_json(id json_value, T& value) {
58 NSNumber* json_number = SAFE_CAST(json_value, NSNumber);
59 if (json_number) {
60 value = to_scalar<T>(json_number);
61 }
62 }
63 };
64
65 template <typename T> struct Converter<T, typename std::enable_if<std::is_same_v<T, std::string>>::type> {
66 template <typename U = T> static id to_json(U&& value) { return @(value.c_str()); }
67
68 static void from_json(id json_value, T& value) {
69 NSString* json_string = SAFE_CAST(json_value, NSString);
70 if (json_string) {
71 value = json_string.UTF8String;
72 }
73 }
74 };
75
76 template <typename T> struct Converter<T, typename std::enable_if<is_vector<T>::value>::type> {
77 using value_type = typename T::value_type;
78
79 template <typename U = T> static id to_json(U&& values) {
80 NSMutableArray<id>* result = [NSMutableArray arrayWithCapacity:values.size()];
81 for (auto it = values.begin(); it != values.end(); ++it) {
82 [result addObject:Converter<value_type>::to_json(*it)];
83 }
84
85 return result;
86 }
87
88 static void from_json(id json_value, T& values) {
89 NSArray<id>* json_values = SAFE_CAST(json_value, NSArray);
90 for (id json_object in json_values) {
91 value_type value;
92 Converter<value_type>::from_json(json_object, value);
93 values.emplace_back(std::move(value));
94 }
95 }
96 };
97
98 template <typename T> struct Converter<T, std::enable_if_t<is_unordered_map<T>::value>> {
99 using value_type = typename T::mapped_type;
100 using key_type = typename T::key_type;
101
102 static_assert(std::is_same<key_type, std::string>::value, "key_type must be string");
103
104 template <typename U = T> static id to_json(U&& values) {
105 NSMutableDictionary<NSString*, id>* result = [NSMutableDictionary dictionaryWithCapacity:values.size()];
106 for (auto it = values.begin(); it != values.end(); ++it) {
107 result[@(it->first.c_str())] = Converter<value_type>::to_json(it->second);
108 }
109
110 return result;
111 }
112
113 static void from_json(id json_value, T& values) {
114 NSDictionary<NSString*, id>* json_values = SAFE_CAST(json_value, NSDictionary);
115 for (NSString* key in json_values) {
116 value_type value;
117 Converter<value_type>::from_json(json_values[key], value);
118 values.emplace(std::string(key.UTF8String), std::move(value));
119 }
120 }
121 };
122
123 template <typename T> inline id to_json_value(T&& value) {
124 return Converter<typename std::decay_t<T>>::to_json(std::forward<T>(value));
125 }
126
127 template <typename T> void from_json_value(id json_value, T& value) {
128 return Converter<T>::from_json(json_value, value);
129 }
130
131 } // namespace serde
132 } // namespace json
133 } // namespace executorchcoreml
134