Browse Source

DataContract/Member support

Brad Robinson 11 years ago
parent
commit
7782c7d46f
5 changed files with 115 additions and 31 deletions
  1. 2 1
      EmitDev/EmitDev.csproj
  2. 24 0
      EmitDev/Program.cs
  3. 71 29
      PetaJson.cs
  4. 1 0
      TestCases/TestCases.csproj
  5. 17 1
      readme.md

+ 2 - 1
EmitDev/EmitDev.csproj

@@ -20,7 +20,7 @@
     <DebugType>full</DebugType>
     <Optimize>false</Optimize>
     <OutputPath>bin\Debug\</OutputPath>
-    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DefineConstants>TRACE;DEBUG</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
@@ -36,6 +36,7 @@
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Core" />
+    <Reference Include="System.Runtime.Serialization" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />

+ 24 - 0
EmitDev/Program.cs

@@ -4,6 +4,9 @@ using System.Linq;
 using System.Text;
 using PetaJson;
 using System.Globalization;
+using System.Runtime.Serialization.Json;
+using System.IO;
+using System.Runtime.Serialization;
 
 namespace EmitDev
 {
@@ -51,12 +54,33 @@ namespace EmitDev
         }
     }
 
+    [DataContract]
+    class DCTest
+    {
+        [DataMember(Name="AAA")] public string ZZZ;
+        [DataMember] private int priv;
+        [DataMember] public string Prop { get; set; }
+        [DataMember] public string Field;
+    }
 
     class Program
     {
 
         static void Main(string[] args)
         {
+            Json.WriteWhitespaceDefault = false;
+
+            var dc = new DCTest() { Prop = "Hi", Field = "Bye" };
+            var ser = new DataContractJsonSerializer(typeof(DCTest));
+            var memStream = new MemoryStream();
+            ser.WriteObject(memStream, dc);
+            var str = Encoding.UTF8.GetString(memStream.GetBuffer());
+            Console.WriteLine(str);
+
+            Console.WriteLine(Json.Format(dc));
+            return;
+
+
             var p = new Person()
             {
                 StringField = "Hello World",

+ 71 - 29
PetaJson.cs

@@ -7,6 +7,7 @@
 
 // Define PETAJSON_NO_DYNAMIC to disable Expando support
 // Define PETAJSON_NO_EMIT to disable Reflection.Emit
+// Define PETAJSON_NO_DATACONTRACT to disable support for [DataContract]/[DataMember]
 
 using System;
 using System.Collections.Generic;
@@ -23,6 +24,10 @@ using System.Dynamic;
 #if !PETAJSON_NO_EMIT
 using System.Reflection.Emit;
 #endif
+#if !PETAJSON_NO_DATACONTRACT
+using System.Runtime.Serialization;
+#endif
+
 
 
 namespace PetaJson
@@ -403,6 +408,11 @@ namespace PetaJson
     //
     // [Json(KeepInstance=true)] causes container/subobject types to be serialized into the existing member instance (if not null)
     //
+    // You can also use the system supplied DataContract and DataMember attributes.  They'll only be used if there
+    // are no PetaJson attributes on the class or it's members. You must specify DataContract on the type and
+    // DataMember on any fields/properties that require serialization.  There's no need for exclude attribute.
+    // When using DataMember, the name of the field or property is used as is - the first letter is left in upper case
+    //
     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field)]
     public class JsonAttribute : Attribute
     {
@@ -1025,6 +1035,11 @@ namespace PetaJson
             static char[] _charsToEscape = new char[] { '\"', '\r', '\n', '\t', '\f', '\0', '\\', '\'' };
             public void WriteStringLiteral(string str)
             {
+                if (str == null)
+                {
+                    _writer.Write("null");
+                    return;
+                }
                 _writer.Write("\"");
 
                 int pos = 0;
@@ -1333,48 +1348,75 @@ namespace PetaJson
                 // Check cache
                 return _cache.Get(type, () =>
                 {
+                    var allMembers = Utils.GetAllFieldsAndProperties(type); 
+
                     // Does type have a [Json] attribute
                     bool typeMarked = type.GetCustomAttributes(typeof(JsonAttribute), true).OfType<JsonAttribute>().Any();
 
                     // Do any members have a [Json] attribute
-                    bool anyFieldsMarked = Utils.GetAllFieldsAndProperties(type).Any(x => x.GetCustomAttributes(typeof(JsonAttribute), false).OfType<JsonAttribute>().Any());
+                    bool anyFieldsMarked = allMembers.Any(x => x.GetCustomAttributes(typeof(JsonAttribute), false).OfType<JsonAttribute>().Any());
 
-                    // Should we serialize all public methods?
-                    bool serializeAllPublics = typeMarked || !anyFieldsMarked;
-
-                    // Build 
-                    var ri = CreateReflectionInfo(type, mi =>
+#if !PETAJSON_NO_DATACONTRACT
+                    // Try with DataContract and friends
+                    if (!typeMarked && !anyFieldsMarked && type.GetCustomAttributes(typeof(DataContractAttribute), true).OfType<DataContractAttribute>().Any())
                     {
-                        // Explicitly excluded?
-                        if (mi.GetCustomAttributes(typeof(JsonExcludeAttribute), false).OfType<JsonExcludeAttribute>().Any())
-                            return null;
-
-                        // Get attributes
-                        var attr = mi.GetCustomAttributes(typeof(JsonAttribute), false).OfType<JsonAttribute>().FirstOrDefault();
-                        if (attr != null)
+                        var ri = CreateReflectionInfo(type, mi =>
                         {
-                            return new JsonMemberInfo()
+                            // Get attributes
+                            var attr = mi.GetCustomAttributes(typeof(DataMemberAttribute), false).OfType<DataMemberAttribute>().FirstOrDefault();
+                            if (attr != null)
                             {
-                                Member = mi,
-                                JsonKey = attr.Key ?? mi.Name.Substring(0, 1).ToLower() + mi.Name.Substring(1),
-                                KeepInstance = attr.KeepInstance,
-                            };
-                        }
+                                return new JsonMemberInfo()
+                                {
+                                    Member = mi,
+                                    JsonKey = attr.Name ?? mi.Name,     // No lower case first letter if using DataContract/Member
+                                };
+                            }
+
+                            return null;
+                        });
+
+                        ri.Members.Sort((a, b) => String.CompareOrdinal(a.JsonKey, b.JsonKey));    // Match DataContractJsonSerializer
+                        return ri;
+                    }
+#endif
+                    {
+                        // Should we serialize all public methods?
+                        bool serializeAllPublics = typeMarked || !anyFieldsMarked;
 
-                        // Serialize all publics?
-                        if (serializeAllPublics && Utils.IsPublic(mi))
+                        // Build 
+                        var ri = CreateReflectionInfo(type, mi =>
                         {
-                            return new JsonMemberInfo()
+                            // Explicitly excluded?
+                            if (mi.GetCustomAttributes(typeof(JsonExcludeAttribute), false).Any())
+                                return null;
+
+                            // Get attributes
+                            var attr = mi.GetCustomAttributes(typeof(JsonAttribute), false).OfType<JsonAttribute>().FirstOrDefault();
+                            if (attr != null)
                             {
-                                Member = mi,
-                                JsonKey = mi.Name.Substring(0, 1).ToLower() + mi.Name.Substring(1),
-                            };
-                        }
+                                return new JsonMemberInfo()
+                                {
+                                    Member = mi,
+                                    JsonKey = attr.Key ?? mi.Name.Substring(0, 1).ToLower() + mi.Name.Substring(1),
+                                    KeepInstance = attr.KeepInstance,
+                                };
+                            }
 
-                        return null;
-                    });
+                            // Serialize all publics?
+                            if (serializeAllPublics && Utils.IsPublic(mi))
+                            {
+                                return new JsonMemberInfo()
+                                {
+                                    Member = mi,
+                                    JsonKey = mi.Name.Substring(0, 1).ToLower() + mi.Name.Substring(1),
+                                };
+                            }
 
-                    return ri;
+                            return null;
+                        });
+                        return ri;
+                    }
                 });
             }
 

+ 1 - 0
TestCases/TestCases.csproj

@@ -36,6 +36,7 @@
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Core" />
+    <Reference Include="System.Runtime.Serialization" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />

+ 17 - 1
readme.md

@@ -169,8 +169,24 @@ In this example the existing CurrentSettings object will be serialized into. If
 was set to false, PetaJson would instantiate a new Settings object, load it and then assign
 it to the CurrentSettings property.
 
+## DataContract and DataMember attributes
 
-Note: support for standard [DataContract] and [DataMember] probably coming soon.
+You can also use the system supplied DataContract and DataMember attributes.  They'll only be used if there
+are no PetaJson attributes on the class or it's members. You must specify DataContract on the type and
+DataMember on all members that require serialization.  
+
+	[DataContract]
+	class Person
+	{
+		[DataMember] public string Name;		// Serialized as "Name"
+		[DataMember] public string Address;		// Serialized as "Address"
+		[DataMember(Name="Cool")]
+		public string Hot;						// Serialized as "Cool"
+		public int Age {...}					// Not serialized
+	}
+
+Note that the first letter of the member is left as upper case (unlike when using the Json attributes) and
+there's no need for an exclude attribute as only members marked DataMember are included in the first place.
 
 ## Custom Formatting