Json.NET CamelCasePropertyNameContractResolver not working with private property setters - c#

I am using Json.NET (8.0.3) and I am trying to use the CamelCasePropertyNameContractResolver with JsonConvert.DeseralizeObject() so that I can read JSON with camel case properties. Here is an example of the JSON.
{ "name":"somename", "type":"sometype" }
Here is the class I am trying to deserialize to:
public class MyClass {
public string Name { get; private set; }
public string Type { get; private set; }
}
If I use JsonConvert.DeseralizeObject the Name and Type values are null because technically the class property names do not match the JSON property names. This was expected. If I add the JsonProperty attribute then it will deserialize correctly (also expected).
public class MyClass {
[JsonProperty("name")]
public string Name { get; private set; }
[JsonProperty("type")]
public string Type { get; private set; }
}
I do not want to put the JsonProperty attribute on all of the properties so I tried the CamelCasePropertyNameContractResolver.
JsonConvert.DefaultSettings = () => new JsonSerialierSettings {
ContractResolver = new CamelCasePropertyNameContractResolver()
};
MyClass value = JsonConvert.DeserializeObject<MyClass>(json);
The Name and Type properties of the MyClass object are both null which was unexpected. If I make the setter public then it works correctly.
public class MyClass {
public string Name { get; set; }
public string Type { get; set; }
}
The obvious answer here is to just keep the setter public, but if I want/need to have the setter private, how can I get the CamelCasePropertyNameContractResolver to work with private setters? Am I doing something wrong, or is this a possible bug?

You can do it by writing a custom ContractResolver
string json = #"{""name"":""somename"", ""type"":""sometype"" }";
var settings = new JsonSerializerSettings() {
ContractResolver = new AllPropertiesContractResolver() };
var res = JsonConvert.DeserializeObject<MyClass>(json,settings);
public class MyClass
{
public string Name { get; private set; }
public string Type { get; private set; }
}
public class AllPropertiesContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Select(x => new Newtonsoft.Json.Serialization.JsonProperty()
{
PropertyName = x.Name,
PropertyType = x.PropertyType,
Readable = true,
ValueProvider = new AllPropertiesValueProvider(x),
Writable = true
})
.ToList();
return props;
}
}
public class AllPropertiesValueProvider : Newtonsoft.Json.Serialization.IValueProvider
{
PropertyInfo _propertyInfo;
public AllPropertiesValueProvider(PropertyInfo p)
{
_propertyInfo = p;
}
public object GetValue(object target)
{
return _propertyInfo.GetValue(target); //Serialization
}
public void SetValue(object target, object value)
{
_propertyInfo.SetValue(target, value, null); //Deserialization
}
}
BTW: If I use JsonConvert.DeseralizeObject the Name and Type values are null because technically the class property names do not match the JSON property names. is not correct. If your properties had public setters and getters, deserialization would ignore the cases when using default settings (this is what I use in this answer. My ContractResolver additionally includes private properties in deserialization process; that is all).....
See the other question I used the same ContractResolver: What am I doing wrong with JSON.NET's JsonConvert

Related

How to exclude properties from JsonConvert.PopulateObject that don't exist in some base type or interface?

