Newtonsoft JsonConvert.SerializeObject use property name insted of [JsonProperty("")] [duplicate] - c#

This question already has answers here:
How to ignore JsonProperty(PropertyName = "someName") when serializing json?
(2 answers)
Closed 2 years ago.
I have a class in my project which needs to be serialized for two different use cases.
As I can't add two diffrent attributes to on property I would like to serialize the objects of the class
one time with the [JsonProperty("attributname")] decleration and one time with the property name it self.
For ex:
public class Contact
{
[JsonProperty("name")]
public string Lastname { get; set; }
}
public class Program
{
public void SerializeByJsonPropertyName()
{
var contact = new Contact()
{
Lastname = "Harber"
}
var requestJson = JsonConvert.SerializeObject(contact);
// Serialized Object requestJson:
// {
// "name" = "Harber"
// }
}
public void SerializeByPropertyName()
{
var contact = new Contact()
{
Lastname = "Harber"
}
var requestJson = JsonConvert.SerializeObject(contact /*, ? Setting ?*/);
// Serialized Object requestJson:
// {
// "Lastname" = "Harber"
// }
}
}
The first szenario works totaly fine, but for the second szenario I couldĀ“t find any solution. Except creating two classes or duplicate the properties in my class.. IS there any setting in Newtonsofts JsonConverter for doing this?
Thanks for your help!

You can create different naming strategies, then create different settings, each setting has a Contract resolver, each contract resolver has a naming strategy, then you supply for each settings the naming strategy you want to use,
something like this
public static class JsonSerializingSettings {
public static JsonSerializerSettings JsonUnModified{ get; set; } = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver() {
NamingStrategy = new UnmodifiedNamingStrategy()
}
};
public static JsonSerializerSettings JsonDefault { get; set; } = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver() {
NamingStrategy = new DefaultNamingStrategy()
}
};
}
public class UnmodifiedNamingStrategy : NamingStrategy {
protected override string ResolvePropertyName(string name) {
return name;
}
}
and when you want to use it
var requestJson = JsonConvert.SerializeObject(contact,JsonSerializingSettings.JsonDefault);
// Serialized Object requestJson:
// {
// "name" = "Harber"
// }
var requestJson = JsonConvert.SerializeObject(contact,JsonSerializingSettings.JsonUnModified);
// Serialized Object requestJson:
// {
// "Lastname" = "Harber"
// }

Related

.Net System.Text.Json cannot serialize object [duplicate]

This question already has answers here:
How to use class fields with System.Text.Json.JsonSerializer?
(3 answers)
Closed 1 year ago.
I can't serialize BaseResponse<NoContent> object. But Device object serialized successfully. I didnt find reason
var data = new BaseResponse<NoContent>();
var json1 = JsonSerializer.Serialize(data);
var data2 = new Device();
var json2 = JsonSerializer.Serialize(data2);
BaseResponse Content :
public class BaseResponse<T> where T : class, new()
{
public BaseResponse()
{
Data = new T();
}
public T Data;
public string Status = "Success";
public List<string> Errors;
public BaseResponse<T> AddError(string message)
{
Errors ??= new List<string>();
Status = "Error";
Errors.Add(message);
return this;
}
}
Edit: Newtonsoft.Json can serialize both of them. I decided use Newtonsoft.
you have to add getters and setters
public class BaseResponse<T> where T : class, new()
{
public BaseResponse()
{
Data = new T();
}
public T Data {get; set;}
public string Status {get; set;} = "Success";
public List<string> Errors {get; set;}
public BaseResponse<T> AddError(string message)
{
Errors ??= new List<string>();
Status = "Error";
Errors.Add(message);
return this;
}
}
test
var data = new BaseResponse<NoContent>();
data.AddError("error message");
var json1 = System.Text.Json.JsonSerializer.Serialize(data);
output
{
"Data": {
"Content": "No Content"
},
"Status": "Error",
"Errors": [
"error message"
]
}
NoContent class
public class NoContent
{
public string Content {get; set;} ="No Content";
}
What's missing is the option to include fields, by default System.Text.Json does not serialize fields, only properties.
You can change this behaviour by supplying JsonSerializerOptions, e.g:
var json1 = JsonSerializer.Serialize(data, new JsonSerializerOptions() { IncludeFields = true });

Base Class Serialization Issue - do not serialize Derived Class Properties

