I'm using Newtownsoft JSON.Net and want to add custom attributes, then have a JSONConverter that deals with these custom attributes. Currently the way to do this is [JsonConverter(typeof(CustomJsonConverter))]. But I don't want this converter to always be called, only when I pass it into JsonConvert.SerializeObject(...). This is because this class is being used by two different paths, one of which needs to modify the JSON based on the properties and the other doesn't.
In other words, I only want these attributes to be considered when I explicitly tell my code to consider them. They should be ignored when Newtonsoft is doing its default serialization. How can I achieve this?
Example:
class Foo {
[CustomAttributeToAddMyExtraProperty]
public int Bar;
[JsonProperty('default')]
public int Baz;
}
If I just use the default Newtonsoft JSON.Net serialize, I should get
{
"Bar":value1,
"default":value2
}
But if I pass in my custom converter explicitly, I should get this:
{
"Bar":value1,
"default":value2,
"MyExtraProperty":value3
}
Notice that the JSON.Net attributes are always used.
UPDATE
I have tried this:
namespace Project1
{
class Class1
{
static void Main()
{
Class2 foo = new Class2();
Console.WriteLine(JsonConvert.SerializeObject(foo, new JsonSerializerSettings() { ContractResolver = new BlahResolver() }));
Console.Read();
}
}
class Class2
{
[Blah]
public int one = 1;
[JsonProperty]
public int two = 2;
[Blah]
public string three = "3";
}
internal class BlahAttribute : Attribute
{
}
class BlahResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
var attr = property.AttributeProvider.GetAttributes(typeof(BlahAttribute), true);
if (attr.Count == 1)
{
property.Converter = new BlahConverter();
}
return property;
}
}
class BlahConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsValueType;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return existingValue;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken token = JToken.Parse("{ \"foo\":\"a\", \"bar\":34 }");
token.WriteTo(writer);
}
}
}
Output:
{"one":{"foo":"a","bar":34},"two":2,"three":{"foo":"a","bar":34}}
I'm able to successfully identify properties with my custom attribute and redirect them to my custom converter. The issue now is the converter is adding properties as if one is a complex type. I want to add the properties in the top level JSON, not as properties of one.
Output I want:
{"one":1, "foo":"a","bar":34,"two":2,"three":3,"foo":"a","bar":34}
I realize that this has multiple properties with the same name. I don't know if that's valid or not, but eventually the object value will be included in the name (ex. one_foo, one_bar), so you can disregard that.
With your update, you're very close to getting what you need to work for this scenario. At this point, your only issue is in the WriteJson method.
All you need to do at this point is simply:
writer.WritePropertyName("propertyName");
writer.WriteValue("propertyValue");
If you need to use a different serializer depending on the current execution path, you are correct that the attribute route will not work
Instead pass in the currently desired converter during the serialization process:
string json = JsonConvert.SerializeObject(employee, Formatting.Indented, new MyCustomConverter(typeof(MyType)));
Source
Related
Is it possible to somehow force serialization of x property?
public class Cake
{
[JsonIgnore]
// possibly some attribute like [ReadOnlyJsonIgnore] or [JsonIgnoreReadOnly] would be nice
public int x { get; set; }
}
But keep [JsonIgnore] for deserialization only? What I want to achieve is some form of read only properties. So they can be serialized but can't be deserialized, are simply ignored like they aren't there.
Okay, what I did to make this "force" work(a bit hackish):
I created custom JsonConverter that use custom IContractResolver that ignores [JsonIgnore] only for writing in WriteJson.
public class CustomJsonConverter : JsonConverter
{
...
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// somehow using serializer passed to this method doesn't seem to work
var token = JToken.FromObject(value, new JsonSerializer()
{
ContractResolver = new DontIgnoreContractResolver()
});
token.WriteTo(writer);
}
}
public class DontIgnoreContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var jsonProperty = base.CreateProperty(member, memberSerialization);
if (jsonProperty.Ignored)
{
jsonProperty.Ignored = false;
}
return jsonProperty;
}
}
but for ReadJson it uses default contract resolver and ignores the property. But yeah, that's possible.
I have a specific situation where I need to include the class name as property in JSON when classes are serialized. The tricky parts is I need to do this dynamically. I can't just create an anonymous class before calling serialization.
I have decorated my class with a custom attribute as below:
[OntologyArea("con", " http://someurl/area/DomicileAddress")]
public class DomicileAddress : IContactPoint
{
[JsonProperty(PropertyName = "con:hasAddressPoint")]
public IAddressPoint AddressPoint
{
get; set;
}
}
In the above example the OntologyArea attribute should be read and included as a Property. The propery name should be the first argument of OntologyArea + the class name (i.e con:DomicileAddress) and the value should be the concrete class of IAddressPoint.
The tricky part is that the concrete class of IAddressPoint might need to do the same as shown here:
[OntologyArea("geo", "http://someurl.net/geolocation")]
public class StreetAddress : IAddressPoint
{
[JsonProperty("geo:hasStartingStreetNumber")]
public string StartingStreetNumber
{
get; set;
}
}
an example of JSON:
"con:DomicileAddress" : {
"con:hasAddressPoint" : {
"geo:StreetAddress" : {
"geo:hasEndingStreetNumber" : ""
}
}
}
So if any object does have a OntologyArea attribute I need to add a parent level. If it does not contain this attribute normal serilization should continue.
Please let me know If I need to explain more.
Is this solution for specifying the concrete type is a must or is it just your proposed solution?
Because Json.NET has a built-in support for encoding the actual types for properties of interface types or base classes:
var json = JsonConvert.SerializeObject(myContract, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
If you really need a completely custom logic you have to implement a converter, which also can be passed to the JsonSerializerSettings. It must be derived from JsonConverter and you have to implement the WriteJson method to emit your desired json sting using low level tokens just like in case of an XmlWriter:
private class MyConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(IAddressPoint).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var address = (IAddressPoint)value;
writer.WriteStartObject(); // {
writer.WritePropertyName($"geo:{address.GetType().Name}"); // "geo:StreetAddress"
// ... etc.
writer.WriteEndObject(); // }
// or you can just emit raw string:
writer.WriteRaw(myJsonBody);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
{
// todo: and the deserialization part goes here
}
}
This pointed me in the correct direction. I have an attribute that is read in this class and appended to the writer object.
I have a collection of name / value pairs where they are defined with the words name and value just like a Key/Value object, i.e.
[{"Name":"ActivityId","DataType":1,"Value":"a7868f8c-07ac-488d-a414-714527c2e76f"},
{"Name":"Address1","DataType":2,"Value":"123 Main St"}]
If I had an object like:
class Request
{
public Guid ActivityId { get; set; }
public string Address1 {get; set; }
}
How can I deserialize this to the class above?
Should I consider a custom converter? Does Json.NET have something built-in? Is there a way to decorate the properties with an attribute that I'm missing? Would it be easier to customize the serialization?
I'm trying to avoid pulling the data for each property from a Dictionary, which would be the easy route, but would require me to do this with each custom implementation. I would prefer to do this in a base class in a single method using Json.NET (or something in the .NET framework).
I've searched quite a bit, and most examples are real name/value pairs, not prefixed with name and value, i.e.
[{"ActivityId":"a7868f8c-07ac-488d-a414-714527c2e76f"}]
Any ideas?
This can be done in a straightforward manner with a custom JsonConverter like the one below. The converter works by first transforming the array of name-value pairs into a JObject with properties mirroring the pairs, then populating the target object from the JObject using the serializer's built-in Populate method.
class NameValueConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Load the array of name-value pairs and transform into a JObject.
// We are assuming all the names will be distinct.
JObject obj = new JObject(
JArray.Load(reader)
.Children<JObject>()
.Select(jo => new JProperty((string)jo["Name"], jo["Value"]))
);
// Instantiate the target object and populate it from the JObject.
object result = Activator.CreateInstance(objectType);
serializer.Populate(obj.CreateReader(), result);
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// WriteJson is not called when CanWrite returns false
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
// We only want this converter to handle classes that are expressly
// marked with a [JsonConverter] attribute, so return false here.
// (CanConvert is not called when [JsonConverter] attribute is used.)
return false;
}
}
To use the converter, just add a [JsonConverter] attribute to the target class:
[JsonConverter(typeof(NameValueConverter))]
class Request
{
public Guid ActivityId { get; set; }
public string Address1 {get; set; }
}
Then, you can deserialize as you normally would:
Request req = JsonConvert.DeserializeObject<Request>(json);
Fiddle: https://dotnetfiddle.net/tAp1Py
Let's say I've the following dynamic object:
public class SomeDynamicObject : DynamicObject
{
public string Text { get; set; }
}
If I serialize it using JsonConvert.SerializeObject(new SomeDynamicObject { Text = "hello world" }) it'll return {} instead of { "Text": "hello world" }.
I suspect the issue is that JSON.NET thinks it's a full dynamic object while my case is a dynamic object with declared members.
Is there any serialization settings or built-in converter that could be configured so JSON.NET can serialize both kinds of members?
To avoid confusion
Actual use case: I don't know which will be the types being serialized but I need to cover the whole use case of serializing declared properties of a dynamic object.
That is, I can't use attributes. That's why I'm asking if there's some converter or a serialization setting that can generalize this use case.
Update for non-attribute converter
Since you can't decorate, you lose a lot of power. Once the JsonWriter has converted to a JObject, the dynamic properties appear to be lost.
However, you can always use a little reflection in a custom converter's WriteJson method to serialize non-dynamic types.
public class SomeDynamicObject : DynamicObject
{
public string Text { get; set; }
public DynamicObject DynamicProperty { get; set; }
}
public class CustomDynamicConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
JObject jObject = JObject.Load(reader);
var target = Activator.CreateInstance(objectType);
//Create a new reader for this jObject, and set all properties to match the original reader.
JsonReader jObjectReader = jObject.CreateReader();
jObjectReader.Culture = reader.Culture;
jObjectReader.DateParseHandling = reader.DateParseHandling;
jObjectReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
jObjectReader.FloatParseHandling = reader.FloatParseHandling;
// Populate the object properties
serializer.Populate(jObjectReader, target);
return target;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var properties = value.GetType().GetProperties().Where(x => x.PropertyType != typeof(DynamicObject)).ToList();
JObject o = (JObject)JToken.FromObject(value);
properties.ForEach(x =>
{
o.AddFirst(new JProperty(x.Name, x.GetValue(value)));
});
o.WriteTo(writer);
}
}
If you explicitly decorate your properties with [JsonProperty], the serializer will pick them up, even if the containing type is dynamic.
public class SomeDynamicObject : DynamicObject
{
[JsonProperty]
public string Text { get; set; }
}
when serialized correctly outputs:
{"Text":"hello world"}
I have created several classes that map to Schema.org objects;
public class Thing {
public virtual string FullSchemaType { get { return "[schema.org]Thing"; } }
}
public class CreativeWork : Thing {
public override string FullSchemaType {get {return string.Join(":", base.FullSchemaType, "CreativeWork"); } }
[JsonProperty("author")]
public string Author { get;set; }
// etc
}
public class MediaObject : CreativeWork {
public override string FullSchemaType {get {return string.Join(":", base.FullSchemaType, "MediaObject"); } }
[JsonProperty("duration")]
public float? Duration { get;set; }
}
I have a factory class that creates e.g. a MediaObject, sets its properties. The FullSchemaType property is a Schema.org compliant way of noting its type. I am putting these objects into a database, serialising them using Json.NET 6.0.3.
I want to deserialize them into the correct C# objects. The standard Json.Net way to do this is to use TypeNameHandling - but that inserts a $type property into the serialised Json, which isn't ideal as we have several different applications interfacing with this database.
Is there a way to tell Json.NET to look at my FullSchemaType property for type binding information?
You can use JsonSerializerSettings to customise parameters of serialization: setting TypeNameHandling = TypeNameHandling.None allows you not to include $type information in serialized Json, and if you have any further problems with, say, customised converters you can use JsonSerializerSettings.IList<JsonConverter> settings to specify ones.
More detailed info is here: JsonSerializerSettings
and here: Newtonsoft.Json.TypeNameHandling
And here is a good example of JsonCreationConverter, you can overrride method ReadJson in your own converter, based on JsonConverter like this:
public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
// Load JObject from stream
JObject jObject = JObject.Load(reader);
if (jObject[FullSchemaType] == {your_media_object_type_name})
MediaObject target = Create(objectType, jObject);
else if (jObject[FullSchemaType] == {your_creative_work_type_name})
CreativeWork target = Create(objectType, jObject);
else
Thing target = Create(objectType, jObject);
// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);
return target;
}