Can Json.NET populate readonly fields in a class?
Asked Answered
S

3

12

I haven't seen much information about Json.NET supporting deserializing objects with readonly fields. I do notice that the .NET DataContract and DataMember attributes allow for populating readonly fields during deserializtion, but Json.NET doesn't seem to support this, at least from the behavior I'm seeing.

Salgado answered 14/11, 2010 at 19:1 Comment(0)
C
-2

afai can see changing a field to readonly results in a null value after deserialization. I had a working sample for another question (modified as shown below), and that's the behaviour I see.

public class NameAndId
{
    public string name;
    public int id; 
}

public class Data
{
    public NameAndId[] data;
}

public class Target
{
    public string id;
    public readonly NameAndId from;
    public DateTime updated_time;
    public readonly string message;
    public Data likes;
}

public class Program
{
    static void Main(string[] args)
    {
        string json = File.ReadAllText(@"c:\temp\json.txt");
        Target newTarget = JsonConvert.DeserializeObject<Target>(json);
    }
}
Carrollcarronade answered 14/11, 2010 at 21:5 Comment(4)
Yeah, that's the same behavior I see. Unfortunately, this along with constructor restrictions on the serialized objects is the deal-breaker for Json.NET for me. :(Salgado
My guess is that the .Net builtin classes have privileged access to those fields that are not available to the third-party library.Carrollcarronade
It's no different than setting any other field with reflection, so it's doable and I've filed a feature request on the Json.NET issue tracker.Lemniscus
@MarkLeMoine There are actually - to my knowledge - no ctor restrictions that you can't get around by creating a custom contract resolver.Lemniscus
L
10

Not the most elegant solution, but you can extend the DefaultConstractResolver to do it:

public class ContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        property.Writable = CanSetMemberValue(member, true);
        return property;
    }

    public static bool CanSetMemberValue(MemberInfo member, bool nonPublic)
    {
        switch (member.MemberType)
        {
            case MemberTypes.Field:
                var fieldInfo = (FieldInfo)member;

                return nonPublic || fieldInfo.IsPublic;
            case MemberTypes.Property:
                var propertyInfo = (PropertyInfo)member;

                if (!propertyInfo.CanWrite)
                    return false;
                if (nonPublic)
                    return true;
                return (propertyInfo.GetSetMethod(nonPublic) != null);
            default:
                return false;
        }
    }
}

I have just remove one little check from the CanSetMemberValue method. Unfortunately it's neither virtual nor an instance method, so I had to override CreateProperty as well.

Lemniscus answered 6/7, 2011 at 20:44 Comment(0)
M
6

This can be done now. Declare your properties using the JsonProperty attribute, and ensure that they have a protected set declared:

[JsonProperty("Name")] public string Name {get; protected set;}

This didn't work for me when using only a get, but works perfectly with the protected set.

Motorboating answered 30/8, 2017 at 21:14 Comment(2)
The [JsonProperty] attribute is not required for this to workSpindell
It didn't work for me any other way in 2017. And I tried practically every combination I could come up with. I was writing a code-generator, so I spent quite a lot of time making the generated code as tight as possible. But this certainly may have been changed in the last two years.Motorboating
C
-2

afai can see changing a field to readonly results in a null value after deserialization. I had a working sample for another question (modified as shown below), and that's the behaviour I see.

public class NameAndId
{
    public string name;
    public int id; 
}

public class Data
{
    public NameAndId[] data;
}

public class Target
{
    public string id;
    public readonly NameAndId from;
    public DateTime updated_time;
    public readonly string message;
    public Data likes;
}

public class Program
{
    static void Main(string[] args)
    {
        string json = File.ReadAllText(@"c:\temp\json.txt");
        Target newTarget = JsonConvert.DeserializeObject<Target>(json);
    }
}
Carrollcarronade answered 14/11, 2010 at 21:5 Comment(4)
Yeah, that's the same behavior I see. Unfortunately, this along with constructor restrictions on the serialized objects is the deal-breaker for Json.NET for me. :(Salgado
My guess is that the .Net builtin classes have privileged access to those fields that are not available to the third-party library.Carrollcarronade
It's no different than setting any other field with reflection, so it's doable and I've filed a feature request on the Json.NET issue tracker.Lemniscus
@MarkLeMoine There are actually - to my knowledge - no ctor restrictions that you can't get around by creating a custom contract resolver.Lemniscus

© 2022 - 2024 — McMap. All rights reserved.