I am tiring to serialize a fairly large list of entities, that are all derived from a base class.
I only need the base class properties in the client. How do I achieve this without instantiating a new instance of the base class?
I have tried creating a custom ContractResolver, but it seems that it does a getType() at runtime, instead of using the Type of the list/Array being serialized
See code sample below.
I want to achieve. castBaseString == actualBaseString ;
So I want castBaseString to = [{"Id":1},{"Id":2}] not [{"Value":"value","Id":1},{"Value":"value2","Id":2}]
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Tests {
[TestClass]
public class JsonNetTest {
class Base {
public int Id { get; set; }
}
class Derived : Base {
public string Value { get; set; }
}
class OtherDerived : Base {
public string Value { get; set; }
public string OtherValue { get; set; }
}
[TestMethod]
public void Test() {
IEnumerable<Derived> deriveds = new Derived[] {
new Derived {Id = 1, Value = "value" },
new Derived {Id = 2, Value = "value2" }
};
IEnumerable<Base> castBases = deriveds.Cast<Base>().ToList();
IEnumerable<Base> bases = new Base[] {
new Base {Id = 1 },
new Base {Id = 2 }
};
JsonSerializerSettings s = new JsonSerializerSettings();
var derString = JsonConvert.SerializeObject(deriveds, s);
var castBaseString = JsonConvert.SerializeObject(castBases, s);
var actualBaseString = JsonConvert.SerializeObject(bases, s);
Assert.AreEqual(actualBaseString, castBaseString);
Assert.AreNotEqual(castBaseString, derString);
}
}
}
EDIT BASED ON COMMENTS
Additional Context:
I just posted this simple test case for clarity.
the actual context this is being used is in an aspnet core application.
Consider there are 3 controllers
/api/DerivedController/
/api/OtherDerivedController/
/api/BaseController/
when a client calls 1, we want to return a list of Derived, when a client calls
2 we want to return a list of OtherDerived when they call 3, we want to return a list of Base
The data is stored in 2 different tables in the database TBL_DERIVED and TBL_OTHERDERIVED.
What we want to achieve when they call base is to return data from One or both of these tables, but just the common properties of these tables.
Hope this clarifies.
If you don't want to use attributes, you can use a ContractResolver to force only properties from Base to be serialized:
public class DerivedTypeFilterContractResolver<T> : DefaultContractResolver {
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType != typeof(T)) {
property.ShouldSerialize = instance => false;
}
return property;
}
}
Then use it like this:
void Main() {
IEnumerable<Derived> deriveds = new Derived[] {
new Derived {Id = 1, Value = "value" },
new Derived {Id = 2, Value = "value2" }
};
IEnumerable<Base> castBases = deriveds.Cast<Base>().ToList();
IEnumerable<Base> bases = new Base[] {
new Base {Id = 1 },
new Base {Id = 2 }
};
JsonSerializerSettings s = new JsonSerializerSettings {
ContractResolver = new DerivedTypeFilterContractResolver<Base>()
};
var derString = JsonConvert.SerializeObject(deriveds, s);
var castBaseString = JsonConvert.SerializeObject(castBases, s);
var actualBaseString = JsonConvert.SerializeObject(bases, s);
Console.WriteLine(castBaseString);
}
class Base {
public int Id { get; set; }
}
class Derived : Base {
public string Value { get; set; }
}
Output:
[{"Id":1},{"Id":2}]
Add [JsonIgnore] top of property.
class Derived : Base {
[JsonIgnore]
public string Value { get; set; }
}
Turns out this is not possible the way the json serialization works.
#stuartd answer might be a good workaround if you only have limited cases you want to do this for.

MongoDB C# Driver: flatten object

Imagine I have a class structure like this
class Aggregate
{
string Id {get;set;}
Parameters Parameters {get;set;}
}
class Parameters
{
List<string> Values {get;set;}
// logic based on Values
}
Now I want mongo to store it like this
{
"_id": "...",
"parameters": ["param A", "param B"]
}
How do I configure it using BsonClassMap.RegisterClassMap?
I'm ignoring the fact that the members of your class are private at the moment, as that would mean you couldn't read anything from the class. So let's just make them public
class Aggregate
{
public string Id { get; set; }
public Parameters Parameters { get; set; }
}
class Parameters
{
public List<string> Values { get; set; }
}
So now we'll need a custom serializer to deal with flattering the object and then also reading back the flattened document.
Below is just a noddy example that delegate serializing Parameters to the BsonArraySerializer.
public class ParametersSerializer : IBsonSerializer
{
private readonly BsonArraySerializer _bsonArraySerializer;
public ParametersSerializer()
{
_bsonArraySerializer = new BsonArraySerializer();
}
public object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var bsonArray = _bsonArraySerializer.Deserialize(context, args);
var netList = BsonTypeMapper.MapToDotNetValue(bsonArray, new BsonTypeMapperOptions()
{
MapBsonArrayTo = typeof(List<string>)
}) as List<string>;
return new Parameters
{
Values = netList
};
}
public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)
{
var typed = (Parameters) value;
var bsonArray = new BsonArray(typed.Values);
_bsonArraySerializer.Serialize(context, args, bsonArray);
}
public Type ValueType { get; } = typeof(Parameters);
}
Now we've got this we can just add an attribute to our class to tell Mongo driver to use it.
class Aggregate
{
public string Id { get; set; }
[BsonSerializer(typeof(ParametersSerializer))]
public Parameters Parameters { get; set; }
}
Now when we write and read documents to mongo:
var pack = new ConventionPack();
pack.Add(new CamelCaseElementNameConvention());
ConventionRegistry.Register(
"My Custom Conventions",
pack,
t => t == typeof(Aggregate) || t == typeof(Parameters));
var client = new MongoClient();
var database = client.GetDatabase("test");
var collection = database.GetCollection<Aggregate>("test");
await database.DropCollectionAsync(collection.CollectionNamespace.CollectionName);
await collection.InsertOneAsync(new Aggregate{Id = "...", Parameters = new Parameters{Values = {"1", "2"}}});
var all = await (await collection.FindAsync(Builders<Aggregate>.Filter.Empty)).ToListAsync();
we'll get the following save in MongoDB collection:
> db.test.find()
{ "_id" : "...", "parameters" : [ "1", "2" ] }
Also notice we're using a custom convention of camel case to deal with creating camel case properties too.

