This question already has answers here:
How to serialize a Dictionary as part of its parent object using Json.Net
(3 answers)
How to deserialize a child object with dynamic (numeric) key names?
(2 answers)
Closed 5 years ago.
everybody
I have a json serialize problem for dictionary property name
This is my codes:
public class MyClass
{
public string A { get; set; }
public string B { get; set; }
public Dictionary<string, C> CS { get; set; }
}
public class C
{
public string C1 { get; set; }
public string C2 { get; set; }
}
MyClass myObject = new MyClass()
{
A = "A_Value",
B = "B_Value",
CS = new Dictionary<string, C>
{
{"CS1", new C() { C1 = "CS1_C1_Value", C2 = "CS1_C2_Value" } },
{"CS2", new C() { C1 = "CS2_C1_Value", C2 = "CS2_C2_Value" } }
}
};
JsonConvert.SerializeObject(myObject);
I will get the result:
{
"A":"A_Value",
"B":"B_Value",
"CS":{
"CS1":{
"C1":"CS1_C1_Value",
"C2":"CS1_C2_Value"
},
"CS2":{
"C1":"CS2_C1_Value",
"C2":"CS2_C2_Value"
}
}
}
How to I serialize it without dictionary's property name like:
{
"A":"A_Value",
"B":"B_Value",
"CS1":{
"C1":"CS1_C1_Value",
"C2":"CS1_C2_Value"
},
"CS2":{
"C1":"CS2_C1_Value",
"C2":"CS2_C2_Value"
}
}
I tried remove the property of serialized result, then add the serialized dictionary data
Have easier way to solve it?
If you are willing to convert the dictionary to Dictionary<string, object> you can use [JsonExtensionData] attribute
public class MyClass
{
public string A { get; set; }
public string B { get; set; }
[JsonExtensionData]
public Dictionary<string, object> CS { get; set; }
}
Related
This question already has answers here:
How can I change property names when serializing with Json.net?
(4 answers)
Closed 6 months ago.
I'm having an issue with JsonConvert.SerializeObject when I try to serialize my object with:
var statementOfAccount = new
{
main = new
{
#xm = "https://api-path.com",
RESTHeader = new
{
Responsibility = "Responsibility ",
RespApplication = "AR",
NLSLanguage = "AMERICAN"
}
}
};
And giving me:
{
"main": {
"xm": "https://api-path.com",
"RESTHeader": {
"Responsibility": "Responsibility",
"RespApplication": "AR",
"NLSLanguage": "AMERICAN"
}
}
}
So basically it's removing the # from #xm and please note that I can't change the name of the property so I need a solution that will serialize it as is.
one way would be to create a custom class instead of using an anonymous
public class Custom
{
....
[JsonProperty["#xm"]
public string #xm {get; set;}
....
}
since your anonymous object properties are known and can't cause any property overlaping, another way is to replace a json string after serialization
json=json.Replace("xm:","#xm:");
https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-customize-properties?pivots=dotnet-6-0
this solved my problem to use JsonPropertyName but with JsonSerializer.Serialize instead of JsonConvert.SerializeObject so the problem was me using an anonymous object as most of you suggested but using JsonPropertyName only worked with JsonSerializer.Serialize, it didn't work with JsonConvert.SerializeObject
solution:
public class Main
{
[JsonPropertyName("#xm")]
public string #xm { get; set; }
public RESTHeader RESTHeader { get; set; }
}
public class RESTHeader
{
public string Responsibility { get; set; }
public string RespApplication { get; set; }
public string NLSLanguage { get; set; }
}
public class Root
{
public Main main { get; set; }
}
var statementOfAccount = new Root
{
main = new Main
{
#xm = "https://api-path",
RESTHeader = new RESTHeader
{
Responsibility = "Responsibility ",
RespApplication = "AR",
NLSLanguage = "AMERICAN"
}
}
};
var json = System.Text.Json.JsonSerializer.Serialize(statementOfAccount);
I have just started using AutoMapper on an asp net core project and I'm trying to map an object that has a collection of an object that also has a collection of an object to an entity.
The source
public class MyClass
{
public List<MyCollection> MyCollections { get; set; }
}
public class MyCollection
{
public int CollectionId { get; set; }
public List<Description> Descriptions { get; set; }
}
public class Description
{
public int DescriptionId { get; set; }
public string Text { get; set; }
}
The destination
public class DescriptionToCollection
{
public int DescriptionId { get; set; }
public int CollectionId { get; set; }
}
I've played around with ConvertUsing thinking something like this, but I can't make it work.
CreateMap<MyClass, List<DescriptionToCollection>>()
.ConvertUsing(source => source.MyCollections.Select(x =>x.Description.Select(y=> new DescriptionToCollection{ DescriptionId=y.DescriptionId,CollectionId=x.CollectionId}).ToList()
));
Searching AutoMappers docs and the internet, I couldn't find anything similar to my problem.
Any help is highly appreciated.
Besides a typo in your example code, you almost had it. Because you aren't mapping 1:1 at the top level, you need to flatten somewhere, and you do that using SelectMany, moving the ToList call appropriately.
CreateMap<MyClass, List<DescriptionToCollection>>()
.ConvertUsing(source => source.MyCollections.SelectMany(x => // SelectMany to flatten
x.Descriptions.Select(y =>
new DescriptionToCollection
{
DescriptionId = y.DescriptionId,
CollectionId = x.CollectionId
}
) // ToList used to be here
).ToList()
);
Try to implement ITypeConverter, follow the example code:
Your Classes
public class Class1
{
public List<Class2> class2 { get; set; }
}
public class Class2
{
public int CollectionId { get; set; }
public List<Class3> class3 { get; set; }
}
public class Class3
{
public int DescriptionId { get; set; }
public string Text { get; set; }
}
public class ClassDto
{
public int DescriptionId { get; set; }
public int CollectionId { get; set; }
}
The custom map
public class ClassCustomMap : ITypeConverter<Class1, List<ClassDto>>
{
public List<ClassDto> Convert(Class1 source, List<ClassDto> destination, ResolutionContext context)
{
var classDtoList = new List<ClassDto>();
foreach (var item in source.class2)
{
var collectionID = item.CollectionId;
foreach (var obj in item.class3)
{
var classDto = new ClassDto();
classDto.CollectionId = collectionID;
classDto.DescriptionId = obj.DescriptionId;
classDtoList.Add(classDto);
}
}
return classDtoList;
}
}
The mapping declaration
CreateMap<Class1, List<ClassDto>>().ConvertUsing<ClassCustomMap>();
How to use it
var class2 = new Class2();
class2.CollectionId = 2;
var class3 = new Class3();
class3.DescriptionId = 1;
class3.Text = "test";
class2.class3 = new System.Collections.Generic.List<Class3>() { class3 };
var class1 = new Class1();
class1.class2 = new System.Collections.Generic.List<Class2>() { class2 };
var result = mapper.Map<List<ClassDto>>(class1);
For clarity and to simplify I have used explicit cycles, if you want you can optimize the conversion function using LinQ and Lambda
You are missing the purpose of auto-mapper.
It should be used for transforming an input object of one type into an output object of a different type.
And you wanted to create a map from MyClass type to List - this should be treated as projection.
You can achieve that using LINQ (for example as a extension method on MyClass):
public static class MyClassExtension
{
public static List<DescriptionToCollection> ToDescriptionToCollection(this MyClass value)
{
return value.MyCollections.SelectMany(mc => mc.Descriptions.Select(d => new DescriptionToCollection()
{
CollectionId = mc.CollectionId,
DescriptionId = d.DescriptionId
})).ToList();
}
}
I have following Json-based configuration file:
{
"PostProcessing": {
"ValidationHandlerConfiguration": {
"MinimumTrustLevel": 80,
"MinimumMatchingTrustLevel": 75
},
"MatchingCharacterRemovals": [
"-",
"''",
":"
]
},
"Processing": {
"OrderSelection": {
"SelectionDaysInterval": 30,
"SelectionDaysMaximum": 365
}
}
}
As serialization framework I use Newtonsoft. To serialize this config into objects I have implemented following classes:
[JsonObject(MemberSerialization.OptIn)]
public class RecognitionConfiguration {
[JsonProperty(PropertyName = "PostProcessing", Required = Required.Always)]
public PostRecognitionConfiguration PostRecognitionConfiguration { get; set; }
[JsonProperty(PropertyName = "Processing", Required = Required.Always)]
public ProcessRecognitionConfiguration ProcessRecognitionConfiguration { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class PostRecognitionConfiguration {
[JsonProperty(Required = Required.Always)]
public ValidationHandlerConfiguration ValidationHandlerConfiguration { get; set; }
[JsonProperty] public List<string> MatchingCharacterRemovals { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class ProcessRecognitionConfiguration {
[JsonProperty(PropertyName = "OrderSelection", Required = Required.Always)]
public OrderSelectionConfiguration OrderSelectionConfiguration { get; set; }
}
In a class I try to serialize a specific configuration section into these class structures using IConfigurationSection.Get().
var serializedConfiguration = this.ConfigurationSection.Get<RecognitionConfiguration>();
But when I debug the code, I always get an "empty" variable serializedConfiguration which is not null, but all properties are null.
If I use
this.ConfigurationSection.GetSection("Processing").Get<ProcessRecognitionConfiguration>()
or change the naming of the properties in the json file to exactly match the property names in the classes like this:
{
"ProcessRecognitionConfiguration": {
"OrderSelectionConfiguration": {
"SelectionDaysInterval": 30,
"SelectionDaysMaximum": 365
}
}
}
it it works fine. Do you have any idea, why setting PropertyName on JsonProperty does not seem to have any effect?
That is by design. Binding to POCO via configuration is done by convention. Not like Model Binding to Controller Action parameters.
It matches property names on the POCO to keys in the provided JSON.
Reference Configuration in ASP.NET Core
So either you change the settings to match the class like you showed in the original question, or change the class to match the settings keys in the Json-based configuration file.
[JsonObject(MemberSerialization.OptIn)]
public class RecognitionConfiguration {
[JsonProperty(PropertyName = "PostProcessing", Required = Required.Always)]
public PostRecognitionConfiguration PostProcessing{ get; set; }
[JsonProperty(PropertyName = "Processing", Required = Required.Always)]
public ProcessRecognitionConfiguration Processing{ get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class PostRecognitionConfiguration {
[JsonProperty(Required = Required.Always)]
public ValidationHandlerConfiguration ValidationHandlerConfiguration { get; set; }
[JsonProperty]
public List<string> MatchingCharacterRemovals { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class ProcessRecognitionConfiguration {
[JsonProperty(PropertyName = "OrderSelection", Required = Required.Always)]
public OrderSelectionConfiguration OrderSelection { get; set; }
}
public partial class ValidationHandlerConfiguration {
[JsonProperty("MinimumTrustLevel")]
public long MinimumTrustLevel { get; set; }
[JsonProperty("MinimumMatchingTrustLevel")]
public long MinimumMatchingTrustLevel { get; set; }
}
public partial class OrderSelectionConfiguration {
[JsonProperty("SelectionDaysInterval")]
public long SelectionDaysInterval { get; set; }
[JsonProperty("SelectionDaysMaximum")]
public long SelectionDaysMaximum { get; set; }
}
EDIT: I found this one is much more pleasant than my previous solutions: Bind everything in an ExpandoObject, write them to JSON and use JSON.NET to bind them back. Using the code of this article:
namespace Microsoft.Extensions.Configuration
{
public static class ConfigurationBinder
{
public static void BindJsonNet(this IConfiguration config, object instance)
{
var obj = BindToExpandoObject(config);
var jsonText = JsonConvert.SerializeObject(obj);
JsonConvert.PopulateObject(jsonText, instance);
}
private static ExpandoObject BindToExpandoObject(IConfiguration config)
{
var result = new ExpandoObject();
// retrieve all keys from your settings
var configs = config.AsEnumerable();
foreach (var kvp in configs)
{
var parent = result as IDictionary<string, object>;
var path = kvp.Key.Split(':');
// create or retrieve the hierarchy (keep last path item for later)
var i = 0;
for (i = 0; i < path.Length - 1; i++)
{
if (!parent.ContainsKey(path[i]))
{
parent.Add(path[i], new ExpandoObject());
}
parent = parent[path[i]] as IDictionary<string, object>;
}
if (kvp.Value == null)
continue;
// add the value to the parent
// note: in case of an array, key will be an integer and will be dealt with later
var key = path[i];
parent.Add(key, kvp.Value);
}
// at this stage, all arrays are seen as dictionaries with integer keys
ReplaceWithArray(null, null, result);
return result;
}
private static void ReplaceWithArray(ExpandoObject parent, string key, ExpandoObject input)
{
if (input == null)
return;
var dict = input as IDictionary<string, object>;
var keys = dict.Keys.ToArray();
// it's an array if all keys are integers
if (keys.All(k => int.TryParse(k, out var dummy)))
{
var array = new object[keys.Length];
foreach (var kvp in dict)
{
array[int.Parse(kvp.Key)] = kvp.Value;
}
var parentDict = parent as IDictionary<string, object>;
parentDict.Remove(key);
parentDict.Add(key, array);
}
else
{
foreach (var childKey in dict.Keys.ToList())
{
ReplaceWithArray(input, childKey, dict[childKey] as ExpandoObject);
}
}
}
}
}
Usage:
var settings = new MySettings();
this.Configuration.BindJsonNet(settings);
Here is my testing MySettings class:
public class MySettings
{
[JsonProperty("PostProcessing")]
public SomeNameElseSettings SomenameElse { get; set; }
public class SomeNameElseSettings
{
[JsonProperty("ValidationHandlerConfiguration")]
public ValidationHandlerConfigurationSettings WhateverNameYouWant { get; set; }
public class ValidationHandlerConfigurationSettings
{
[JsonProperty("MinimumTrustLevel")]
public int MinimumTrustLevelFoo { get; set; }
[JsonProperty("MinimumMatchingTrustLevel")]
public int MinimumMatchingTrustLevelBar { get; set; }
}
}
}
After the calling, I get everything as you desired:
Old Answer:
According to the source code here, it is simply (near) impossible to do what you are requiring. I have tried both JsonProperty and DataContract, none of which are honored by the Binder, simply because the source code itself simply use the property name.
If you still insist, there are 2 possibilities, however I do not recommend any as changing properties' names are much simpler:
Fork your source code there, or simply copy that file (in my attempt to trace the code, I rename all methods to something like Bind2, BindInstance2 etc), and rewrite the code accordingly.
This one is very specific to current implementation, so it's not future-proof: the current code is calling config.GetSection(property.Name), so you can write your own IConfiguration and provide your own name for GetSection method and tap it into the bootstrap process instead of using the default one.
Changing PropertyName on JsonProperty does have effect. Here is the same I tried and it did worked for me:
my JSON data:
{"name": "John","age": 30,"cars": [ "Ford", "BMW", "Fiat" ]}
and the Model:
public class RootObject
{
[JsonProperty(PropertyName ="name")]
public string Apple { get; set; }
public int age { get; set; }
public List<string> cars { get; set; }
}
and here is the code:
RootObject obj = JsonConvert.DeserializeObject<RootObject>(json);
and this is the output i get
You need to set the PropertyName in JsonProperty same as json file property name but your C# model property can be what you wanted, just that they need to be decorated with [JsonProperty(PropertyName ="jsonPropertyName")] Hope this helps you solve your issue.
Happy coding...
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 7 years ago.
I have a CibilResponse Class that has properties that are of class type (TUEF class). I am trying to assign value to CibilEnquiryEnq.Tuef.Version but i am getting null reference error. Before you mark this question as duplicate let me mention that I have read some of the similar questions and their answers on SO and post which I have initialized tuef in the constructor as you can see in my code. Can you please point out if you can what is it that I am doing wrong?
ICIBIL.cs
[ServiceContract]
public interface ICIBIL
{
[OperationContract]
string InsertCibil(CibilResponse cibilResponse);
[OperationContract]
string GenerateEnquiry(CibilEnquiry testObj);
}
[DataContract]
public class CibilResponse
{
[DataMember]
public string ResponseString { get; set; }
[DataMember]
public string Business { get; set; }
[DataMember]
public string MkrId { get; set; }
}
[DataContract]
public class CibilEnquiry
{
[DataMember]
public TUEF Tuef { get; set; }
public CibilEnquiry()
{
this.Tuef = new TUEF();
}
}
[DataContract]
public class TUEF
{
[DataMember]
public string SegmentTag { get; set; }
[DataMember]
public string Version { get; set; }
[DataMember]
public string MemberReferenceNumber { get; set; }
}
Appication :
CibilWcfService.CIBIL obj = new CibilWcfService.CIBIL();
CibilWcfService.CibilEnquiry CibilEnquiryEnq = new CibilWcfService.CibilEnquiry();
CibilEnquiryEnq.Tuef.Version = "123";// null reference error here
string res = obj.GenerateEnquiry(CibilEnquiryEnq);
Can you try the below. C# is case sensitive.
using CbilFileReader.CibilWcfService;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
CIBIL obj = new CIBIL();
CibilEnquiry CibilEnquiryEnq = new CibilEnquiry();
TUEF objtuef = new TUEF();
objtuef.Version = "123";
CibilEnquiryEnq.Tuef = objtuef;
string res = obj.GenerateEnquiry(CibilEnquiryEnq);
}
}
}
This might do the trick for you
CibilWcfService.CIBIL obj = new CibilWcfService.CIBIL();
CibilWcfService.CibilEnquiry CibilEnquiryEnq = new CibilWcfService.CibilEnquiry();
CibilWcfService.CibilEnquiry.Tuef ObjTUEF = new CibilWcfService.CibilEnquiry.Tuef();
ObjTUEF.Version="123";
CibilEnquiryEnq.Tuef = ObjTUEF;
string res = obj.GenerateEnquiry(CibilEnquiryEnq);
I have a dictionary of strings and object that i obtained deserializing this json answer:
{"labels":[{"id":"1","descrizione":"Etichetta interna","tipo":"0","template_file":"et_int.txt"},{"id":"2","descrizione":"Etichetta esterna","tipo":"1","template_file":"et_ext.txt"}],"0":200,"error":false,"status":200}
using the code:
var labels = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(json);
Now i want to loop only trought the objects inside the "labels" key.
I tried
foreach (var outer in labels["labels"]){/* code */}
but i got error:
CS1579: foreach statement cannot operate on variables of type 'object' because 'object' does not contain a public definition for 'GetEnumerator'.
Solved replacing the dictionary with a class, thank you
Create a class to deserialize your json:
To create classes, you can copy the json in clipboard and use the
Edit / Paste special / Paste JSON as class
in visual studio (I use vs2013).
[TestMethod]
public void test()
{
string json = "{\"labels\" : [{\"id\" : \"1\",\"descrizione\" : \"Etichetta interna\",\"tipo\" : \"0\",\"template_file\" : \"et_int.txt\"}, {\"id\" : \"2\",\"descrizione\" : \"Etichetta esterna\",\"tipo\" : \"1\",\"template_file\" : \"et_ext.txt\"}],\"0\" : 200,\"error\" : false,\"status\" : 200}";
var root = JsonConvert.DeserializeObject<Rootobject>(json);
foreach (var label in root.Labels)
{
//Use label.Id, label.Descrizione, label.Tipo, label.TemplateFile
}
}
public class Rootobject
{
public Label[] Labels { get; set; }
public int _0 { get; set; }
public bool Error { get; set; }
public int Status { get; set; }
}
public class Label
{
public string Id { get; set; }
public string Descrizione { get; set; }
public string Tipo { get; set; }
public string TemplateFile { get; set; }
}
You need to loop through your dictionary.
foreach(KeyValuePair<string, Object> entry in labels)
{
// do something with entry.Value or entry.Key
}
Once you start looping through it you will get access to key and value. Since you are interested to look at entry.value you can do operation on that easily. Currently your dictionary value is type of object which does not have an enumerator
Your problem is that you've defined the Type of Value for each dictionary entry as object. C# can't know how to loop over on object. So you need to work out what type is actually inside the object once the JavaScriptSerializer have parsed the JSON. One way is
var t = typeof(labels["labels"]);
Once you know what type the serializer is creating, all you need to do is cast the object back to that type. For example, assuming it's a list of objects
var labels = (List<object>)labels["labels"];
foreach (var label in labels)
{
}
Alternatively, if each object in the JSON is the same, you could try create the dictionary as the type you need. So you serializing becomes
var labels = new JavaScriptSerializer()
.Deserialize<Dictionary<string, List<object>>>(json);
A possible solution:
static void Main(string[] args) {
string json = #"{'labels':[{'id':'1','descrizione':'Etichetta interna','tipo':'0','template_file':'et_int.txt'},{'id':'2','descrizione':'Etichetta esterna','tipo':'1','template_file':'et_ext.txt'}],'0':200,'error':false,'status':200}";
var labels = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(json);
IEnumerable inner_labels = labels["labels"] as IEnumerable;
if (inner_labels != null) {
foreach (var outer in inner_labels) {
Console.WriteLine(outer);
}
}
}
Otherwise, you can create a class with deserialization information and instruct the deserializer to deserialize your json string to that type:
using System;
using System.Collections.Generic;
using System.Web.Script.Serialization;
using System.Xml.Serialization;
[Serializable]
public class JsonData {
[XmlElement("labels")]
public List<JsonLabel> labels { get; set; }
[XmlElement("0")]
public int zero { get; set; }
[XmlElement("error")]
public bool error { get; set; }
[XmlElement("status")]
public int status { get; set; }
}
[Serializable]
public class JsonLabel {
[XmlElement("id")]
public int id { get; set; }
[XmlElement("descrizione")]
public string descrizione { get; set; }
[XmlElement("tipo")]
public int tipo { get; set; }
[XmlElement("template_file")]
public string template_file { get; set; }
}
class Program {
static void Main(string[] args) {
string json = #"your json string here...";
var jsonData = new JavaScriptSerializer().Deserialize<JsonData>(json);
foreach (var label in jsonData.labels) {
Console.WriteLine(label.id);
}
}
}
Could you please try below snippet?
It might be help you.
foreach (var item in labels["labels"] as ArrayList)
{
Console.Write(item);
}