Is it possible to define options to JsonConvert.PopulateObject to exclude fields given in a json, that does not exists in the target object's interface implementation?
public interface IElementWriter
{
string Name { get; set; }
}
public interface IElementUpdateWriter : IElementWriter
{
string FirstName { get; set; }
}
public interface IElementInsertWriter : IElementWriter
{
DateTime? CreationDate { get; set; }
}
public class Element:IElementWriter, IElementInsertWriter, IElementUpdateWriter {
public int ID { get; set; }
public string Name { get; set; }
public DateTime? CreationDate { get; set; }
public string FirstName { get; set; }
}
static void Main(string[] args)
{
IElementWriter element = new Element() { ID = 1, Name = "SourceName", CreationDate=DateTime.Today, FirstName="SourceFirstName" };
string json = "{ id:'8', Name:'newName', FirstName:'newFirstName' }";
JsonConvert.PopulateObject(json, element, new JsonSerializerSettings() {
});
Console.WriteLine(JsonConvert.SerializeObject(element));
Console.ReadLine();
}
Result:
{"ID":8,"Name":"newName","CreationDate":"2019-06-05T00:00:00+02:00","FirstName":"newFirstName"}
Required because IElementWriter does not have ID nor FirstName:
{"ID":1,"Name":"newName","CreationDate":"2019-06-05T00:00:00+02:00","FirstName":"SourceFirstName"}
There is no simple setting in JsonSerializerSettings that will cause JsonConvert.PopulateObject() to populate an instance of a derived type as if it were an instance of some base type. To confirm this, you can check the source for JsonSerializerInternalReader.Populate(), which takes as arguments only a reader and target and pulls the target's contract directly from its type:
public void Populate(JsonReader reader, object target)
{
Type objectType = target.GetType();
JsonContract contract = Serializer._contractResolver.ResolveContract(objectType);
Thus your options include:
Modify the definition of your Element class and add [JsonIgnore] to the properties you don't want to populate.
You probably don't want to do this because it will prevent the properties from ever being serialized or deserialized.
Use a custom contract resolver to ignore all properties of Element that are not also properties of IElementWriter.
This would seem to be the better solution.
Assuming you choose option #2, you can introduce the following custom contract resolver:
public class UpcastingContractResolver<TDerived, TBase> : DefaultContractResolver where TDerived: TBase
{
readonly HashSet<string> baseProperties = ((JsonObjectContract)JsonSerializer.Create().ContractResolver.ResolveContract(typeof(TBase)))
.Properties.Select(p => p.UnderlyingName)
.ToHashSet();
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, memberSerialization);
if (type == typeof(TDerived) || type.IsSubclassOf(typeof(TDerived)))
{
foreach (var property in properties)
{
if (!baseProperties.Contains(property.UnderlyingName))
property.Ignored = true;
}
}
return properties;
}
}
Then, cache an instance somewhere for performance as suggested by Newtonsoft:
static IContractResolver elementAsElementWriterResolver = new UpcastingContractResolver<Element, IElementWriter>();
And populate your element as follows:
// Only populate those properties present in IElementWriter
JsonConvert.PopulateObject(json, element, new JsonSerializerSettings
{
ContractResolver = elementAsElementWriterResolver
});
Demo fiddle here.

Creating a generic root object for JSON.NET? [duplicate]

