Custom Properties

Oct 12, 2009 at 6:47 AM

Hi,

I'm looking to add custom properties to an object at serialization time.

Specifically, I have objects annotated with [DataContract] and some other attributes and I want to serialize the values of these attributes along with the objects when converting them to json. I tried a custom contract resolver with:

protected override IList CreateProperties(JsonObjectContract contract) {
IList properties = base.CreateProperties(contract);
foreach (Attribute attribute in contract.UnderlyingType.GetCustomAttributes(true).Where(a => _typesToSerialize.Contains(a.GetType()))) {
properties.Add(new JsonProperty {
PropertyName = attribute.GetType().Name,
DefaultValue = attribute.ToJson(),
Readable = true,
Writable = false,
});
}
return properties;
}

_typesToSerialize is just an array of Types of Attributes that I want to serialize the values for (in this case DataContractAttribute and DataMemberAttribute).

but the serializer doesn't seem to like JsonProperties without Members (throws an exception). I also tried throwing in a "dummy" member info, but to no avail.

 

I'm guessing I need to implement a custom JsonConverter? It would serialize each object to a JObject, add the property with the attribute value, then continue the serialization... It seems kind of messy if I can avoid it.

 

Do you have a suggestion about how to implement this effectively? Thanks.

 

Coordinator
Oct 12, 2009 at 9:52 AM

I've been thinking about making it so that JsonProperties weren't tied to MemberInfos but instead had a "ValueStrategy" property which could have a MemberInfoStrategy assigned to it, or your own implementation but haven't done it yet. Maybe one day in the future.

I've used the JObject technique you mentioned on a project myself and it has worked well.

Oct 12, 2009 at 2:22 PM

Is there a good way you can think of to do this recursively? ie. do the extra property for the inner most object first?

The serializer does an out->in walk, but I want to visit each object in the graph and pick up the attirbute.

Thanks.

 

Oct 12, 2009 at 3:53 PM

Also, I'd like to still preserve references...

Oct 14, 2009 at 8:05 PM
Edited Oct 14, 2009 at 10:15 PM

Hi Again,

 

I think I have this working on the Serializing side, while preserving object references

			
			public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
				if (value != null) {
					DataContractAttribute attribute = value.GetType().GetCustomAttributes(typeof(DataContractAttribute), true).FirstOrDefault() as DataContractAttribute;
					if (attribute != null) {
						_skip = true;
						JObject o = JObject.FromObject(value, serializer);
						o.Add("DataContract", JObject.FromObject(attribute));
						writer.WriteRawValue(o.ToString());
					} else {
						serializer.Serialize(writer, value);
					}
				}
			}

 but I can't seem to get the hang of it on the deserialization. Any help would be much appreciated:

	public override object ReadJson(JsonReader reader, Type objectType, JsonSerializer serializer) {
			public override object ReadJson(JsonReader reader, Type objectType, JsonSerializer serializer) {
				_skip = true;
				object result = null;
				if (objectType == typeof(object)) {
					JObject jsonObject = JObject.ReadFrom(reader) as JObject;
					result = DeserializeDataContract(jsonObject);
				} else {
					result = Activator.CreateInstance(objectType);
					serializer.Populate(reader, result);
				}
				return result;
			}
			private object DeserializeDataContract(JObject jsonObject) {
				object result = null;
				JToken values = jsonObject["$values"];
				if (values != null && values.Type == JTokenType.Array) {
					List<object> items = new List<object>();
					foreach (JObject value in jsonObject["$values"] as JArray) {
						object item = DeserializeDataContract(value);
					}
					result = items;
				}

				if (jsonObject["DataContract"] != null) {
					DataContractAttribute attribute = JsonConvert.DeserializeObject<DataContractAttribute>(jsonObject["DataContract"].ToString());
					int namespaceIndex = Math.Max(attribute.Namespace.LastIndexOf("/") + 1, 0);
					string typeName = string.Format("{0}.{1}", attribute.Namespace.Substring(namespaceIndex), attribute.Name);
					Type objectType = Type.GetType(typeName);
					result = JsonConvert.DeserializeObject(jsonObject.ToString(), objectType);

				}
				return result;
			}
 Thanks.