diff --git a/nanoFramework.Json.Benchmark/SerializationBenchmarks/ReferenceTypesSerializationBenchmark.cs b/nanoFramework.Json.Benchmark/SerializationBenchmarks/ReferenceTypesSerializationBenchmark.cs
index df6ff952..ccdfd728 100644
--- a/nanoFramework.Json.Benchmark/SerializationBenchmarks/ReferenceTypesSerializationBenchmark.cs
+++ b/nanoFramework.Json.Benchmark/SerializationBenchmarks/ReferenceTypesSerializationBenchmark.cs
@@ -23,6 +23,8 @@ public class ReferenceTypesSerializationBenchmark : BaseIterationBenchmark
private JsonTestClassComplex complexClass;
private JsonTestTown myTown;
private ArrayList arrayList;
+ private JsonIgnoreTestClass ignoreTest;
+ private JsonIgnoreTestClassNoAttr ignoreTestNoAttr;
[Setup]
public void Setup()
@@ -65,6 +67,8 @@ public void Setup()
{ DateTime.UtcNow },
{ TimeSpan.FromSeconds(100) }
};
+ ignoreTest = JsonIgnoreTestClass.CreateTestClass();
+ ignoreTestNoAttr = JsonIgnoreTestClassNoAttr.CreateTestClass();
}
[Benchmark]
@@ -120,5 +124,50 @@ public void ArrayList()
JsonConvert.SerializeObject(arrayList);
});
}
+
+ [Benchmark]
+ public void ClassWithAttributeIgnoreEnabled()
+ {
+ RunInIteration(() =>
+ {
+ // Turn ON the UseIgnore setting
+ Configuration.Settings.UseIgnoreAttribute = true;
+ JsonConvert.SerializeObject(ignoreTest);
+ // Turn OFF the UseIgnore setting
+ Configuration.Settings.UseIgnoreAttribute = false;
+ });
+ }
+ [Benchmark]
+ public void ClassWithAttributeIgnoreDisabled()
+ {
+ RunInIteration(() =>
+ {
+ // Turn OFF the UseIgnore setting
+ Configuration.Settings.UseIgnoreAttribute = false;
+ JsonConvert.SerializeObject(ignoreTest);
+ });
+ }
+ [Benchmark]
+ public void ClassNoAttributeIgnoreEnabled()
+ {
+ RunInIteration(() =>
+ {
+ // Turn ON the UseIgnore setting
+ Configuration.Settings.UseIgnoreAttribute = true;
+ JsonConvert.SerializeObject(ignoreTestNoAttr);
+ // Turn OFF the UseIgnore setting
+ Configuration.Settings.UseIgnoreAttribute = false;
+ });
+ }
+ [Benchmark]
+ public void ClassNoAttributeIgnoreDisabled()
+ {
+ RunInIteration(() =>
+ {
+ // Turn OFF the UseIgnore setting
+ Configuration.Settings.UseIgnoreAttribute = false;
+ JsonConvert.SerializeObject(ignoreTestNoAttr);
+ });
+ }
}
}
diff --git a/nanoFramework.Json.Test.Shared/JsonIgnoreTestClass.cs b/nanoFramework.Json.Test.Shared/JsonIgnoreTestClass.cs
new file mode 100644
index 00000000..35be5c49
--- /dev/null
+++ b/nanoFramework.Json.Test.Shared/JsonIgnoreTestClass.cs
@@ -0,0 +1,63 @@
+//
+// Copyright (c) .NET Foundation and Contributors
+// See LICENSE file in the project root for full license information.
+//
+
+namespace nanoFramework.Json.Test.Shared
+{
+ ///
+ /// Used to test the JsonIgnore attribute.
+ ///
+ [JsonIgnore("MyIgnoredProperty, AnotherIgnoredProperty")]
+ public class JsonIgnoreTestClass
+ {
+ public int TestProperty { get; set; }
+ public int OtherTestProperty { get; set; }
+ public int AThirdTestProperty { get; set; }
+ public int MyIgnoredProperty => TestProperty + 1;
+ public int AnotherIgnoredProperty => OtherTestProperty + 1;
+
+ public int this[int index] => TestProperty + index;
+
+ public static JsonIgnoreTestClass CreateTestClass()
+ {
+ return new JsonIgnoreTestClass()
+ {
+ TestProperty = 1,
+ OtherTestProperty = 2,
+ AThirdTestProperty = 3
+ };
+ }
+
+ public bool IsEqual(JsonIgnoreTestClass otherInstance)
+ {
+ return (TestProperty == otherInstance.TestProperty
+ && OtherTestProperty == otherInstance.OtherTestProperty
+ && AThirdTestProperty == otherInstance.AThirdTestProperty);
+ }
+ }
+
+ ///
+ /// Used to 1-to-1 compare with JsonIgnoreTestClass.
+ ///
+ public class JsonIgnoreTestClassNoAttr
+ {
+ public int TestProperty { get; set; }
+ public int OtherTestProperty { get; set; }
+ public int AThirdTestProperty { get; set; }
+ public int MyIgnoredProperty => TestProperty + 1;
+ public int AnotherIgnoredProperty => OtherTestProperty + 1;
+
+ public int this[int index] => TestProperty + index;
+
+ public static JsonIgnoreTestClassNoAttr CreateTestClass()
+ {
+ return new JsonIgnoreTestClassNoAttr()
+ {
+ TestProperty = 1,
+ OtherTestProperty = 2,
+ AThirdTestProperty = 3
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/nanoFramework.Json.Test.Shared/nanoFramework.Json.Test.Shared.projitems b/nanoFramework.Json.Test.Shared/nanoFramework.Json.Test.Shared.projitems
index 884026b0..0929649b 100644
--- a/nanoFramework.Json.Test.Shared/nanoFramework.Json.Test.Shared.projitems
+++ b/nanoFramework.Json.Test.Shared/nanoFramework.Json.Test.Shared.projitems
@@ -10,6 +10,7 @@
+
diff --git a/nanoFramework.Json.Test/JsonUnitTests.cs b/nanoFramework.Json.Test/JsonUnitTests.cs
index 819eec3c..f3e6d77c 100644
--- a/nanoFramework.Json.Test/JsonUnitTests.cs
+++ b/nanoFramework.Json.Test/JsonUnitTests.cs
@@ -1384,6 +1384,34 @@ public void DeserializeObjectWithStringContainingNonAsciiChars()
Assert.AreEqual(input.Value, result.Value);
}
+ [TestMethod]
+ public void DeserializeObjectWithJsonIgnoreAttribute()
+ {
+ OutputHelper.WriteLine("Starting JsonIgnore Test...");
+ Json.Configuration.Settings.UseIgnoreAttribute = true;
+ OutputHelper.WriteLine("UseIgnoreAttribute enabled.");
+
+ var testObject = JsonIgnoreTestClass.CreateTestClass();
+ var jsonString = JsonSerializer.SerializeObject(testObject);
+ OutputHelper.WriteLine("After serialize.");
+ JsonIgnoreTestClass dserResult = JsonConvert.DeserializeObject(jsonString, typeof(JsonIgnoreTestClass)) as JsonIgnoreTestClass;
+ OutputHelper.WriteLine("After deserialize.");
+
+ //test serialize and deserialize
+ bool jsonSuccess = testObject.IsEqual(dserResult);
+ Assert.IsTrue(jsonSuccess);
+ OutputHelper.WriteLine("Serialization/Deserialization was " + (jsonSuccess ? "" : "NOT ") + "successful.");
+
+ //test ignored properties are actually ignored
+ bool areIgnoredPropsPresent = jsonString.Contains("MyIgnoredProperty")
+ || jsonString.Contains("AnotherIgnoredProperty");
+ Assert.IsFalse(areIgnoredPropsPresent);
+ OutputHelper.WriteLine("Ignore was " + (areIgnoredPropsPresent ? "NOT " : "") + "successful.");
+
+ Json.Configuration.Settings.UseIgnoreAttribute = false;
+ OutputHelper.WriteLine("UseIgnoreAttribute set back to false.");
+ OutputHelper.WriteLine("Finished JsonIgnore Test.");
+ }
}
#region Test classes
diff --git a/nanoFramework.Json/Configuration/Settings.cs b/nanoFramework.Json/Configuration/Settings.cs
index adb0637f..35c16c94 100644
--- a/nanoFramework.Json/Configuration/Settings.cs
+++ b/nanoFramework.Json/Configuration/Settings.cs
@@ -23,6 +23,11 @@ public static class Settings
///
public static bool CaseSensitive { get; set; } = true;
+ ///
+ /// If true, will check for JsonIgnoreAttribute upon serialization. Has a performance cost. Defaults to false.
+ ///
+ public static bool UseIgnoreAttribute { get; set; } = false;
+
///
/// Gets or sets a value indicating whether deserialization should throw exception when no property found.
///
diff --git a/nanoFramework.Json/JsonIgnoreAttribute.cs b/nanoFramework.Json/JsonIgnoreAttribute.cs
new file mode 100644
index 00000000..b5f5c471
--- /dev/null
+++ b/nanoFramework.Json/JsonIgnoreAttribute.cs
@@ -0,0 +1,33 @@
+// Copyright (c) .NET Foundation and Contributors
+// See LICENSE file in the project root for full license information.
+
+using System;
+
+namespace nanoFramework.Json
+{
+ ///
+ /// Hides properties from the json serializer.
+ ///
+ [System.AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
+ public sealed class JsonIgnoreAttribute : Attribute
+ {
+ ///
+ /// Array of property names for json serializer to ignore.
+ ///
+ public string[] PropertyNames { get; set; }
+
+ ///
+ /// Hides properties from the json serializer.
+ ///
+ /// A comma separated list of property names to ignore in json.
+ public JsonIgnoreAttribute(string getterNamesToIgnore)
+ {
+ // Split by commas, then trim whitespace for each
+ PropertyNames = getterNamesToIgnore.Split(',');
+ for(int i = 0; i < PropertyNames.Length; i++)
+ {
+ PropertyNames[i] = PropertyNames[i].Trim();
+ }
+ }
+ }
+}
diff --git a/nanoFramework.Json/JsonSerializer.cs b/nanoFramework.Json/JsonSerializer.cs
index 9a44f845..481d660f 100644
--- a/nanoFramework.Json/JsonSerializer.cs
+++ b/nanoFramework.Json/JsonSerializer.cs
@@ -77,6 +77,13 @@ public static string SerializeObject(object o, bool topObject = true)
private static string SerializeClass(object o, Type type)
{
+ // Cache the type's class-level attributes only if UseIgnoreAttribute setting is enabled.
+ object[] classAttributes = null;
+ if (Settings.UseIgnoreAttribute)
+ {
+ classAttributes = type.GetCustomAttributes(false);
+ }
+
Hashtable hashtable = new();
// Iterate through all of the methods, looking for internal GET properties
@@ -84,19 +91,24 @@ private static string SerializeClass(object o, Type type)
foreach (MethodInfo method in methods)
{
- if (!ShouldSerializeMethod(method))
+ if (!ShouldSerializeMethod(method, classAttributes))
{
continue;
}
-
+
object returnObject = method.Invoke(o, null);
- hashtable.Add(method.Name.Substring(4), returnObject);
+ hashtable.Add(ExtractGetterName(method), returnObject);
}
return SerializeIDictionary(hashtable);
}
- private static bool ShouldSerializeMethod(MethodInfo method)
+ ///
+ /// Checks whether a property (MethodInfo) should be serialized.
+ ///
+ /// The MethodInfo to check.
+ /// The cached class-level attributes. Only used if UseIgnoreAttribute is true.
+ private static bool ShouldSerializeMethod(MethodInfo method, object[] classAttributes)
{
// We care only about property getters when serializing
if (!method.Name.StartsWith("get_"))
@@ -125,9 +137,71 @@ private static bool ShouldSerializeMethod(MethodInfo method)
return false;
}
+ // Ignore indexer properties
+ // (string comparison is MUCH faster than method.GetParameters)
+ if (method.Name == "get_Item")
+ {
+ return false;
+ }
+
+ // Ignore properties listed in [JsonIgnore()] attribute
+ // Only check for attribute if the setting is on
+ if (Settings.UseIgnoreAttribute &&
+ ShouldIgnorePropertyFromClassAttribute(method, classAttributes))
+ {
+ return false;
+ }
+
return true;
}
+ ///
+ /// Checks for JsonIgnore attribute on a method's declaring class. Helper method for SerializeClass.
+ ///
+ /// The MethodInfo of a property getter to check.
+ /// The cached class-level attributes. Only used if UseIgnoreAttribute is true.
+ ///
+ private static bool ShouldIgnorePropertyFromClassAttribute(MethodInfo method, object[] classAttributes)
+ {
+ string[] gettersToIgnore = null;
+
+ foreach (object attribute in classAttributes)
+ {
+ if (attribute is JsonIgnoreAttribute ignoreAttribute)
+ {
+ gettersToIgnore = ignoreAttribute.PropertyNames;
+ break;
+ }
+ }
+
+ if (gettersToIgnore == null)
+ {
+ return false;
+ }
+
+ foreach (string propertyName in gettersToIgnore)
+ {
+ if (propertyName.Equals(ExtractGetterName(method)))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Extracts "get_" from MethodInfo.Name to retrieve the name of a getter property.
+ /// Assumes the MethodInfo is for a getter, checked elsewhere.
+ ///
+ /// The MethodInfo of the getter property.
+ /// The property name as it appears in written code.
+ private static string ExtractGetterName(MethodInfo getterMethodInfo)
+ {
+ // Substring(4) is to extract the "get_" for property methods
+ return getterMethodInfo.Name.Substring(4);
+ }
+
///
/// Convert an IEnumerable to a JSON string.
///
diff --git a/nanoFramework.Json/nanoFramework.Json.nfproj b/nanoFramework.Json/nanoFramework.Json.nfproj
index d72860e5..9a4f1d09 100644
--- a/nanoFramework.Json/nanoFramework.Json.nfproj
+++ b/nanoFramework.Json/nanoFramework.Json.nfproj
@@ -52,6 +52,7 @@
+