Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit b15364c

Browse files
committed
Serialize private properties and fields that are annotated with [DataMember] attribute
1 parent e465126 commit b15364c

File tree

2 files changed

+118
-13
lines changed

2 files changed

+118
-13
lines changed

src/ServiceStack.Text/ReflectionExtensions.cs

Lines changed: 84 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,44 @@ public static object CreateInstance(string typeName)
593593
return ctorFn();
594594
}
595595

596+
public static PropertyInfo[] GetAllProperties(this Type type)
597+
{
598+
if (type.IsInterface())
599+
{
600+
var propertyInfos = new List<PropertyInfo>();
601+
602+
var considered = new List<Type>();
603+
var queue = new Queue<Type>();
604+
considered.Add(type);
605+
queue.Enqueue(type);
606+
607+
while (queue.Count > 0)
608+
{
609+
var subType = queue.Dequeue();
610+
foreach (var subInterface in subType.GetTypeInterfaces())
611+
{
612+
if (considered.Contains(subInterface)) continue;
613+
614+
considered.Add(subInterface);
615+
queue.Enqueue(subInterface);
616+
}
617+
618+
var typeProperties = subType.GetTypesProperties();
619+
620+
var newPropertyInfos = typeProperties
621+
.Where(x => !propertyInfos.Contains(x));
622+
623+
propertyInfos.InsertRange(0, newPropertyInfos);
624+
}
625+
626+
return propertyInfos.ToArray();
627+
}
628+
629+
return type.GetTypesProperties()
630+
.Where(t => t.GetIndexParameters().Length == 0) // ignore indexed properties
631+
.ToArray();
632+
}
633+
596634
public static PropertyInfo[] GetPublicProperties(this Type type)
597635
{
598636
if (type.IsInterface())
@@ -648,22 +686,25 @@ internal static void Reset()
648686

649687
public static PropertyInfo[] GetSerializableProperties(this Type type)
650688
{
651-
var publicProperties = GetPublicProperties(type);
652-
return publicProperties.OnlySerializableProperties(type);
689+
var properties = type.IsDto()
690+
? type.GetAllProperties()
691+
: type.GetPublicProperties();
692+
return properties.OnlySerializableProperties(type);
653693
}
654694

655-
public static PropertyInfo[] OnlySerializableProperties(this PropertyInfo[] publicProperties, Type type = null)
695+
public static PropertyInfo[] OnlySerializableProperties(this PropertyInfo[] properties, Type type = null)
656696
{
657-
var publicReadableProperties = publicProperties.Where(x => x.PropertyGetMethod() != null);
697+
var isDto = type.IsDto();
698+
var readableProperties = properties.Where(x => x.PropertyGetMethod(nonPublic: isDto) != null);
658699

659-
if (type.IsDto())
700+
if (isDto)
660701
{
661-
return publicReadableProperties.Where(attr =>
702+
return readableProperties.Where(attr =>
662703
attr.HasAttribute<DataMemberAttribute>()).ToArray();
663704
}
664705

665706
// else return those properties that are not decorated with IgnoreDataMember
666-
return publicReadableProperties
707+
return readableProperties
667708
.Where(prop => prop.AllAttributes()
668709
.All(attr => {
669710
var name = attr.GetType().Name;
@@ -686,8 +727,8 @@ public static FieldInfo[] GetSerializableFields(this Type type)
686727
{
687728
if (type.IsDto())
688729
{
689-
return type.GetAllFields().Where(attr =>
690-
attr.HasAttribute<DataMemberAttribute>()).ToArray();
730+
return type.GetAllFields().Where(f =>
731+
f.HasAttribute<DataMemberAttribute>()).ToArray();
691732
}
692733

693734
if (!JsConfig.IncludePublicFields)
@@ -856,6 +897,26 @@ internal static PropertyInfo[] GetTypesPublicProperties(this Type subType)
856897
#endif
857898
}
858899

900+
internal static PropertyInfo[] GetTypesProperties(this Type subType)
901+
{
902+
#if (NETFX_CORE || PCL)
903+
var pis = new List<PropertyInfo>();
904+
foreach (var pi in subType.GetRuntimeProperties())
905+
{
906+
var mi = pi.GetMethod ?? pi.SetMethod;
907+
if (mi != null && mi.IsStatic) continue;
908+
pis.Add(pi);
909+
}
910+
return pis.ToArray();
911+
#else
912+
return subType.GetProperties(
913+
BindingFlags.FlattenHierarchy |
914+
BindingFlags.Public |
915+
BindingFlags.NonPublic |
916+
BindingFlags.Instance);
917+
#endif
918+
}
919+
859920
public static Assembly GetAssembly(this Type type)
860921
{
861922
#if (NETFX_CORE || PCL)
@@ -879,7 +940,12 @@ public static FieldInfo[] Fields(this Type type)
879940
#if (NETFX_CORE || PCL)
880941
return type.GetRuntimeFields().ToArray();
881942
#else
882-
return type.GetFields();
943+
return type.GetFields(
944+
BindingFlags.FlattenHierarchy |
945+
BindingFlags.Instance |
946+
BindingFlags.Static |
947+
BindingFlags.Public |
948+
BindingFlags.NonPublic);
883949
#endif
884950
}
885951

@@ -888,7 +954,12 @@ public static PropertyInfo[] Properties(this Type type)
888954
#if (NETFX_CORE || PCL)
889955
return type.GetRuntimeProperties().ToArray();
890956
#else
891-
return type.GetProperties();
957+
return type.GetProperties(
958+
BindingFlags.FlattenHierarchy |
959+
BindingFlags.Instance |
960+
BindingFlags.Static |
961+
BindingFlags.Public |
962+
BindingFlags.NonPublic);
892963
#endif
893964
}
894965

@@ -902,7 +973,7 @@ public static FieldInfo[] GetAllFields(this Type type)
902973
#if (NETFX_CORE || PCL)
903974
return type.GetRuntimeFields().ToArray();
904975
#else
905-
return type.GetPublicFields();
976+
return type.Fields();
906977
#endif
907978
}
908979

@@ -1033,7 +1104,7 @@ public static MethodInfo PropertyGetMethod(this PropertyInfo pi, bool nonPublic
10331104
#if (NETFX_CORE || PCL)
10341105
return pi.GetMethod;
10351106
#else
1036-
return pi.GetGetMethod(false);
1107+
return pi.GetGetMethod(nonPublic);
10371108
#endif
10381109
}
10391110

tests/ServiceStack.Text.Tests/JsonTests/BasicJsonTests.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,10 +519,32 @@ public void Empty_string_converts_to_null_DateTime()
519519
[DataContract]
520520
class ModelWithDataMemberField
521521
{
522+
public ModelWithDataMemberField() {}
523+
524+
public ModelWithDataMemberField(string privateField, string privateProperty)
525+
{
526+
PrivateField = privateField;
527+
PrivateProperty = privateProperty;
528+
}
529+
522530
[DataMember]
523531
public int Id;
524532
[DataMember]
525533
public string Name { get; set; }
534+
[DataMember]
535+
private string PrivateProperty { get; set; }
536+
[DataMember]
537+
private string PrivateField;
538+
539+
public string GetPrivateProperty()
540+
{
541+
return PrivateProperty;
542+
}
543+
544+
public string GetPrivateField()
545+
{
546+
return PrivateField;
547+
}
526548
}
527549

528550
[Test]
@@ -538,6 +560,18 @@ public void Explicit_DataMember_attribute_also_applies_to_public_fields()
538560
Assert.That(person.ToJson().FromJson<ModelWithDataMemberField>().Id, Is.EqualTo(1));
539561
}
540562

563+
[Test]
564+
public void Explicit_DataMember_attribute_serializers_private_properties_and_fields()
565+
{
566+
var person = new ModelWithDataMemberField("field", "property");
567+
568+
Assert.That(person.ToJsv().FromJsv<ModelWithDataMemberField>().GetPrivateField(), Is.EqualTo("field"));
569+
Assert.That(person.ToJson().FromJson<ModelWithDataMemberField>().GetPrivateField(), Is.EqualTo("field"));
570+
571+
Assert.That(person.ToJsv().FromJsv<ModelWithDataMemberField>().GetPrivateProperty(), Is.EqualTo("property"));
572+
Assert.That(person.ToJson().FromJson<ModelWithDataMemberField>().GetPrivateProperty(), Is.EqualTo("property"));
573+
}
574+
541575
[Test]
542576
public void Can_include_null_values_for_adhoc_types()
543577
{

0 commit comments

Comments
 (0)