How to create custom serialization on different properties of an object [duplicate] - json.net

This question already has answers here:
Keep casing when serializing dictionaries
(4 answers)
Closed 5 years ago.
I have an object and I want to serialize it to a custom serialization format. e.g
class MyObj
{
public string Name { get;set;}
public Dictionary<string, string> KeyValues {get;set; }
}
I want to camelcase Name but not KeyValues. Is this possible in Newtonsoft? I know how to do this for the entire object but not for specific properties.
So, the output should look like this:
{
"name" : "Mike",
"keyValues":
{
"Abc": "x",
"Prv": "y"
}
}

You just have to attribute the fields with JsonProperty and set the label that you want to use:
public class MyObj
{
[JsonProperty("name")]
public string Name { get;set;}
[JsonProperty("KeyValues")]
public Dictionary<string, string> KeyValues {get;set; }
}
JsonProperty is included in Newtonsoft.Json library.

Related

Change key name of data loaded through JsonExtensionData [duplicate]

This question already has an answer here:
How to change all keys to lowercase when parsing JSON to a JToken
(1 answer)
Closed 5 years ago.
I have a JSON resulting from a mix of system data and user entries, like this :
{
"Properties": [{
"Type": "A",
"Name": "aaa",
"lorem ipsum": 7.1
}, {
"Type": "B",
"Name": "bbb",
"sit amet": "XYZ"
}, {
"Type": "C",
"Name": "ccc",
"abcd": false
}]
}
I need to load it, process it, and save it to MongoDB. I deserialize it to this class :
public class EntityProperty {
public string Name { get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> OtherProperties { get; set; }
public string Type { get; set; }
}
The problem is that MongoDB does not allow dots in key names, but the users can do whatever they want.
So I need a way to save this additional JSON data but I also need to change the key name as it's being processed.
I tried to add [JsonConverter(typeof(CustomValuesConverter))] to the OtherProperties attribute but it seems to ignore it.
Update/Clarification: since the serialization is done by Mongo (I send the objects to the library), I need the extension data names to be fixed during deserialization.
Update
Since the fixing of names must be done during deserialization, you could generalize the LowerCasePropertyNameJsonReader from How to change all keys to lowercase when parsing JSON to a JToken by Brian Rogers to perform the necessary transformation.
First, define the following:
public class PropertyNameMappingJsonReader : JsonTextReader
{
readonly Func<string, string> nameMapper;
public PropertyNameMappingJsonReader(TextReader textReader, Func<string, string> nameMapper)
: base(textReader)
{
if (nameMapper == null)
throw new ArgumentNullException();
this.nameMapper = nameMapper;
}
public override object Value
{
get
{
if (TokenType == JsonToken.PropertyName)
return nameMapper((string)base.Value);
return base.Value;
}
}
}
public static class JsonExtensions
{
public static T DeserializeObject<T>(string json, Func<string, string> nameMapper, JsonSerializerSettings settings = null)
{
using (var textReader = new StringReader(json))
using (var jsonReader = new PropertyNameMappingJsonReader(textReader, nameMapper))
{
return JsonSerializer.CreateDefault(settings).Deserialize<T>(jsonReader);
}
}
}
Then deserialize as follows:
var root = JsonExtensions.DeserializeObject<RootObject>(json, (s) => s.Replace(".", ""));
Or, if you are deserializing from a Stream via a StreamReader you can construct your PropertyNameMappingJsonReader directly from it.
Sample fiddle.
Alternatively, you could also fix the extension data in an [OnDeserialized] callback, but I think this solution is neater because it avoids adding logic to the objects themselves.
Original Answer
Assuming you are using Json.NET 10.0.1 or later, you can create your own custom NamingStrategy, override NamingStrategy.GetExtensionDataName(), and implement the necessary fix.
First, define MongoExtensionDataSettingsNamingStrategy as follows:
public class MongoExtensionDataSettingsNamingStrategy : DefaultNamingStrategy
{
public MongoExtensionDataSettingsNamingStrategy()
: base()
{
this.ProcessExtensionDataNames = true;
}
protected string FixName(string name)
{
return name.Replace(".", "");
}
public override string GetExtensionDataName(string name)
{
if (!ProcessExtensionDataNames)
{
return name;
}
return name.Replace(".", "");
}
}
Then serialize your root object as follows:
var settings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver { NamingStrategy = new MongoExtensionDataSettingsNamingStrategy() },
};
var outputJson = JsonConvert.SerializeObject(root, settings);
Notes:
Here I am inheriting from DefaultNamingStrategy but you could inherit from CamelCaseNamingStrategy if you prefer.
The naming strategy is only invoked to remap extension data names (and dictionary keys) during serialization, not deserialization.
You may want to cache the contract resolver for best performance.
There is no built-in attribute to specify a converter for dictionary keys, as noted in this question. And in any event Json.NET would not use the JsonConverter applied to OtherProperties since the presence of the JsonExtensionData attribute supersedes the converter property.
Alternatively, if it would be more convenient to specify the naming strategy using Json.NET serialization attributes, you will need a slightly different naming strategy. First create:
public class MongoExtensionDataAttributeNamingStrategy : MongoExtensionDataSettingsNamingStrategy
{
public MongoExtensionDataAttributeNamingStrategy()
: base()
{
this.ProcessDictionaryKeys = true;
}
public override string GetDictionaryKey(string key)
{
if (!ProcessDictionaryKeys)
{
return key;
}
return FixName(key);
}
}
And modify EntityProperty as follows:
[JsonObject(NamingStrategyType = typeof(MongoExtensionDataAttributeNamingStrategy))]
public class EntityProperty
{
public string Name { get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> OtherProperties { get; set; }
public string Type { get; set; }
}
The reason for the inconsistency is that, as of Json.NET 10.0.3, DefaultContractResolver uses GetDictionaryKey() when remapping extension data names using a naming strategy that is set via attributes here, but uses GetExtensionDataName() when the naming strategy is set via settings here. I have no explanation for the inconsistency; it feels like a bug.

How to get the elements from a generic List<T> in C#? [duplicate]

This question already has answers here:
Convert generic List/Enumerable to DataTable?
(28 answers)
Closed 8 years ago.
I have a class that converts a T List into a DataTable. And my problem is that I can not get the elements from the generic List T. T can be any model from entityFramework, like:
public class Test
{
public string userId { get; set; }
public string email { get; set; }
public string name { get; set; }
}
So List T is equivalent to List Test
My code is:
public class ConvertListToDataTable<T>
{
public T Field;
public DataTable ConvertTest<T>(List<T> item)
{
DataTable dT = new DataTable();
foreach(T t in item)
{
// here I want to get the elements from T and put them into the DataTable
}
return dT;
}
}
I know how to procces the dataTable, but I don't know how to get the 'userId', 'email', 'name' from the list
The reason why you cannot access just any property from an object of type T is because T could be literally anything - from a generics standpoint how can you know upfront what properties type T has?
If you happen to know that all objects are based on a common interface or base class then you could apply a generic constraint to your ConvertListToDataTable<T> class; this would allow the compiler to know upfront that some properties are available to that type at a base level.
But if you want to allow T to be any type at all then #MrFox is correct in that you would need to use Reflection to view the properties the type has at runtime and fill your DataTable that way.
Reflection can solve this:
public void Test(object t)
{
var properties = t.GetType().GetProperties();
foreach (var p in properties)
{
dT.Columns.Add(new DataColumn(p.Name, p.PropertyType));
}
var row = dT.NewRow();
int col = 0;
foreach (var p in properties)
{
row[col++] = p.GetValue(t);
}
}
You can give the GetProperties method arguments if you only want properties of a certain type.

formatting issue when de-serialize numeric field as string in Json.net

When I try to convert a json numeric field into C# string field, I get the following formatting issue:
Json format:
{"a":-7.0}
Corresponding C# class:
public class JsonReport
{
public string a {get;set;}
}
when I call the method:
var obj=JsonConvert.DeserializeObject<JsonReport>(strContent);
I expect the obj.a would be "-7.0", but it turn out to be "-7", which is not what I want , what should I set to fix the issue? Thanks.
If you have a decimal value like this, it is usually best to wait until the last possible moment--that is, just before you are about to display the value to the end user--before formatting it to a string. Waiting affords you the greatest flexibility in working with the value; you can do calculations with it, or decide to format it differently for different displays. So my recommendation would be to change the a property in your JsonReport class to be a double or decimal (depending on your needs), then format the value later, when you are ready to output it.
All that said, if you really want to format the decimal value to a string during deserialization, you can do this with a custom JsonConverter. Here is what it might look like:
class FloatToStringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken value = JToken.Load(reader);
if (value.Type == JTokenType.Float)
{
// Adjust the formatting to your requirements
return value.ToObject<double>().ToString("0.0#######;-0.0#######");
}
return value.ToString();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use the converter, decorate the class properties you want formatted with a JsonConverter attribute like this:
public class JsonReport
{
[JsonConverter(typeof(FloatToStringConverter))]
public string a { get; set; }
}
Then you can deserialize as normal:
var obj = JsonConvert.DeserializeObject<JsonReport>(strContent);
Console.WriteLine(obj.a);
Output:
-7.0
pass your json object as string
{"a":"-7.0"}
Change your C# class
public class JsonReport
{
public string a {get;set;}
}

Converting Entity Framework object to JSON (without object graph)

Suppose I have a object structure like this
Library 1 ---- 1+ Book 1 ---- 1+ Page
I want to serialize a json object of a book with an array of page objects.
Using JSON.net serializer, I can get this to serialize without getting a circular reference, but the JSON still includes all of the properties of the book in each page, which includes data about the library...which can have data on other books which is a ton of noise.
From the answer from this question - Serialize Entity Framework objects into JSON, I know that I can do generics, but is this really the only way? This just seems like a ton of extra work. Especially if for a Json result that is Book with and array of page objects in it.
I am using Entity Framework 4.3.1 and Json.net 4.0.30319...
You should look at the serialization attributes.
[JsonObject(MemberSerialization.OptIn)]
public class Page
{
[JsonProperty]
public string Text { get; set; }
// not serialized because mode is opt-in
public Book Book { get; set; }
}
Original answer
The aforementioned way should be prefered in most of the cases, but there are some where it is not enough.
There are two ways of doing it.
You can implement a JsonConverter, and override the WriteJson method to write only the properties you want.
class BookConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Book);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value.GetType() == typeof(T2))
{
JObject obj = new JObject();
Book b = value as Book;
obj["titre"] = b.Name;
obj["pages"] = b.Pages;
// This line can also be
// obj.WriteTo(writer, this);
// if you also need change the way pages are serialized.
obj.WriteTo(writer, null);
}
else
{
throw new NotImplementedException();
}
}
}
You can call it like that :
string result = JsonConvert.SerializeObject(
book,
new JsonSerializerSettings
{
Converters = new JsonConverter[] { new BookConverter() }
});
You can also create a JsonBook class, and serialize it.
class JsonBook{
public JsonBook(Book b){/*...*/}
public List<Pages> l;
public string title;
// No reference to Library.
}

Custom JsonSerialization

I have a simple class in asp.net mvc that looks like this:
public class JsonResponseItem
{
public string Key { get; set; }
public string Value { get; set; }
public JsonResponseItem(string key, string value)
{
Key = key;
Value = value;
}
}
In my controllers I create a list of that type
List<JsonResponseItem> response = new List<JsonResponseItem>();
so I can easily manage and add to the Json response. A dictionary object is kind of hard to do that with.
When I return the json object
return Json(response);
It deserializes it so I have to reference everything by index first, because of the list. So if I had a property called "IsValid" I would have to reference it like this "IsValid[0]". I have way too much javascript code to make these changes.
How could I deserialize the JsonResponseItem class so I don't need the index reference in there?
Thanks!
A Dictionary<string, string> would serialize into exactly the Json you're asking for. If you don't want to expose directly a dictionary, wrap it around in another class or use Json(response.ToDictionary(item => item.Key).

Resources