Json camel casing properties

https://dotnetfiddle.net/R96sPn
I am trying to create AddJsonObject such that the name of External.AddParameter obeys whatever the ContractRevolver is set for External.Serialize
In the example below it is camel casing, but as you can see the output is not camel cased. Changing the ContractResolver should effectively determine the formatting of the name parameter. No additional code can be added to External class
This is a class that I cannot modify:
public static class External
{
public static string Serialize(object obj)
{
JsonSerializer ser = new JsonSerializer{
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore
};
using (StringWriter stringWriter = new StringWriter())
{
using (JsonTextWriter jsonTextWriter = new JsonTextWriter((TextWriter)stringWriter))
{
jsonTextWriter.Formatting = Formatting.Indented;
jsonTextWriter.QuoteChar = '"';
ser.Serialize((JsonWriter)jsonTextWriter, obj);
return stringWriter.ToString();
}
}
}
public static void AddParameter(string name, string str)
{
Console.WriteLine(name + " : " + str);
}
}
Just example class:
public class Product { public Product ChildProduct { get; set; } public string Name { get; set; } public DateTime Expiry { get; set; } public string[] Sizes { get; set; } }
Main:
public class Program
{
public void AddJsonObject(object obj)
{
foreach (var property in obj.GetType().GetProperties())
{
var propValue = property.GetValue(obj, null);
External.AddParameter(property.Name, External.Serialize(propValue));
}
}
public void Main()
{
Product product = new Product();
product.Name = "Apple";
product.Expiry = new DateTime(2008, 12, 28);
product.Sizes = new string[]{"small", "big"};
product.ChildProduct = new Product();
AddJsonObject(product);
}
}
Output:
ChildProduct : {
"expiry": "0001-01-01T00:00:00"
}
Name : "Apple"
Expiry : "2008-12-28T00:00:00"
Sizes : [
"small",
"big"
]
Desired Output:
childProduct : {
"expiry": "0001-01-01T00:00:00"
}
name : "Apple"
expiry : "2008-12-28T00:00:00"
sizes : [
"small",
"big"
]
This demo showcases Serialize using JSON.net with CamelCase, but AddJsonObject should work independent of what json serializer they use or what formatting. This example just showcases a non-trivial example.
My initial attempt consisted of wrapping the object into a parent object. External.Serialize() the wrapper object then somehow feed the results into AddParameter such that the name is the output of the serialization -- Couldn't get this to work right.
Change Line where you initialize ContractResolver (ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()) to
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
};
Ok I have a solution that works, but Ill hold off anymore clever answers come up
public void AddJsonObject(object obj)
{
var jsonObj = JObject.Parse(External.Serialize(obj));
foreach (var property in jsonObj.Properties())
{
External.AddParameter(property.Name, External.Serialize( property.Value));
}
}

Include class Name in WCF Json Serialization

This must be trivial but I can't seem to get it done. Given the following data contract class:
public class SampleItem
{
public int Id { get; set; }
public string StringValue { get; set; }
}
when deserialized to JSON by my WCF service, provides the following output:
[{"Id":1,"StringValue":"Hello"}]
Is there any way to include the class name too? i.e.:
"SampleItem": [{"Id":1,"StringValue":"Hello"}]
You could try something like this:
private dynamic AddClassName(SampleItem item)
{
return new {SampleItem = item};
}
And
var item = new SampleItem {Id = 1, StringValue = "Hello"};
dynamic itemClassName = AppendClassName(item);
string json = new JavaScriptSerializer().Serialize(itemClassName);
Debug.WriteLine(json);
Edit - this works for all types:
private static string GetJsonWrapper<T>(T item)
{
string typeName = typeof(T).Name;
string jsonOriginal = new JavaScriptSerializer().Serialize(item);
return string.Format("{{\"{0}\":{1}}}", typeName, jsonOriginal);
}

Categories

Resources