Is there a way to change name of Data property during serialization, so I can reuse this class in my WEB Api.
For an example, if i am returning paged list of users, Data property should be serialized as "users", if i'm returning list of items, should be called "items", etc.
Is something like this possible:
public class PagedData
{
[JsonProperty(PropertyName = "Set from constructor")]??
public IEnumerable<T> Data { get; private set; }
public int Count { get; private set; }
public int CurrentPage { get; private set; }
public int Offset { get; private set; }
public int RowsPerPage { get; private set; }
public int? PreviousPage { get; private set; }
public int? NextPage { get; private set; }
}
EDIT:
I would like to have a control over this functionality, such as passing name to be used if possible. If my class is called UserDTO, I still want serialized property to be called Users, not UserDTOs.
Example
var usersPagedData = new PagedData("Users", params...);
You can do this with a custom ContractResolver. The resolver can look for a custom attribute which will signal that you want the name of the JSON property to be based on the class of the items in the enumerable. If the item class has another attribute on it specifying its plural name, that name will then be used for the enumerable property, otherwise the item class name itself will be pluralized and used as the enumerable property name. Below is the code you would need.
First let's define some custom attributes:
public class JsonPropertyNameBasedOnItemClassAttribute : Attribute
{
}
public class JsonPluralNameAttribute : Attribute
{
public string PluralName { get; set; }
public JsonPluralNameAttribute(string pluralName)
{
PluralName = pluralName;
}
}
And then the resolver:
public class CustomResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty prop = base.CreateProperty(member, memberSerialization);
if (prop.PropertyType.IsGenericType && member.GetCustomAttribute<JsonPropertyNameBasedOnItemClassAttribute>() != null)
{
Type itemType = prop.PropertyType.GetGenericArguments().First();
JsonPluralNameAttribute att = itemType.GetCustomAttribute<JsonPluralNameAttribute>();
prop.PropertyName = att != null ? att.PluralName : Pluralize(itemType.Name);
}
return prop;
}
protected string Pluralize(string name)
{
if (name.EndsWith("y") && !name.EndsWith("ay") && !name.EndsWith("ey") && !name.EndsWith("oy") && !name.EndsWith("uy"))
return name.Substring(0, name.Length - 1) + "ies";
if (name.EndsWith("s"))
return name + "es";
return name + "s";
}
}
Now you can decorate the variably-named property in your PagedData<T> class with the [JsonPropertyNameBasedOnItemClass] attribute:
public class PagedData<T>
{
[JsonPropertyNameBasedOnItemClass]
public IEnumerable<T> Data { get; private set; }
...
}
And decorate your DTO classes with the [JsonPluralName] attribute:
[JsonPluralName("Users")]
public class UserDTO
{
...
}
[JsonPluralName("Items")]
public class ItemDTO
{
...
}
Finally, to serialize, create an instance of JsonSerializerSettings, set the ContractResolver property, and pass the settings to JsonConvert.SerializeObject like so:
JsonSerializerSettings settings = new JsonSerializerSettings
{
ContractResolver = new CustomResolver()
};
string json = JsonConvert.SerializeObject(pagedData, settings);
Fiddle: https://dotnetfiddle.net/GqKBnx
If you're using Web API (looks like you are), then you can install the custom resolver into the pipeline via the Register method of the WebApiConfig class (in the App_Start folder).
JsonSerializerSettings settings = config.Formatters.JsonFormatter.SerializerSettings;
settings.ContractResolver = new CustomResolver();
Another Approach
Another possible approach uses a custom JsonConverter to handle the serialization of the PagedData class specifically instead using the more general "resolver + attributes" approach presented above. The converter approach requires that there be another property on your PagedData class which specifies the JSON name to use for the enumerable Data property. You could either pass this name in the PagedData constructor or set it separately, as long as you do it before serialization time. The converter will look for that name and use it when writing out JSON for the enumerable property.
Here is the code for the converter:
public class PagedDataConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(PagedData<>);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Type type = value.GetType();
var bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
string dataPropertyName = (string)type.GetProperty("DataPropertyName", bindingFlags).GetValue(value);
if (string.IsNullOrEmpty(dataPropertyName))
{
dataPropertyName = "Data";
}
JObject jo = new JObject();
jo.Add(dataPropertyName, JArray.FromObject(type.GetProperty("Data").GetValue(value)));
foreach (PropertyInfo prop in type.GetProperties().Where(p => !p.Name.StartsWith("Data")))
{
jo.Add(prop.Name, new JValue(prop.GetValue(value)));
}
jo.WriteTo(writer);
}
public override bool CanRead
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use this converter, first add a string property called DataPropertyName to your PagedData class (it can be private if you like), then add a [JsonConverter] attribute to the class to tie it to the converter:
[JsonConverter(typeof(PagedDataConverter))]
public class PagedData<T>
{
private string DataPropertyName { get; set; }
public IEnumerable<T> Data { get; private set; }
...
}
And that's it. As long as you've set the DataPropertyName property, it will be picked up by the converter on serialization.
Fiddle: https://dotnetfiddle.net/8E8fEE
UPD Sep 2020: #RyanHarlich pointed that proposed solution doesn't work out of the box. I found that Newtonsoft.Json doesn't initialize getter-only properties in newer versions, but I'm pretty sure it did ATM I wrote this answer in 2016 (no proofs, sorry :).
A quick-n-dirty solution is to add public setters to all properties ( example in dotnetfiddle ). I encourage you to find a better solution that keeps read-only interface for data objects. I haven't used .Net for 3 years, so cannot give you that solution myself, sorry :/
Another option with no need to play with json formatters or use string replacements - only inheritance and overriding (still not very nice solution, imo):
public class MyUser { }
public class MyItem { }
// you cannot use it out of the box, because it's abstract,
// i.e. only for what's intended [=implemented].
public abstract class PaginatedData<T>
{
// abstract, so you don't forget to override it in ancestors
public abstract IEnumerable<T> Data { get; }
public int Count { get; }
public int CurrentPage { get; }
public int Offset { get; }
public int RowsPerPage { get; }
public int? PreviousPage { get; }
public int? NextPage { get; }
}
// you specify class explicitly
// name is clear,.. still not clearer than PaginatedData<MyUser> though
public sealed class PaginatedUsers : PaginatedData<MyUser>
{
// explicit mapping - more agile than implicit name convension
[JsonProperty("Users")]
public override IEnumerable<MyUser> Data { get; }
}
public sealed class PaginatedItems : PaginatedData<MyItem>
{
[JsonProperty("Items")]
public override IEnumerable<MyItem> Data { get; }
}
Here is a solution that doesn't require any change in the way you use the Json serializer. In fact, it should also work with other serializers. It uses the cool DynamicObject class.
The usage is just like you wanted:
var usersPagedData = new PagedData<User>("Users");
....
public class PagedData<T> : DynamicObject
{
private string _name;
public PagedData(string name)
{
if (name == null)
throw new ArgumentNullException(nameof(name));
_name = name;
}
public IEnumerable<T> Data { get; private set; }
public int Count { get; private set; }
public int CurrentPage { get; private set; }
public int Offset { get; private set; }
public int RowsPerPage { get; private set; }
public int? PreviousPage { get; private set; }
public int? NextPage { get; private set; }
public override IEnumerable<string> GetDynamicMemberNames()
{
yield return _name;
foreach (var prop in GetType().GetProperties().Where(p => p.CanRead && p.GetIndexParameters().Length == 0 && p.Name != nameof(Data)))
{
yield return prop.Name;
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (binder.Name == _name)
{
result = Data;
return true;
}
return base.TryGetMember(binder, out result);
}
}
The following is another solution tested in .NET Standard 2.
public class PagedResult<T> where T : class
{
[JsonPropertyNameBasedOnItemClassAttribute]
public List<T> Results { get; set; }
[JsonProperty("count")]
public long Count { get; set; }
[JsonProperty("total_count")]
public long TotalCount { get; set; }
[JsonProperty("current_page")]
public long CurrentPage { get; set; }
[JsonProperty("per_page")]
public long PerPage { get; set; }
[JsonProperty("pages")]
public long Pages { get; set; }
}
I am using Humanizer for pluralization.
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (member.GetCustomAttribute<JsonPropertyNameBasedOnItemClassAttribute>() != null)
{
Type[] arguments = property.DeclaringType.GenericTypeArguments;
if(arguments.Length > 0)
{
string name = arguments[0].Name.ToString();
property.PropertyName = name.ToLower().Pluralize();
}
return property;
}
return base.CreateProperty(member, memberSerialization);
}
There's a package called SerializationInterceptor. Here's the GitHub link: https://github.com/Dorin-Mocan/SerializationInterceptor/wiki. You can also install the package using Nuget Package Manager.
The example from below uses Syste.Text.Json for serialization. You can use any other serializer(except Newtonsoft.Json). For more info on why Newtonsoft.Json not allowed, please refer to GitHub documentation.
You can create an interceptor
public class JsonPropertyNameInterceptorAttribute : InterceptorAttribute
{
public JsonPropertyNameInterceptorAttribute(string interceptorId)
: base(interceptorId, typeof(JsonPropertyNameAttribute))
{
}
protected override void Intercept(in AttributeParams originalAttributeParams, object context)
{
string theNameYouWant;
switch (InterceptorId)
{
case "some id":
theNameYouWant = (string)context;
break;
default:
return;
}
originalAttributeParams.ConstructorArgs.First().ArgValue = theNameYouWant;
}
}
And put the interceptor on the Data prop
public class PagedData<T>
{
[JsonPropertyNameInterceptor("some id")]
[JsonPropertyName("during serialization this value will be replaced with the one passed in context")]
public IEnumerable<T> Data { get; private set; }
public int Count { get; private set; }
public int CurrentPage { get; private set; }
public int Offset { get; private set; }
public int RowsPerPage { get; private set; }
public int? PreviousPage { get; private set; }
public int? NextPage { get; private set; }
}
And then you can serialize the object like this
var serializedObj = InterceptSerialization(
obj,
objType,
(o, t) =>
{
return JsonSerializer.Serialize(o, t, new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.Preserve });
},
context: "the name you want");
Hope this will be of use to you.
have a look here:
How to rename JSON key
Its not done during serialization but with a string operation.
Not very nice (in my eyes) but at least a possibility.
Cheers Thomas

