|
1 |
| -using System; |
2 |
| -using System.Collections; |
3 |
| -using System.IO; |
4 |
| -using System.Linq; |
5 |
| -using System.Reflection; |
6 | 1 | using System.Text;
|
7 |
| -using GitVersion.OutputVariables; |
| 2 | +using GitVersion.Extensions; |
8 | 3 |
|
9 | 4 | namespace GitVersion.Helpers
|
10 | 5 | {
|
11 |
| - // Credit to https://github.com/neuecc |
12 |
| - // Inspired by https://gist.github.com/neuecc/7d728cd99d2a1e613362 |
13 | 6 | public static class JsonSerializer
|
14 | 7 | {
|
15 |
| - private const string INDENT_STRING = " "; |
16 |
| - private static readonly Encoding UTF8 = new UTF8Encoding(false); |
17 | 8 |
|
18 | 9 | public static string Serialize(object obj)
|
19 | 10 | {
|
20 |
| - using var ms = new MemoryStream(); |
21 |
| - using var sw = new StreamWriter(ms, UTF8); |
22 |
| - Serialize(sw, obj); |
23 |
| - sw.Flush(); |
24 |
| - return UTF8.GetString(ms.ToArray()); |
25 |
| - } |
26 |
| - |
27 |
| - public static void Serialize(TextWriter tw, object obj) |
28 |
| - { |
29 |
| - SerializeObject(tw, obj); |
30 |
| - } |
31 |
| - |
32 |
| - enum JsonType |
33 |
| - { |
34 |
| - @string, |
35 |
| - number, |
36 |
| - boolean, |
37 |
| - @object, |
38 |
| - array, |
39 |
| - @null |
40 |
| - } |
41 |
| - |
42 |
| - static JsonType GetJsonType(object obj) |
43 |
| - { |
44 |
| - if (obj == null) return JsonType.@null; |
45 |
| - |
46 |
| - return Type.GetTypeCode(obj.GetType()) switch |
| 11 | + var builder = new StringBuilder(); |
| 12 | + builder.AppendLine("{"); |
| 13 | + var first = true; |
| 14 | + foreach (var (key, value) in obj.GetProperties()) |
47 | 15 | {
|
48 |
| - TypeCode.Boolean => JsonType.boolean, |
49 |
| - TypeCode.String => JsonType.@string, |
50 |
| - TypeCode.Char => JsonType.@string, |
51 |
| - TypeCode.DateTime => JsonType.@string, |
52 |
| - TypeCode.Int16 => JsonType.number, |
53 |
| - TypeCode.Int32 => JsonType.number, |
54 |
| - TypeCode.Int64 => JsonType.number, |
55 |
| - TypeCode.UInt16 => JsonType.number, |
56 |
| - TypeCode.UInt32 => JsonType.number, |
57 |
| - TypeCode.UInt64 => JsonType.number, |
58 |
| - TypeCode.Single => JsonType.number, |
59 |
| - TypeCode.Double => JsonType.number, |
60 |
| - TypeCode.Decimal => JsonType.number, |
61 |
| - TypeCode.SByte => JsonType.number, |
62 |
| - TypeCode.Byte => JsonType.number, |
63 |
| - TypeCode.Object => (obj switch |
64 |
| - { |
65 |
| - VersionVariables _ => JsonType.@object, |
66 |
| - // specialized for well known types |
67 |
| - Uri _ => JsonType.@string, |
68 |
| - DateTimeOffset _ => JsonType.@string, |
69 |
| - Guid _ => JsonType.@string, |
70 |
| - StringBuilder _ => JsonType.@string, |
71 |
| - IDictionary _ => JsonType.@object, |
72 |
| - _ => ((obj is IEnumerable) ? JsonType.array : JsonType.@object) |
73 |
| - }), |
74 |
| - TypeCode.DBNull => JsonType.@null, |
75 |
| - TypeCode.Empty => JsonType.@null, |
76 |
| - _ => JsonType.@null |
77 |
| - }; |
78 |
| - } |
| 16 | + if (!first) builder.AppendLine(","); |
| 17 | + else first = false; |
79 | 18 |
|
80 |
| - static void SerializeObject(TextWriter tw, object o) |
81 |
| - { |
82 |
| - switch (GetJsonType(o)) |
83 |
| - { |
84 |
| - case JsonType.@string: |
85 |
| - switch (o) |
86 |
| - { |
87 |
| - case string s when NotAPaddedNumber(s) && int.TryParse(s, out var n): |
88 |
| - WriteNumber(tw, n); |
89 |
| - break; |
90 |
| - case string s: |
91 |
| - WriteString(tw, s); |
92 |
| - break; |
93 |
| - case DateTime time: |
94 |
| - { |
95 |
| - var s = time.ToString("o"); |
96 |
| - WriteString(tw, s); |
97 |
| - break; |
98 |
| - } |
99 |
| - case DateTimeOffset offset: |
100 |
| - { |
101 |
| - var s = offset.ToString("o"); |
102 |
| - WriteString(tw, s); |
103 |
| - break; |
104 |
| - } |
105 |
| - default: |
106 |
| - WriteString(tw, o.ToString()); |
107 |
| - break; |
108 |
| - } |
| 19 | + builder.Append($" \"{key}\":"); |
| 20 | + // preserve leading zeros for padding |
109 | 21 |
|
110 |
| - break; |
111 |
| - case JsonType.number: |
112 |
| - WriteNumber(tw, o); |
113 |
| - break; |
114 |
| - case JsonType.boolean: |
115 |
| - WriteBoolean(tw, (bool) o); |
116 |
| - break; |
117 |
| - case JsonType.@object: |
118 |
| - WriteObject(tw, o); |
119 |
| - break; |
120 |
| - case JsonType.array: |
121 |
| - WriteArray(tw, (IEnumerable) o); |
122 |
| - break; |
123 |
| - case JsonType.@null: |
124 |
| - WriteNull(tw); |
125 |
| - break; |
126 |
| - default: |
127 |
| - break; |
| 22 | + if (NotAPaddedNumber(value) && int.TryParse(value, out var number)) |
| 23 | + builder.Append(number); |
| 24 | + else |
| 25 | + builder.Append($"\"{value}\""); |
128 | 26 | }
|
129 |
| - } |
130 | 27 |
|
131 |
| - static void WriteString(TextWriter tw, string o) |
132 |
| - { |
133 |
| - tw.Write('\"'); |
134 |
| - |
135 |
| - foreach (var c in o) |
136 |
| - { |
137 |
| - switch (c) |
138 |
| - { |
139 |
| - case '"': |
140 |
| - tw.Write("\\\""); |
141 |
| - break; |
142 |
| - case '\\': |
143 |
| - tw.Write("\\\\"); |
144 |
| - break; |
145 |
| - case '\b': |
146 |
| - tw.Write("\\b"); |
147 |
| - break; |
148 |
| - case '\f': |
149 |
| - tw.Write("\\f"); |
150 |
| - break; |
151 |
| - case '\n': |
152 |
| - tw.Write("\\n"); |
153 |
| - break; |
154 |
| - case '\r': |
155 |
| - tw.Write("\\r"); |
156 |
| - break; |
157 |
| - case '\t': |
158 |
| - tw.Write("\\t"); |
159 |
| - break; |
160 |
| - default: |
161 |
| - tw.Write(c); |
162 |
| - break; |
163 |
| - } |
164 |
| - } |
165 |
| - |
166 |
| - tw.Write('\"'); |
167 |
| - } |
168 |
| - |
169 |
| - static void WriteNumber(TextWriter tw, object o) |
170 |
| - { |
171 |
| - tw.Write(o.ToString()); |
172 |
| - } |
173 |
| - |
174 |
| - static void WriteBoolean(TextWriter tw, bool o) |
175 |
| - { |
176 |
| - tw.Write(o ? "true" : "false"); |
177 |
| - } |
178 |
| - |
179 |
| - static void WriteObject(TextWriter tw, object o) |
180 |
| - { |
181 |
| - tw.Write('{'); |
182 |
| - |
183 |
| - if (o is IDictionary dict) |
184 |
| - { |
185 |
| - // Dictionary |
186 |
| - var isFirst = true; |
187 |
| - foreach (DictionaryEntry item in dict) |
188 |
| - { |
189 |
| - if (!isFirst) tw.Write(","); |
190 |
| - else isFirst = false; |
191 |
| - |
192 |
| - tw.Write('\"'); |
193 |
| - tw.Write(item.Key); |
194 |
| - tw.Write('\"'); |
195 |
| - tw.Write(":"); |
196 |
| - SerializeObject(tw, item.Value); |
197 |
| - } |
198 |
| - } |
199 |
| - else |
200 |
| - { |
201 |
| - // Object |
202 |
| - var isFirst = true; |
203 |
| - var propertyInfos = o.GetType() |
204 |
| - .GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty) |
205 |
| - .Where(p => !p.GetCustomAttributes(typeof(VersionVariables.ReflectionIgnoreAttribute), false).Any()); |
206 |
| - |
207 |
| - foreach (var item in propertyInfos) |
208 |
| - { |
209 |
| - if (!isFirst) tw.Write(","); |
210 |
| - else isFirst = false; |
211 |
| - |
212 |
| - var key = item.Name; |
213 |
| - var value = item.GetGetMethod().Invoke(o, null); |
214 |
| - tw.Write('\"'); |
215 |
| - tw.Write(key); |
216 |
| - tw.Write('\"'); |
217 |
| - tw.Write(":"); |
218 |
| - SerializeObject(tw, value); |
219 |
| - } |
220 |
| - |
221 |
| - isFirst = true; |
222 |
| - foreach (var item in o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetField)) |
223 |
| - { |
224 |
| - if (!isFirst) tw.Write(","); |
225 |
| - else isFirst = false; |
226 |
| - |
227 |
| - var key = item.Name; |
228 |
| - var value = item.GetValue(o); |
229 |
| - tw.Write('\"'); |
230 |
| - tw.Write(key); |
231 |
| - tw.Write('\"'); |
232 |
| - tw.Write(":"); |
233 |
| - SerializeObject(tw, value); |
234 |
| - } |
235 |
| - } |
236 |
| - |
237 |
| - tw.Write('}'); |
238 |
| - } |
239 |
| - |
240 |
| - static void WriteArray(TextWriter tw, IEnumerable o) |
241 |
| - { |
242 |
| - tw.Write("["); |
243 |
| - var isFirst = true; |
244 |
| - foreach (var item in o) |
245 |
| - { |
246 |
| - if (!isFirst) tw.Write(","); |
247 |
| - else isFirst = false; |
248 |
| - |
249 |
| - SerializeObject(tw, item); |
250 |
| - } |
251 |
| - |
252 |
| - tw.Write("]"); |
253 |
| - } |
254 |
| - |
255 |
| - static void WriteNull(TextWriter tw) |
256 |
| - { |
257 |
| - tw.Write("null"); |
| 28 | + builder.AppendLine().Append("}"); |
| 29 | + return builder.ToString(); |
258 | 30 | }
|
259 | 31 |
|
260 | 32 | private static bool NotAPaddedNumber(string value) => value == "0" || !value.StartsWith("0");
|
261 |
| - |
262 |
| - public static string FormatJson(string json) |
263 |
| - { |
264 |
| - var indentation = 0; |
265 |
| - var quoteCount = 0; |
266 |
| - var result = |
267 |
| - from ch in json |
268 |
| - let quotes = ch == '"' ? quoteCount++ : quoteCount |
269 |
| - let lineBreak = ch == ',' && quotes % 2 == 0 ? ch + System.Environment.NewLine + string.Concat(Enumerable.Repeat(INDENT_STRING, indentation)) : null |
270 |
| - let openChar = ch == '{' || ch == '[' ? ch + System.Environment.NewLine + string.Concat(Enumerable.Repeat(INDENT_STRING, ++indentation)) : ch.ToString() |
271 |
| - let closeChar = ch == '}' || ch == ']' ? System.Environment.NewLine + string.Concat(Enumerable.Repeat(INDENT_STRING, --indentation)) + ch : ch.ToString() |
272 |
| - select lineBreak ?? |
273 |
| - (openChar.Length > 1 |
274 |
| - ? openChar |
275 |
| - : closeChar); |
276 |
| - |
277 |
| - return string.Concat(result); |
278 |
| - } |
279 | 33 | }
|
280 | 34 | }
|
0 commit comments