How to Change Property Names When Serializing With Json.Net

How can I change property names when serializing with Json.net?

You could decorate the property you wish controlling its name with the [JsonProperty] attribute which allows you to specify a different name:

using Newtonsoft.Json;
// ...

[JsonProperty(PropertyName = "FooBar")]
public string Foo { get; set; }

Documentation: Serialization Attributes

Change json property name in output using Json.Net

You could use custom resolve to create desired behaviour:

public class CustomContractResolver : DefaultContractResolver
{
public bool UseJsonPropertyName { get; }

public CustomContractResolver(bool useJsonPropertyName)
{
UseJsonPropertyName = useJsonPropertyName;
}

protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (!UseJsonPropertyName)
property.PropertyName = property.UnderlyingName;

return property;
}
}

public class ErrorDetails
{
public int Id { get; set; }
[JsonProperty("error_message")]
public string ErrorMessage { get; set; }
}


var json = "{'Id': 1,'error_message': 'An error has occurred!'}";
var serializerSettings = new JsonSerializerSettings()
{
ContractResolver = new CustomContractResolver(false)
};
var dezerializerSettings = new JsonSerializerSettings
{
ContractResolver = new CustomContractResolver(true)
};

var obj = JsonConvert.DeserializeObject<ErrorDetails>(json, dezerializerSettings);
var jsonNew = JsonConvert.SerializeObject(obj, serializerSettings);

Kudos to NtFreX and his answer. I'm providing mine so that you can indicate that your question is resolved.

How to change property names depending on the type when serializing with Json.net?

There is no built-in way to dynamically change the name of a property based on its runtime type, but you could make a custom JsonConverter coupled with a custom Attribute class to do what you want. The converter would need to be made to operate at the class level in order to be able to control the names of the properties written to JSON. It could iterate through the properties of the target class using reflection, and check if any property declared as object has the custom attribute applied. If it does and the object's runtime type matches the type specified in the attribute, then use the property name from the attribute, otherwise just use the original property name.

Here's what the custom attribute would look like:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
class JsonPropertyNameByTypeAttribute : Attribute
{
public string PropertyName { get; set; }
public Type ObjectType { get; set; }

public JsonPropertyNameByTypeAttribute(string propertyName, Type objectType)
{
PropertyName = propertyName;
ObjectType = objectType;
}
}

And here is the code for the converter:

public class DynamicPropertyNameConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Type type = value.GetType();
JObject jo = new JObject();

foreach (PropertyInfo prop in type.GetProperties().Where(p => p.CanRead))
{
string propName = prop.Name;
object propValue = prop.GetValue(value, null);
JToken token = (propValue != null) ? JToken.FromObject(propValue, serializer) : JValue.CreateNull();

if (propValue != null && prop.PropertyType == typeof(object))
{
JsonPropertyNameByTypeAttribute att = prop.GetCustomAttributes<JsonPropertyNameByTypeAttribute>()
.FirstOrDefault(a => a.ObjectType.IsAssignableFrom(propValue.GetType()));

if (att != null)
propName = att.PropertyName;
}

jo.Add(propName, token);
}

jo.WriteTo(writer);
}

public override bool CanRead
{
get { return false; }
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// ReadJson is not called if CanRead returns false.
throw new NotImplementedException();
}

public override bool CanConvert(Type objectType)
{
// CanConvert is not called if a [JsonConverter] attribute is used
return false;
}
}

To use the converter, first add a [JsonConverter] attribute to the target class containing the property (or properties) that you want to be dynamically named. Then, add the custom attribute to the target property (or properties) in that class. You can add as many of the attribute as needed to cover the range of types you are expecting.

For example:

[JsonConverter(typeof(DynamicPropertyNameConverter))]
class Foo
{
public int Id { get; set; }
public string Name { get; set; }

[JsonPropertyNameByType("Vehicle", typeof(Vehicle))]
[JsonPropertyNameByType("Profile", typeof(Profile))]
public object Item { get; set; }
}

Then, serialize as you normally would:

string json = JsonConvert.SerializeObject(foo, Formatting.Indented);

Here is a working demo: https://dotnetfiddle.net/75HwrV

Json.net on deserialize change property type and name

Your basic problem is that your CustomContractResolver only changes the PropertyName and PropertyType of the returned JsonProperty, however the underlying PropertyInfo from which it was created is still that of the public surrogate property, not the private internal "real" property. Thus the ValueProvider, among other things, will still be wrong.

What you need to do instead is to generate a JsonProperty for the internal property, correct its name and accessibility, and return it in place of the JsonProperty for the public property. This will ensure that the serializer will serialize and deserialize the internal property instead of its public surrogate.

The following contract resolver does the job:

public class CustomContractResolver : DefaultContractResolver
{
const string InternalSuffix = "Internal";

protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = member as PropertyInfo;
var jProperty = base.CreateProperty(member, memberSerialization);
if (property != null && jProperty != null && memberSerialization != MemberSerialization.Fields && !jProperty.HasMemberAttribute)
{
var replacementName = jProperty.UnderlyingName + InternalSuffix;

// Check for replacement property.
var replacementProperty = jProperty.DeclaringType.GetProperty(replacementName, BindingFlags.Instance | BindingFlags.NonPublic);
if (replacementProperty != null
&& replacementProperty.GetGetMethod(true) != null && replacementProperty.GetSetMethod(true) != null
&& ReplacementTypeCompatible(property, replacementProperty.PropertyType)
)
{
var replacementJProperty = base.CreateProperty(replacementProperty, memberSerialization);
replacementJProperty.PropertyName = jProperty.PropertyName;
if (!replacementJProperty.Readable && replacementProperty.GetGetMethod(true) != null)
replacementJProperty.Readable = true;
if (!replacementJProperty.Writable && replacementProperty.GetSetMethod(true) != null)
replacementJProperty.Writable = true;
return replacementJProperty;
}

// Check for replacement field.
var replacementField = jProperty.DeclaringType.GetField(replacementName, BindingFlags.Instance | BindingFlags.NonPublic);
if (replacementField != null
&& ReplacementTypeCompatible(property, replacementField.FieldType)
)
{
var replacementJProperty = base.CreateProperty(replacementField, memberSerialization);
replacementJProperty.PropertyName = jProperty.PropertyName;
replacementJProperty.Readable = true;
replacementJProperty.Writable = true;
return replacementJProperty;
}
}

return jProperty;
}