C# Web API :Marking a class serializable makes me unable to use it as an incoming request object [duplicate]

I have just updated my version of NewtonSoft JSON.NET from version 3.0.0 to 3.5.0 and I have noticed that protected members are not implicitly serialised.
I have the following class:
public class SimpleFileContainer : IDto
{
public virtual string Name { get; protected set; }
public virtual string Path { get; protected set; }
public SimpleFileContainer(string name, string path)
{
Name = name;
Path = path;
}
}
The following test code does not pass
var json = JsonConvert.SerializeObject(new SimpleFileContainer("Name", "Path"));
var deserialised = JsonConvert.DeserializeObject<SimpleFileContainer >(json);
Assert.That(deserialised.Name, Is.EqualTo("Name");
both the Name and Path properties are null unless I either make the property sets public or add update the class with the following attributes:
[JsonObject(MemberSerialization.OptOut)]
public class SimpleFileContainer : IDto
{
[JsonProperty]
public virtual string Name { get; protected set; }
[JsonProperty]
public virtual string Path { get; protected set; }
public SimpleFileContainer(string name, string path)
{
Name = name;
Path = path;
}
}
This is a reasonably sized project that uses the serialization process a lot, I do not want to go through the code adding these attributes to every class and member.
Is there a way round this?
I had this same problem today. Luckily Ayende had the fix and I am sharing it with you. When you configure the serializer, change the DefaultMembersSearchFlags property on the ContractResolver:
var serializer = new JsonSerializer
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ContractResolver = new DefaultContractResolver
{
DefaultMembersSearchFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
},
TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
};

