Skip to content

Commit 4135d68

Browse files
committed
Add JsonProperty Attribute to enable custom field names;
Change mapping of field names to case insensitive comparison;
1 parent d6d45a8 commit 4135d68

5 files changed

Lines changed: 112 additions & 56 deletions

File tree

Tiny-JSON/Tiny-JSON/DefaultEncoder.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ public static Encoder GenericEncoder() {
1717
foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)) {
1818
if (field.GetCustomAttributes(typeof(NonSerializedAttribute), true).Length == 0) {
1919
if (first) first = false; else builder.AppendSeperator();
20-
JsonMapper.EncodeNameValue(field.Name, field.GetValue(obj), builder);
20+
21+
var fieldName = field.UnwrappedFieldName(type);
22+
JsonMapper.EncodeNameValue(fieldName, field.GetValue(obj), builder);
2123
}
2224
}
2325
type = type.BaseType;

Tiny-JSON/Tiny-JSON/Extensions.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Linq;
3+
using System.Reflection;
34

45
namespace Tiny {
56
public static class TypeExtensions {
@@ -16,6 +17,42 @@ public static bool HasGenericInterface(this Type type, Type genericInterface) {
1617
var interfaceTest = new Predicate<Type>(i => i.IsGenericType && i.GetGenericTypeDefinition().IsAssignableFrom(genericInterface));
1718
return interfaceTest(type) || type.GetInterfaces().Any(i => interfaceTest(i));
1819
}
20+
21+
static string UnwrapFieldName(string name) {
22+
if (name.StartsWith("<", StringComparison.Ordinal) && name.Contains(">")) {
23+
return name.Substring(name.IndexOf("<", StringComparison.Ordinal) + 1, name.IndexOf(">", StringComparison.Ordinal) - 1);
24+
}
25+
return name;
26+
}
27+
28+
public static string UnwrappedFieldName(this FieldInfo field, Type type) {
29+
string name = UnwrapFieldName(field.Name);
30+
31+
if (field.GetCustomAttributes(typeof(JsonPropertyAttribute), true).Length == 1) {
32+
var jsonProperty = field.GetCustomAttributes(typeof(JsonPropertyAttribute), true)[0] as JsonPropertyAttribute;
33+
name = jsonProperty.Name;
34+
} else {
35+
foreach (var property in type.GetProperties()) {
36+
if (UnwrapFieldName(property.Name).Equals(name, StringComparison.OrdinalIgnoreCase)) {
37+
name = property.UnwrappedPropertyName();
38+
break;
39+
}
40+
}
41+
}
42+
43+
return name;
44+
}
45+
46+
public static string UnwrappedPropertyName(this PropertyInfo property) {
47+
string name = UnwrapFieldName(property.Name);
48+
49+
if (property.GetCustomAttributes(typeof(JsonPropertyAttribute), true).Length == 1) {
50+
var jsonProperty = property.GetCustomAttributes(typeof(JsonPropertyAttribute), true)[0] as JsonPropertyAttribute;
51+
name = jsonProperty.Name;
52+
}
53+
54+
return name;
55+
}
1956
}
2057

2158
public static class JsonExtensions {

Tiny-JSON/Tiny-JSON/JsonMapper.cs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -98,17 +98,10 @@ public static void EncodeValue(object value, JsonBuilder builder) {
9898
}
9999

100100
public static void EncodeNameValue(string name, object value, JsonBuilder builder) {
101-
builder.AppendName(UnwrapName(name));
101+
builder.AppendName(name);
102102
EncodeValue(value, builder);
103103
}
104104

105-
public static string UnwrapName(string name) {
106-
if (name.StartsWith("<", StringComparison.InvariantCulture) && name.Contains(">")) {
107-
return name.Substring(name.IndexOf("<", StringComparison.InvariantCulture) + 1, name.IndexOf(">", StringComparison.InvariantCulture) - 1);
108-
}
109-
return name;
110-
}
111-
112105
static object ConvertValue(object value, Type type) {
113106
if (value != null) {
114107
Type safeType = Nullable.GetUnderlyingType(type) ?? type;
@@ -151,7 +144,9 @@ public static bool DecodeValue(object target, string name, object value) {
151144
while (type != null) {
152145
foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)) {
153146
if (field.GetCustomAttributes(typeof(NonSerializedAttribute), true).Length == 0) {
154-
if (name == UnwrapName(field.Name)) {
147+
string fieldName = field.UnwrappedFieldName(type);
148+
149+
if (name.Equals(fieldName, StringComparison.CurrentCultureIgnoreCase)) {
155150
if (value != null) {
156151
Type targetType = field.FieldType;
157152
object decodedValue = DecodeValue(value, targetType);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
namespace Tiny {
3+
4+
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
5+
public class JsonPropertyAttribute : Attribute {
6+
public string Name { get; private set; }
7+
8+
public JsonPropertyAttribute(string name) {
9+
Name = name;
10+
}
11+
}
12+
}

tests/tests/JsonTest.cs

Lines changed: 56 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ enum Kind {
1313
}
1414

1515
class Animal {
16-
public int legs;
17-
internal Kind kind {get; set; }
16+
[JsonProperty("legs")]
17+
public int numberOfLegs;
18+
19+
internal Kind Kind {get; set; }
1820
}
1921

2022
class Bear : Animal {
21-
public bool hungry {get; set; }
23+
[JsonProperty("hungry")]
24+
public bool Hungry {get; set; }
2225
float weight;
2326
[NonSerialized]
2427
public string id = "unknown";
@@ -28,7 +31,7 @@ class Bear : Animal {
2831

2932
public Bear(float weight) {
3033
this.weight = weight;
31-
this.kind = Kind.Mammal;
34+
this.Kind = Kind.Mammal;
3235
}
3336
}
3437

@@ -38,59 +41,65 @@ struct Mission {
3841
}
3942

4043
class Transporter<T> {
41-
Mission mission;
4244
public T[] cargo;
4345
public int? maxCargo;
44-
public List<string> driver { get; private set; }
46+
public List<string> Driver { get; private set; }
4547

48+
[JsonProperty("mission")]
49+
Mission _mission;
4650
public Mission Mission {
47-
get { return mission; }
48-
set { mission = value; }
51+
get { return _mission; }
52+
set { _mission = value; }
4953
}
5054

5155
public Transporter() {
52-
driver = new List<string>();
56+
Driver = new List<string>();
5357
}
5458
}
5559

5660
[Test]
5761
public static void EncodeTest1() {
58-
Animal a = new Animal();
59-
a.legs = 4;
62+
Animal a = new Animal {
63+
numberOfLegs = 4
64+
};
6065

6166
string json = Json.Encode(a);
62-
Assert.AreEqual("{\"legs\":4,\"kind\":0}", json);
67+
Assert.AreEqual("{\"legs\":4,\"Kind\":0}", json);
6368
}
6469

6570
[Test]
6671
public static void EncodeTest2() {
67-
Animal a = new Bear(10);
68-
a.legs = 4;
72+
Animal a = new Bear(10) {
73+
numberOfLegs = 4
74+
};
6975

7076
string json = Json.Encode(a);
71-
Assert.AreEqual("{\"hungry\":false,\"weight\":10.0,\"name\":\"Baloo\",\"legs\":4,\"kind\":3}", json);
77+
Assert.AreEqual("{\"hungry\":false,\"weight\":10.0,\"name\":\"Baloo\",\"legs\":4,\"Kind\":3}", json);
7278
}
7379

7480
[Test]
7581
public static void EncodeTest3() {
76-
Animal a = new Animal();
77-
a.legs = 4;
82+
Animal a = new Animal {
83+
numberOfLegs = 4
84+
};
7885

79-
Animal b = new Animal();
80-
a.legs = 2;
86+
Animal b = new Animal {
87+
numberOfLegs = 2
88+
};
8189

8290
Mission m;
8391
m.target = "unknown";
8492
m.start = new DateTime(2020, 4, 1);
8593

86-
Transporter<Animal> t = new Transporter<Animal>();
87-
t.Mission = m;
88-
t.cargo = new Animal[] { a, b };
89-
t.driver.Add("Joe");
94+
Transporter<Animal> t = new Transporter<Animal> {
95+
Mission = m,
96+
cargo = new Animal[] { a, b }
97+
};
98+
t.Driver.Add("Joe");
9099

91100
string json = Json.Encode(t);
92101
Console.WriteLine("json = " + json);
93-
Assert.AreEqual("{\"mission\":{\"target\":\"unknown\",\"start\":\"2020-03-31T22:00:00.000Z\"},\"cargo\":[{\"legs\":2,\"kind\":0},{\"legs\":0,\"kind\":0}],\"maxCargo\":null,\"driver\":[\"Joe\"]}", json);
102+
Assert.AreEqual("{\"cargo\":[{\"legs\":4,\"Kind\":0},{\"legs\":2,\"Kind\":0}],\"maxCargo\":null,\"Driver\":[\"Joe\"],\"mission\":{\"target\":\"unknown\",\"start\":\"2020-03-31T22:00:00.000Z\"}}", json);
94103
}
95104

96105
[Test]
@@ -171,11 +180,12 @@ public static void EncodeDictTest3() {
171180

172181
[Test]
173182
public static void PrettyEncodeTest1() {
174-
Animal a = new Bear(10.5f);
175-
a.legs = 4;
176-
183+
Animal a = new Bear(10.5f) {
184+
numberOfLegs = 4
185+
};
186+
177187
string json = Json.Encode(a, true);
178-
Assert.AreEqual("{\n\t\"hungry\" : false,\n\t\"weight\" : 10.5,\n\t\"name\" : \"Baloo\",\n\t\"legs\" : 4,\n\t\"kind\" : 3\n}\n", json);
188+
Assert.AreEqual("{\n\t\"hungry\" : false,\n\t\"weight\" : 10.5,\n\t\"name\" : \"Baloo\",\n\t\"legs\" : 4,\n\t\"Kind\" : 3\n}\n", json);
179189
}
180190

181191
[Test]
@@ -195,22 +205,22 @@ public static void DecodeTest0() {
195205
[Test]
196206
public static void DecodeTest1() {
197207
var a = Json.Decode<Animal>("{\"legs\":4,\"a\":false}");
198-
Assert.AreEqual(4, a.legs);
208+
Assert.AreEqual(4, a.numberOfLegs);
199209
}
200210

201211
[Test]
202212
public static void DecodeTest2() {
203213
var a = Json.Decode<Animal>("{\"legs\":null}");
204-
Assert.AreEqual(0, a.legs);
214+
Assert.AreEqual(0, a.numberOfLegs);
205215
}
206216

207217
[Test]
208218
public static void DecodeTest3() {
209-
var a = Json.Decode<Bear>("{\"legs\":4,\"hungry\":true,\"kind\":3,\"name\":null,\"id\":\"a\"}");
219+
var a = Json.Decode<Bear>("{\"Legs\":4,\"hungry\":true,\"kind\":3,\"name\":null,\"id\":\"a\"}");
210220

211-
Assert.AreEqual(4, a.legs);
212-
Assert.AreEqual(true, a.hungry);
213-
Assert.AreEqual(Kind.Mammal, a.kind);
221+
Assert.AreEqual(4, a.numberOfLegs);
222+
Assert.AreEqual(true, a.Hungry);
223+
Assert.AreEqual(Kind.Mammal, a.Kind);
214224
Assert.AreEqual(null, a.name);
215225
Assert.AreEqual("unknown", a.id);
216226
}
@@ -219,9 +229,9 @@ public static void DecodeTest3() {
219229
public static void DecodeTest4() {
220230
var a = Json.Decode<Bear>("{\"legs\":4,\"hungry\":true,\"kind\":\"Mammal\",\"name\":null}");
221231

222-
Assert.AreEqual(4, a.legs);
223-
Assert.AreEqual(true, a.hungry);
224-
Assert.AreEqual(Kind.Mammal, a.kind);
232+
Assert.AreEqual(4, a.numberOfLegs);
233+
Assert.AreEqual(true, a.Hungry);
234+
Assert.AreEqual(Kind.Mammal, a.Kind);
225235
Assert.AreEqual(null, a.name);
226236
}
227237

@@ -232,11 +242,11 @@ public static void DecodeTest5() {
232242
Assert.AreEqual(5, a.maxCargo);
233243
Assert.AreEqual("secret", a.Mission.target);
234244
Assert.AreEqual(new DateTime(2020, 4, 1), a.Mission.start);
235-
Assert.AreEqual(2, a.cargo[0].legs);
236-
Assert.AreEqual(Kind.Bird, a.cargo[0].kind);
237-
Assert.AreEqual(4, a.cargo[1].legs);
238-
Assert.AreEqual(Kind.Reptile, a.cargo[1].kind);
239-
Assert.AreEqual(new List<string>(new string[]{"John", "Homer", null}), a.driver);
245+
Assert.AreEqual(2, a.cargo[0].numberOfLegs);
246+
Assert.AreEqual(Kind.Bird, a.cargo[0].Kind);
247+
Assert.AreEqual(4, a.cargo[1].numberOfLegs);
248+
Assert.AreEqual(Kind.Reptile, a.cargo[1].Kind);
249+
Assert.AreEqual(new List<string>(new string[]{"John", "Homer", null}), a.Driver);
240250
}
241251

242252
[Test]
@@ -270,16 +280,16 @@ public static void DecodeVector3ListTest1() {
270280
public static void DecodeListTest1() {
271281
var list = Json.Decode<IList<Animal>>("[{\"legs\":4}, {\"legs\":2}]");
272282

273-
Assert.AreEqual(4, list[0].legs);
274-
Assert.AreEqual(2, list[1].legs);
283+
Assert.AreEqual(4, list[0].numberOfLegs);
284+
Assert.AreEqual(2, list[1].numberOfLegs);
275285
}
276286

277287
[Test]
278288
public static void DecodeListTest2() {
279289
var array = Json.Decode<Animal[]>("[{\"legs\":4}, {\"legs\":2}]");
280290

281-
Assert.AreEqual(4, array[0].legs);
282-
Assert.AreEqual(2, array[1].legs);
291+
Assert.AreEqual(4, array[0].numberOfLegs);
292+
Assert.AreEqual(2, array[1].numberOfLegs);
283293
}
284294

285295
[Test]

0 commit comments

Comments
 (0)