static bool ReplacementTypeCompatible(PropertyInfo property, Type replacementType)
{
// Add here whatever restrictions you need
if (property.PropertyType.IsGenericType && typeof(IReadOnlyList<>).IsAssignableFrom(property.PropertyType.GetGenericTypeDefinition())
&& replacementType.IsGenericType && typeof(List<>).IsAssignableFrom(replacementType.GetGenericTypeDefinition())
&& replacementType.GetGenericArguments().SequenceEqual(property.PropertyType.GetGenericArguments()))
return true;
return false;
}
}

To use it, cache an instance of the resolver somewhere for performance:

static IContractResolver customContractResolver = new CustomContractResolver();

And deserialize like so:

var settings = new JsonSerializerSettings
{
ContractResolver = customContractResolver,
};
var root = JsonConvert.DeserializeObject<Nbgv>(json, settings);

Notes:

  • In your question, you state I need to map a json List to another field, however in the actual example the underlying member is a property. Thus in CreateProperty() I check for both types of replacement. If in your production code you only need one or the other, you can remove the unneeded logic.

  • The check !jProperty.HasMemberAttribute prevents properties explicitly marked with [JsonProperty] from being replaced. This seems correct but you can remove the check if you don't want it.

Demo fiddle here.

Changing property names for serializing

For Json.NET and DataContractJsonSerializer use DataMemberAttribute:

[DataMember(Name="PropertyB")]
T PropertyA { ... }

Make sure that your class is decorated with the [DataContract] attribute as well.

If you're using JavaScriptSerializer, you need to create derived implementation, as described here:
JavaScriptSerializer.Deserialize - how to change field names

Json.Net property name changed after serialization

You're close. Something like this works. I'll leave it as an excercise to genericize it :):

public class OninaigConverter : JsonConverter
{
private readonly Dictionary<string, string> _propertyMappings = new Dictionary<string, string>
{
{"BillingStreetA", "BillingStreet"},
{"BillingStreet1", "BillingStreet"}
};

public override bool CanWrite => false;

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}

public override bool CanConvert(Type objectType)
{
return objectType.GetTypeInfo().IsClass;
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object instance = Activator.CreateInstance(objectType);
var props = objectType.GetTypeInfo().DeclaredProperties.ToList();

JObject jo = JObject.Load(reader);
foreach (JProperty jp in jo.Properties())
{
if (!_propertyMappings.TryGetValue(jp.Name, out var name))
name = jp.Name;

PropertyInfo prop = props.FirstOrDefault(pi =>
pi.CanWrite && pi.GetCustomAttribute<JsonPropertyAttribute>().PropertyName == name);

prop?.SetValue(instance, jp.Value.ToObject(prop.PropertyType, serializer));
}

return instance;
}
}

Use different name for serializing and deserializing with Json.Net

You can make use of the JsonSerializerSettings, the ContractResolver and the NamingStrategy.

public class ErrorDetails
{
public int Id { get; set; }
public string ErrorMessage { get; set; }
}

var json = "{'Id': 1,'error_message': 'An error has occurred!'}";

For dezerialization you could use the SnakeCaseNamingStrategy.

var dezerializerSettings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new SnakeCaseNamingStrategy()
}
};
var obj = JsonConvert.DeserializeObject<ErrorDetails>(json, dezerializerSettings);

To serialize the object again you dont have to change the JsonSerializerSettings as the default will use the property name.

var jsonNew = JsonConvert.SerializeObject(obj);

jsonNew = "{'Id': 1,'ErrorMessage': 'An error has occurred!'}"


Or you could create a contract resolver which can decide which name to use. Then you can decide when you dezerialize and serialize if you want to use the pascal case name format or the one with the underscore.

public class CustomContractResolver : DefaultContractResolver
{
public bool UseJsonPropertyName { get; }

public CustomContractResolver(bool useJsonPropertyName)
{
UseJsonPropertyName = useJsonPropertyName;
}

protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (!UseJsonPropertyName)
property.PropertyName = property.UnderlyingName;

return property;
}
}

public class ErrorDetails
{
public int Id { get; set; }
[JsonProperty("error_message")]
public string ErrorMessage { get; set; }
}


var json = "{'Id': 1,'error_message': 'An error has occurred!'}";
var serializerSettings = new JsonSerializerSettings()
{
ContractResolver = new CustomContractResolver(false)
};
var dezerializerSettings = new JsonSerializerSettings
{
ContractResolver = new CustomContractResolver(true)
};

var obj = JsonConvert.DeserializeObject<ErrorDetails>(json, dezerializerSettings);
var jsonNew = JsonConvert.SerializeObject(obj, serializerSettings);

jsonNew = "{'Id': 1,'ErrorMessage': 'An error has occurred!'}"



Related Topics



Leave a reply



Submit