XAML serialization of public get/private set properties

I'm using System.Xaml.XamlServices.Save method to serialize an object which has properties with public getters/private setters and by design these properties are ignored. I tried to implement advice of how to override default XAML bindings and get private properties serialized, but it doesn't work for some reason - those properties are still ignored. Could anyone point out what's wrong:
public class CustomXamlSchemaContext : XamlSchemaContext
{
protected override XamlType GetXamlType(string xamlNamespace, string name, params XamlType[] typeArguments)
{
var type = base.GetXamlType(xamlNamespace, name, typeArguments);
return new CustomXamlType(type.UnderlyingType, type.SchemaContext, type.Invoker);
}
}
public class CustomXamlType : XamlType
{
public CustomXamlType(Type underlyingType, XamlSchemaContext schemaContext, XamlTypeInvoker invoker) : base(underlyingType, schemaContext, invoker)
{
}
protected override bool LookupIsConstructible()
{
return true;
}
protected override XamlMember LookupMember(string name, bool skipReadOnlyCheck)
{
var member = base.LookupMember(name, skipReadOnlyCheck);
return new CustomXamlMember(member.Name, member.DeclaringType, member.IsAttachable);
}
}
public class CustomXamlMember : XamlMember
{
public CustomXamlMember(string name, XamlType declaringType, bool isAttachable) : base(name, declaringType, isAttachable)
{
}
protected override bool LookupIsReadOnly()
{
return false;
}
}
public static string Save(object instance)
{
var stringWriter1 = new StringWriter(CultureInfo.CurrentCulture);
var stringWriter2 = stringWriter1;
var settings = new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true };
using (var writer = XmlWriter.Create(stringWriter2, settings))
{
Save(writer, instance);
}
return stringWriter1.ToString();
}
public static void Save(XmlWriter writer, object instance)
{
if (writer == null)
throw new ArgumentNullException("writer");
using (var xamlXmlWriter = new XamlXmlWriter(writer, new CustomXamlSchemaContext()))
{
XamlServices.Save(xamlXmlWriter, instance);
}
}
Having above infrastructure code and a class
public class Class1
{
public string Property1 { get; private set; }
public string Property2 { get; set; }
public DateTime AddedProperty { get; set; }
}
and serializing an instance of this class with
var obj = new Class1 { Property1 = "value1", Property2 = "value2" };
var objString = Save(obj);
I get the result
<Class1 AddedProperty="0001-01-01" Property2="value2" xmlns="clr-namespace:TestNamespace;assembly=Tests" />
where there is no entry for Property1.
What's even more interesting, that none of the overloads are called during serialization.
It turned out couple tweaks to my initial code solves the problem. Here's final solution:
private class CustomXamlSchemaContext : XamlSchemaContext
{
public override XamlType GetXamlType(Type type)
{
var xamlType = base.GetXamlType(type);
return new CustomXamlType(xamlType.UnderlyingType, xamlType.SchemaContext, xamlType.Invoker);
}
}
private class CustomXamlType : XamlType
{
public CustomXamlType(Type underlyingType, XamlSchemaContext schemaContext, XamlTypeInvoker invoker)
: base(underlyingType, schemaContext, invoker)
{
}
protected override bool LookupIsConstructible()
{
return true;
}
protected override XamlMember LookupMember(string name, bool skipReadOnlyCheck)
{
var member = base.LookupMember(name, skipReadOnlyCheck);
return member == null ? null : new CustomXamlMember((PropertyInfo)member.UnderlyingMember, SchemaContext, member.Invoker);
}
protected override IEnumerable<XamlMember> LookupAllMembers()
{
foreach (var member in base.LookupAllMembers())
{
var value = new CustomXamlMember((PropertyInfo)member.UnderlyingMember, SchemaContext, member.Invoker);
yield return value;
}
}
protected override bool LookupIsPublic()
{
return true;
}
}
private class CustomXamlMember : XamlMember
{
public CustomXamlMember(PropertyInfo propertyInfo, XamlSchemaContext schemaContext, XamlMemberInvoker invoker)
: base(propertyInfo, schemaContext, invoker)
{
}
protected override bool LookupIsReadOnly()
{
return false;
}
protected override bool LookupIsWritePublic()
{
return true;
}
}
This customization allows to serialize/deserialize properties with public getter and public/internal/protected/private setters. It ignores all the other properties. It also serializes instances of internal classes.
The problem here is that you are attempting to write readonly and private properties.
According to the XAML standard the only readonly properties that are syntactically correct are for List, Dictionary and static members:
3.3.1.6. Only List, Dictionary, or Static Members may be Read-only
If neither [value type][is list] nor [value type][is dictionary], nor [is static] is True, [is read only] MUST be False.
Have a look here for MSDN syntax detail.
And the standard itself can be downloaded here.
You'll also note that only public properties have any relevance here (from msdn linked above):
In order to be set through attribute syntax, a property must be public and must be writeable. The value of the property in the backing type system must be a value type, or must be a reference type that can be instantiated or referenced by a XAML processor when accessing the relevant backing type.
For WPF XAML events, the event that is referenced as the attribute
name must be public and have a public delegate.
The property or event must be a member of the class or structure that
is instantiated by the containing object element.
and if you think about it you can see why.
The whole C# standard is really built around using classes that interact by using public properties and methods. By doing so other classes don't need to know what resides within a class beyond them. Each class can be treated as a black box where the public properties and methods are the class's interface to other code.
Here's an informative blog regarding XAML serialization.
Personally I would ask myself why I need to serialize/deserialize private member properties.
I'm not quite sure what is wrong with the above code, but if you like I can provide an alternative.
This is kind of a hacky way to do it, but it works (tested it). First, you can throw away all that custom XAML stuff. Then, just change your Class1 to be:
public class Class1
{
private string _Property1;
public string Property2 { get; set; }
public DateTime AddedProperty { get; set; }
public Class1()
{
}
public Class1(string prop1, string prop2)
{
_Property1 = prop1;
Property2 = prop2;
}
public string Property1
{
get { return _Property1; }
set { }
}
}
While the set accessor is accessible, it doesn't do anything so in effect this is the same as a public getter/private setter setup. Proper documentation will also help if someone else needs to use your Class1 and is wondering why the 'set' isn't working for Property1.
This could be a plan B in case no one posts a fix for your above code.
Update: If you need to deserialize the object as well, you could create another object that acts as a go-between for your Class1 and the serialization process. The whole setup would look like this:
public class Class1
{
public string Property1 { get; private set; }
public string Property2 { get; set; }
public DateTime AddedProperty { get; set; }
public Class1()
{
}
public Class1(string prop1, string prop2) : this()
{
Property1 = prop1;
Property2 = prop2;
}
public Class1(Class1DTO dto)
{
Property1 = dto.Property1;
}
public Class1DTO CreateDTO()
{
return new Class1DTO
{
AddedProperty = AddedProperty,
Property1 = Property1,
Property2 = Property2
};
}
}
public class Class1DTO
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public DateTime AddedProperty { get; set; }
}
The whole serialization/deserialization process would be like this:
var obj = new Class1("value1", "value2");
var dto = obj.CreateDTO();
var objString = Save(dto);
using (var stringReader = new StringReader(objString))
{
using (var reader = new XamlXmlReader(stringReader))
{
var deserializedDTO = XamlServices.Load(reader);
var originalObj = new Class1(dto);
}
}
You can then change access modifiers to fine tune the amount of access other people would have on your whole setup (you could create static Serialize/Deserialize methods on your Class1 type and push the Class1DTO type into a private nested class so people can't access it etc.).

Upgrade of NewtonSoft JSON.NET not implicitly serialising protected members

I have just updated my version of NewtonSoft JSON.NET from version 3.0.0 to 3.5.0 and I have noticed that protected members are not implicitly serialised.
I have the following class:
public class SimpleFileContainer : IDto
{
public virtual string Name { get; protected set; }
public virtual string Path { get; protected set; }
public SimpleFileContainer(string name, string path)
{
Name = name;
Path = path;
}
}
The following test code does not pass
var json = JsonConvert.SerializeObject(new SimpleFileContainer("Name", "Path"));
var deserialised = JsonConvert.DeserializeObject<SimpleFileContainer >(json);
Assert.That(deserialised.Name, Is.EqualTo("Name");
both the Name and Path properties are null unless I either make the property sets public or add update the class with the following attributes:
[JsonObject(MemberSerialization.OptOut)]
public class SimpleFileContainer : IDto
{
[JsonProperty]
public virtual string Name { get; protected set; }
[JsonProperty]
public virtual string Path { get; protected set; }
public SimpleFileContainer(string name, string path)
{
Name = name;
Path = path;
}
}
This is a reasonably sized project that uses the serialization process a lot, I do not want to go through the code adding these attributes to every class and member.
Is there a way round this?
I had this same problem today. Luckily Ayende had the fix and I am sharing it with you. When you configure the serializer, change the DefaultMembersSearchFlags property on the ContractResolver:
var serializer = new JsonSerializer
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ContractResolver = new DefaultContractResolver
{
DefaultMembersSearchFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
},
TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
};

Categories

Resources