Are public constructors mandatory ?

Jul 18, 2009 at 1:22 AM

All,

I am trying to add JSON interfaces to an existing library (which consumes web-services, whose responses are dished out both in XML and in JSON formats) . The current implementation was reading the XML data. I am trying to add the JSON interfaces. Unto this end, I am trying to make no modifications to the existing object layout. The problem though is that the individual objects (which I am trying to deserialize) are all defined to have internal constructors. This means when I try to deserialize the JSON into objects, I run into the 'Could not find a public constructor for type ' exception.

So, short of modifying the existing object layout, is there anyway to make JSON.NET look at the constructor. Of course, I do think that a public constructor is required for JSON to create the object instance, but then this might not have been the first time something like this popped up. I searched the discussion board and couldn't find anything relevant. Any suggestions on how to get around the problem ?

Thank you

Gangadhar

Oct 6, 2009 at 4:20 PM
Edited Oct 6, 2009 at 4:22 PM

I'm having the same issue here... and don't see any fix in view. Have you been able to find something?

It seems that JSON.NET during a deserialization builds the related object from outside rather than from within its class (as System.Xml.Serialization does and therefore may use private constructors). I believe it's by design and my only wish is that JSON.NET had been architectured the same way than System.Xml.Serialization with ReadXml(XmlReader reader) of IXmlSerializable.

Eric.

 

Coordinator
Oct 7, 2009 at 6:24 AM

The reason Json.NET currently doesn't support internal/private constructors is that unlike XmlSerializer it can use a non-default constructor. If private constructors were supported which does it fall back to when no public default constructor is present?

Oct 7, 2009 at 1:38 PM
Edited Oct 7, 2009 at 3:08 PM

A possibility would be to implement a method ReadJson(reader) in the class (similar to the ReadXml(reader) method), which would leave it up to the developer how to deserialize to an object. I think the IXmlSerialization does it right but I have absolutely no clue how Microsoft coded it!!!

    public class Test : IJSonSerializable
    {
        public string ATestProperty { get; set; }
        ....

        #region Implementing IJSonSerializable
        /// <summary>
        /// Build the object from the JsonReader
        /// </summary>
        public void ReadJson(JsonReader reader)
        {
            // Loop through the reader
            ....
            this.ATestProperty = reader.ReadValue();
            ....
        }
        ....
        #endregion
    }

Coordinator
Oct 7, 2009 at 10:50 PM

Have you looked at creating a JsonConverter for your class? That allows you to customize how it is serializing/deserialized.

Oct 8, 2009 at 4:21 AM

Sorry, I hadn't really finished my message. What I really want to point out is that:

In my current understanding, I'm guessing a key to allow the Json deserialization to work with a private constructor (as well as private setters) is to be able to implement the method ReadJson(JsonReader reader) inside a class. Just like IXmlSerialization does.

This is a example how this work work:

 

    public class Test : IJSonSerializable
    {
        #region Properties
        public string ATestProperty { get; set; }
        public string AnotherTestProperty { get; private set; }
        ....
        #endregion

        #region Constructors
        /// <remarks>A default constructor is needed for deserialization.</remarks>
        private Test();

        public Test(string prop1)
        {
            this.ATestProperty = prop1;
        }
        ....
        #endregion

        #region Implementing IJSonSerializable
        /// <summary>
        /// Build the object from the JsonReader
        /// </summary>
        public void ReadJson(JsonReader reader)
        {
            // Loop through the reader
            ....
            this.ATestProperty = reader.ReadValue();
            ....
            this.AnotherTestProperty = reader.ReadValue();
        }
        ....
        #endregion
    }

 

A call to ReadJson() would "traverse" the object being built during a deserialization.

And I'm jsut trying to get an idea on how a deserialization would call that method. If someone has an idea about it was coded for XmlDeserialization I would be interesting in implementing it for JSON.NET.

Eric.

Coordinator
Oct 8, 2009 at 9:04 AM

If you wanted to access private variables I think a nested JsonConverter class would work.

I don't really want to add IJsonSerializable because you can do the same sort of thing using the existing JsonConverter functionality.

Oct 8, 2009 at 4:14 PM

I found out that I could create an object via a private constructor if I use AConstructorInfo.Invoke() rather than Activator.CreateInstance(). I got the hint from this post: http://stackoverflow.com/questions/440016/activator-createinstance-with-private-sealed-class

In my application all the serializable classes have a private parameterless constructor. By changing the method CreateAndPopulateObject() in the class JsonSerializerInternalReader with this code, I could use the private constructors to deserialize our JSON records.

    private object CreateAndPopulateObject(JsonReader reader, JsonObjectContract contract, string id)
    {
          object newObject;
          Type objectType = contract.UnderlyingType;

          ConstructorInfo c = objectType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { }, null);

          newObject = c.Invoke(new Object[] { });
          PopulateObject(newObject, reader, contract, id);
          return newObject;
    }

(This is just a proof of concept, not to use in a production environment.)

Eric.

Coordinator
Oct 11, 2009 at 9:48 PM

FYI Activator.CreateInstance(Type, bool) is better.

I've added ConstructorHandling to JsonSerializer/JsonSerializerSettings. Setting it to allow non public default constructors should give you what you want.

Oct 24, 2009 at 3:43 AM

Yes, it works, but I'm not able to deserialize properties with a private setter.

Eric.

Coordinator
Oct 24, 2009 at 4:10 AM

Private member serialization is the same as WCF. Put JsonPropertyAttribute or DataMemberAttribute on a private property if you want to serialize or deserialize it.