5

I have a class inside a DLL which is not labelled with DataContract, JsonProperty etc. Now I want to serialize instance of the class as JSON objects, with the C# property names shortened.

For instance, the class is:

public class Foo
{
    public string SomeLengthyCSharpPropertyName { get; set; }
}

I wonder if I could create a mapping between the C# names and the json names. I cannot directly add the DataContract, JsonProperty attributes like below. Is there any workaround?

[DataContract]
public class Foo
{
    [JsonProperty("s")]
    public string SomeLengthyCSharpPropertyName { get; set; }
}

I tend not to create a another class with the same but JsonProperty-decorated properties and copy the properties to the new class and then serialize.

Sam Axe
  • 33,313
  • 9
  • 55
  • 89
lzl124631x
  • 4,485
  • 2
  • 30
  • 49
  • Did you tried to use Newtonsoft.Json library? With this library you can serialize and deserialize without decorators – qjuanp Sep 01 '15 at 04:44
  • @qjuanp I think we are talking about the same library? How can I shorten the property name without decorators? – lzl124631x Sep 01 '15 at 05:10
  • sorry, I miss that particular detail. Just use [`JsonConvert.SerializeObject(fooInstance)`](http://www.newtonsoft.com/json/help/html/Overload_Newtonsoft_Json_JsonConvert_SerializeObject.htm) and [`JsonConvert.DeserializeObject( stringWithFooJson )`](http://www.newtonsoft.com/json/help/html/Overload_Newtonsoft_Json_JsonConvert_DeserializeObject.htm) – qjuanp Sep 01 '15 at 05:18
  • @qjuanp: you *really* need to read the whole question and understand it before commenting. – Sam Axe Sep 01 '15 at 05:19
  • http://stackoverflow.com/questions/19792274/alternate-property-name-while-deserializing – Sam Axe Sep 01 '15 at 05:25
  • this may also give you some ideas http://stackoverflow.com/questions/11679804/json-net-rename-properties – Sam Axe Sep 01 '15 at 05:31

2 Answers2

2

You can make your own custom ContractResolver with a dictionary of override attributes by member, then override CreateProperty() and apply the overrides to the JsonProperty returned by the base class:

public class JsonPropertyOverride
{
    public string PropertyName { get; set; }

    public bool? Ignored { get; set; }

    // Others as required from http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonPropertyAttribute.htm
    // Changing all value type properties to nullables.
}

public class OverrideContractResolver : DefaultContractResolver
{
    readonly Dictionary<MemberInfo, JsonPropertyOverride> overrides; // A private copy for thread safety.

    public OverrideContractResolver(IDictionary<MemberInfo, JsonPropertyOverride> overrides)
        : base()
    {
        if (overrides == null)
            throw new ArgumentNullException();
        this.overrides = overrides.ToDictionary(p => p.Key, p => p.Value);
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        if (property != null)
        {
            JsonPropertyOverride attr;
            if (overrides.TryGetValue(member, out attr))
            {
                if (attr.PropertyName != null)
                    property.PropertyName = ResolvePropertyName(attr.PropertyName);
                if (attr.Ignored != null)
                    property.Ignored = attr.Ignored.Value;
            }
        }
        return property;
    }
}

You could also inherit from CamelCasePropertyNamesContractResolver if you prefer.

Then use it like:

public class Foo
{
    public string SomeLengthyCSharpPropertyName { get; set; }

    public string DefaultNotIgnored { get; set; }

    [JsonIgnore]
    public string DefaultIgnored { get; set; }
}

public class TestClass
{
    public static void Test()
    {
        var foo = new Foo { SomeLengthyCSharpPropertyName = "SomeLengthyCSharpPropertyName", DefaultIgnored = "DefaultIgnored", DefaultNotIgnored = "DefaultNotIgnored" };

        var resolver = new OverrideContractResolver(new Dictionary<MemberInfo, JsonPropertyOverride> { 
            { typeof(Foo).GetProperty("SomeLengthyCSharpPropertyName"), new JsonPropertyOverride { PropertyName = "c"  } }, 
            { typeof(Foo).GetProperty("DefaultNotIgnored"), new JsonPropertyOverride { Ignored = true  } }, 
            { typeof(Foo).GetProperty("DefaultIgnored"), new JsonPropertyOverride { Ignored = false  } }, 
        });
        var settings = new JsonSerializerSettings { ContractResolver = resolver };

        var json = JsonConvert.SerializeObject(foo, settings); // Outputs {"c":"SomeLengthyCSharpPropertyName","DefaultIgnored":"DefaultIgnored"}
        Debug.WriteLine(json);

        var expectedJson = @"{ ""c"": ""SomeLengthyCSharpPropertyName"", ""DefaultIgnored"": ""DefaultIgnored"" }";
        var ok = JToken.DeepEquals(JToken.Parse(json), JToken.Parse(expectedJson));
        Debug.Assert(ok); // No assert

        var foo2 = JsonConvert.DeserializeObject<Foo>(json, settings);

        var ok2 = foo2.DefaultIgnored == foo.DefaultIgnored && foo2.SomeLengthyCSharpPropertyName == foo.SomeLengthyCSharpPropertyName;
        Debug.Assert(ok2); // No assert
    }
}
dbc
  • 104,963
  • 20
  • 228
  • 340
0

It definitely feels like a workaround, but you might want to consider it. If the class is not sealed, you can inherit from it, override it's properties (those you want to change) and then decorate them.

Felix Av
  • 1,254
  • 1
  • 14
  • 22