Is there a short, more clean way to achieve this ?
public class Sidebar
{
[JsonProperty("0")]
public string Hurry = "hurry";
[JsonProperty("1")]
public string Dont_Spam_This_Button = "don't spam this button";
[JsonProperty("2")]
public string Navigation = "navigation";
[JsonProperty("3")]
public string Overview = "overview";
i want to objects to be numbered so is there a way to do that programatically instead of using attributes and manually counting ?
Yes, you can get the result you want using a custom JsonConverter like this one:
class NumberedPropertiesConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JObject jo = new JObject();
int count = 0;
foreach (MemberInfo member in value.GetType().GetMembers())
{
object memberVal = null;
if (member.MemberType == MemberTypes.Field)
{
memberVal = ((FieldInfo)member).GetValue(value);
}
else if (member.MemberType == MemberTypes.Property)
{
memberVal = ((PropertyInfo)member).GetValue(value);
}
else
{
continue;
}
JToken token = memberVal != null ? JToken.FromObject(memberVal, serializer) : null;
jo.Add(count.ToString(), token);
count++;
}
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 the converter, just mark your class with a [JsonConverter] attribute specifying the type of the custom converter:
[JsonConverter(typeof(NumberedPropertiesConverter))]
public class Sidebar
{
...
}
One important note: the properties/fields will be numbered according to the order returned by the Type.GetMembers() method. Generally, this will match the order declared in the class; however, if you have a mix of public properties and public fields, then all of the properties will be returned before all of the fields.
Here is a demo:
public class Program
{
public static void Main()
{
Sidebar sb = new Sidebar();
string json = JsonConvert.SerializeObject(sb, Formatting.Indented);
Console.WriteLine(json);
}
}
[JsonConverter(typeof(NumberedPropertiesConverter))]
public class Sidebar
{
public string Foo { get { return "foo property"; } }
public string Hurry = "hurry";
public string Dont_Spam_This_Button = "don't spam this button";
public string Navigation = "navigation";
public string Overview = "overview";
public string Bar { get { return "bar property"; } }
}
Output:
{
"0": "foo property",
"1": "bar property",
"2": "hurry",
"3": "don't spam this button",
"4": "navigation",
"5": "overview"
}
Fiddle: https://dotnetfiddle.net/aZ51qv
What you are doing will give you something like {"0":"hurry",...,"3":"overview"} when what you want is more like ["hurry",...,"overview"]
What you can do, is create a custom converter/serializer, or if you don't need automatic serialization/deserialization in .net, just serialize an object[] array in a class method...
public string AsJson()
{
return JsonConvert.SerializeObject(new object[] {
Hurry, Dont_Spam_This_Button, Navigation, Overview
});
}
Related
This question already has an answer here:
json deserialization to C# with dynamic keys [duplicate]
(1 answer)
Closed 7 months ago.
Using System.Text.Json, how can I serialize (and deserialize) an array of n elements (classes), to an object of n children, where the element name is one of the properties of the object?
For example, by default the following classes
public class Project
{
public string Name {get;set;}
public Environment[] Environments {get;set;}
}
public class Environment
{
public string Name {get;set;}
public string Region {get;set;}
public Settings Settings {get;set;}
}
public class Settings
{
public bool OverrideOnFetch {get;set;}
}
will be serialized as
{
"name": "MyProject",
"environments": [
{
"name": "dev",
"region": "us1",
"settings": {
"overrideOnFetch": false
}
},
{
"name": "prod",
"region": "us1",
"settings": {
"overrideOnFetch": false
}
}
]
}
But I want to change this behavior and serialize it as
{
"name": "MyProject",
"environments": {
"dev": {
"region": "us1",
"settings": {
"overrideOnFetch": false
}
},
"prod": {
"region": "us1",
"settings": {
"overrideOnFetch": false
}
}
}
}
without changing the classes (or creating another). As you can see, the Environment.Name acts like the property of environments in JSON.
I have no idea where to continue from here
class ProjectJsonConverter : JsonConverter<Project>
{
public override Project? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, Project value, JsonSerializerOptions options)
{
var namePolicy = options.PropertyNamingPolicy ?? new DefaultJsonNamingPolicy();
writer.WriteStartObject(); //starts the "project" object
writer.WriteString(JsonEncodedText.Encode(namePolicy.ConvertName(nameof(value.Name))), value.Name);
//How to write the "value.Settings" without rewriting everything?
writer.WriteStartObject(); //starts the "environment" object
foreach(var env in value.Environments)
{
}
writer.WriteEndObject(); //ends the "environment" object
writer.WriteEndObject(); //ends the "project" object
}
class DefaultJsonNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
return name;
}
}
}
A much simpler solution than manually writing the JSON array, is to serialize and deserialize to/from a Dictionary within your converter. Then simply convert to/from a list or array type.
A generic version of this would be:
public class PropertyToObjectListConverter<T> : JsonConverter<ICollection<T>>
{
static PropertyInfo _nameProp =
typeof(T).GetProperties().FirstOrDefault(p => p.GetCustomAttributes(typeof(JsonPropertyToObjectAttribute)).Any())
?? typeof(T).GetProperty("Name");
static Func<T, string> _getter = _nameProp?.GetMethod?.CreateDelegate<Func<T, string>>() ?? throw new Exception("No getter available");
static Action<T, string> _setter = _nameProp?.SetMethod?.CreateDelegate<Action<T, string>>() ?? throw new Exception("No setter available");
public override bool CanConvert(Type typeToConvert) =>
typeof(ICollection<T>).IsAssignableFrom(typeToConvert);
public override ICollection<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var dict = JsonSerializer.Deserialize<Dictionary<string, T>>(ref reader, options);
var coll = (ICollection<T>) Activator.CreateInstance(typeToConvert);
foreach (var kvp in dict)
{
var value = kvp.Value;
_setter(value, kvp.Key);
coll.Add(value);
}
return coll;
}
public override void Write(Utf8JsonWriter writer, ICollection<T> value, JsonSerializerOptions options)
{
var dict = value.ToDictionary(_getter);
JsonSerializer.Serialize(writer, dict, options);
}
}
[AttributeUsage(AttributeTargets.Property)]
public class JsonPropertyToObjectAttribute : Attribute
{
}
It looks first for a property with the JsonPropertyToObject attribute, otherwise it tries to find a property called Name. It must be a string type.
Note the use of static getter and setter functions to make the reflection fast, this only works on properties, not fields.
You would use it like this:
public class Project
{
public string Name {get;set;}
[JsonConverter(typeof(PropertyToObjectListConverter<Environment>))]
public Environment[] Environments {get;set;}
}
public class Environment
{
[JsonIgnore]
public string Name {get;set;}
public string Region {get;set;}
public Settings Settings {get;set;}
}
dotnetfiddle
How to serialize a class with Newtonsoft.Json.JsonConvert.SerializeObject to change empty List to null variable
my result from serialization
{"images":[]} or {"images":{}}
and would like to get
{"images":null}
my simple class
public class Images
{
[JsonProperty("images", Required = Required.Always, NullValueHandling = NullValueHandling.Include)]
public Images()
{
Parameters = new Parameters(); **// I know I could not initiate here only when I need it but I don't want to do it**
}
[JsonProperty("parameters", Required = Required.Always, NullValueHandling = NullValueHandling.Include)]
public Parameters Parameters { get; set; }
}
public class Parameters
{
[JsonProperty("id", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Include)]
public string Id { get; set; }
}
I did something like that
internal class NullEmptyConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(GuidId);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
return value;
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (JsonConvert.SerializeObject(untypedValue, Formatting.None).ToString() == "[]")
{
serializer.Serialize(writer, null);
return;
}
if (JsonConvert.SerializeObject(untypedValue, Formatting.None).ToString() == "{}")
{
serializer.Serialize(writer, null);
return;
}
serializer.Serialize(writer,untypedValue);
return;
}
}
use
[JsonConverter (typeof (NullEmptyConverter))]
immediately before
public Parameters Parameters { get; set; }
It works, but maybe it can be done better, simpler or somewhere else something is wrong here?
I've been struggling with this problem for a few days now and was hoping someone could help me out. I've got an ember front end which communicates with an API written in C#.
I'm making a an update (PUT) request but the model comes through with all the properties as null. The reason for this is because the API expects
{
"Type":1,
"Name":"Hello World"
}
but ember sends it in the format of
{
"Object":
{
"Type":1,
"Name":"Hello World"
}
}
I've seen solutions where you can add a custom JsonConverter onto the API Model, but this has to have it's own converter for each model. I'd like to know if someone help me with a generic one that I can apply to any model.
Here's an example of one that is specific for the profile model, I can't seem to get it to work as a generic though.
[JsonConverter(typeof(ProfileConverter))]
public class Profile
{
public string LoginId { get; set; }
public bool NewProfileOptIn { get; set; }
}
and the converter
public class ProfileConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Profile).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
Log.Information("Reading Json from ProfileSetting");
JToken token = JToken.Load(reader);
if (token["ProfileSetting"] != null)
{
JObject inner = token["ProfileSetting"].Value<JObject>();
if (CheckJsonProperty(inner, "login_id") && CheckJsonProperty(inner, "new_profile_opt_in"))
{
Profile model = new Profile
{
LoginId = inner["login_id"].ToString(),
NewProfileOptIn = (bool)inner["new_profile_opt_in"]
};
return model;
}
return null;
}
Log.Error("Invalid Model Name Passed - Expected 'ProfileSetting'");
return null;
}
private bool CheckJsonProperty(JObject objectToCheck, string expectedJsonProperty)
{
if (objectToCheck[expectedJsonProperty] != null)
{
return true;
}
Log.Error("Invalid or Missing Model Property Name Passed - Expected '" + expectedJsonProperty + "'");
return false;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
If any one has any reading resources or examples that would help, it would be much appreciated.
Thanks,
VB.Net
This Example has been coded with Json.Net.
So I'm working with Vb.net but this should work in C# the same way.
I've been able to do it without an JsonConverter as follows:
Public Class TestController
Inherits ApiController
<HttpGet>
Public Function Index() As String
Dim o As New JObject
o.Add("Data", 10)
Dim j As New JObject
j.Add("Object", o)
Dim obj = j.ToObject(Of EmberDataObject(Of TestObject))()
Return "200 - OK"
End Function
End Class
Public Class EmberDataObject(Of T)
<JsonProperty("Object")>
Public Property DataObject As T
End Class
Public Class TestObject
Public Property Data As Integer
End Class
In the above example the JsonObject with this mark up
{
"Object": {
"Data": 10
}
}
can be transformed easily. You can then access the nested Type as obj.DataObject. Through the set JsonProperty Attribute you can omit the Object key word which at least in Vb is not a legal Property name.
C#
The C# translation would be like this:
public class EmberDataObject<T>
{
[JsonProperty("Object")
public T DataObject {get; set;}
}
public class TestData
{
public int Data {get; set;}
}
And then referencing this from your Controller as follows:
public class MyApiController : ApiController
{
public IActionResult Post(EmberDataObject<TestData> emberObject)
{
/// Inner Object Available as emberObject.Object
}
}
I am trying to create a custom Json converter that has no default constructor and instead takes a factory that is dependency injected by Autofac. When ever I hit the object that uses this converter I get an exception that there is no no-arg constructor to use for the deserialization.
I have an objects and primitives. One of the objects is an abstract base object that I have the converter on. Since this converter is abstract I want to dependency inject a factory into the converter's ReadJson method to make the choice as to what conversion to make.
Currently the code is something like the following:
using System;
using System.Collections.Generic;
using Autofac;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using Newtonsoft = Newtonsoft.Json;
public class JsonModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<SubThingFactory>()
.As<IFactory>()
.SingleInstance();
builder.Register(c => this.CreateJsonSerializerSettings(c)).SingleInstance();
builder.RegisterType<CamelCasePropertyNamesContractResolver>()
.As<IContractResolver>()
.SingleInstance();
builder.RegisterType<IsoDateTimeConverter>()
.As<Newtonsoft.JsonConverter>()
.SingleInstance();
builder.RegisterType<SubThingConverter>()
.As<Newtonsoft.JsonConverter>()
.SingleInstance();
builder.Register(c => new StringEnumConverter
{
CamelCaseText = true
})
.As<Newtonsoft.JsonConverter>()
.SingleInstance();
}
private Newtonsoft.JsonSerializerSettings CreateJsonSerializerSettings(IComponentContext context)
{
var settings = new Newtonsoft.JsonSerializerSettings
{
DefaultValueHandling = Newtonsoft.DefaultValueHandling.Ignore,
NullValueHandling = Newtonsoft.NullValueHandling.Ignore,
DateTimeZoneHandling = Newtonsoft.DateTimeZoneHandling.Utc
};
settings.ContractResolver = context.Resolve<IContractResolver>();
foreach (var converter in context.Resolve<IEnumerable<Newtonsoft.JsonConverter>>())
{
settings.Converters.Add(converter);
}
return settings;
}
}
public class ThingBeingDeserialized
{
private string Name;
private SubThing subby;
}
[Newtonsoft.JsonConverterAttribute(typeof(SubThingConverter))]
public abstract class SubThing
{
public string Name { get; set; }
public virtual string GetName()
{
//Uses reflection to get the name from a custom attribute
return this.Name;
}
}
[CustomName("A")]
public class SubThingA : SubThing
{
public int Field1 { get; set; }
}
[CustomName("B")]
public class SubThingB : SubThing
{
public string Field2 { get; set; }
}
public class SubThingConverter : Newtonsoft.JsonConverter
{
//This is Autofac injected in
private readonly IFactory factory;
public SubThingConverter(IFactory factory)
{
this.factory = factory;
}
public override object ReadJson(Newtonsoft.JsonReader reader, Type objectType, object existingValue, Newtonsoft.JsonSerializer serializer)
{
if (reader.TokenType == Newtonsoft.JsonToken.Null)
{
return null;
}
var jsonObject = JObject.Load(reader);
var type = jsonObject["type"].ToString();
return this.factory.GetSubThing(type, jsonObject);
}
public override void WriteJson(Newtonsoft.JsonWriter writer, object value, Newtonsoft.JsonSerializer serializer)
{
var type = value.GetType();
var properties = type.GetProperties();
var jObject = new JObject
{
{ "type", type.Name }
};
foreach (var prop in properties)
{
if (prop.CanRead)
{
var propVal = prop.GetValue(value, null);
if (propVal != null)
{
jObject.Add(prop.Name, JToken.FromObject(propVal, serializer));
}
}
}
jObject.WriteTo(writer);
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(SubThing);
}
}
public interface IFactory
{
SubThing GetSubThing(string type, JObject restOfObj);
}
public class SubThingFactory : IFactory
{
public SubThing GetSubThing(string type, JObject restOfObj)
{
switch (type)
{
case "A":
return new SubThingA
{
Field1 = (int)(restOfObj["Field1"])
};
case "B":
return new SubThingB
{
Field2 = (string)(restOfObj["Field2"])
};
}
return null;
}
}
public class CustomNameAttribute : Attribute
{
public CustomNameAttribute(string name)
{
this.Name = name;
}
public string Name { get; set; }
}
The way I am doing the Autofac injection for the JsonSerializerSettings is by registering the settings such that the settings.Converters will pick up the enumeration of all the JsonConverters that are registered with the Autofac container and the SubThingConverter is registered such that when it is resolved it will have the IFactory resolved for it and the JsonSerializer also comes from the autofac container with these settings.
Even when I skip the dependency injection and use a new JsonSerializer with the JsonSerializerSettings with the custom converter added as
settings.Converters.Add(new SubThingConverter(new SubThingFactory()))
I still get the complaint that the SubThingConverter does not have a no arg constructor.
It seems to me that overridding the settings to explicitly use this converter should be enough. I also tried adding in the object[] params in the JsonConverter attribute on the SubThing, I couldn't get it to work and it seems to need to be a compile time array, which doesn't work with the dependency injection I need to do. Any pointers would be greatly appreciated. Thank you!
I want to deserialize json which returns different data for different parameters.
Mostly I get:
{"posts": [{ "id" : 1, "name":"post1" },{ "id" : 1, "name":"post1" }]}
But sometimes the data returned is
{"posts": false}
I want to deserialize this as the following class
public class GetReaderResponse
{
public IEnumerable<ReaderPost> posts {get; set;}
}
public class ReaderPost
{
public int id {get; set;}
public string name{get; set;}
}
I am using C#,json.net but not able to do this correctly.
Newtonsoft.Json.JsonConvert.DeserializeObject<GetReaderResponse>(dataString);
You could build a custom converter, but an easy way to handle this would be to write an error handler that detects errors with the posts property:
var settings = new JsonSerializerSettings();
settings.Error += (sender, args) =>
{
if (string.Equals("posts", args.ErrorContext.Path, StringComparison.OrdinalIgnoreCase))
{
var currentObject = args.CurrentObject as GetReaderResponse;
currentObject.posts = Enumerable.Empty<ReaderPost>();
args.ErrorContext.Handled = true;
}
};
GetReaderResponse resp =
JsonConvert.DeserializeObject<GetReaderResponse>(json, settings);
This sets posts to Enumerable.Empty<ReaderPost>. This is still a little unsatisfying because if any error occurs, the property will be set. You could build a full custom converter to do this as a more complete solution.
Here's a converter that will take care of this:
public class PostsConverter : JsonConverter
{
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
JToken val = JValue.ReadFrom(reader);
object result = null;
if (val.Type == JTokenType.Array)
{
result = val.ToObject<IEnumerable<ReaderPost>>();
}
else if (val.Type == JTokenType.Boolean)
{
result = Enumerable.Empty<ReaderPost>();
}
return result;
}
public override void WriteJson(
JsonWriter writer,
object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert (Type type)
{
return typeof(IEnumerable<ReaderPost>).IsAssignableFrom(type);
}
public override bool CanRead
{
get { return true; }
}
}
Usage:
var settings = new JsonSerializerSettings();
settings.Converters = new [] { new PostsConverter() };
GetReaderResponse resp =
JsonConvert.DeserializeObject<GetReaderResponse>(json, settings);
Example: https://dotnetfiddle.net/i9CXwp
By using JSON.NETs built in LINQ to JSON, you can try someting like this:
JObject jObject = JObject.Parse(json);
GetReaderResponse response = new GetReaderResponse();
if (jObject["posts"] is JArray)
response = jObject.ToObject<GetReaderResponse>();
// Do something with the response object.
where json variable is the json string you need to deserialize.
try this:
public class GetReaderResponse
{
public bool posts { get; set; }
public ReaderPost[] post { get; set; }
}
After reading #Ilija's comment I think I might have found a answer. I did not want not use string literals so I modified my class GetReaderResponse to look like below:
public class GetReaderResponse
{
public dynamic posts {get; set;}
public IEnumerable<ReaderPost> Posts
{
get
{
if (posts is bool )
return new ReaderPost[0];
return posts.ToObject<IEnumerable<ReaderPost>>();
}
}
}
Does this sound fine or does it look messy?