C# JsonConvertDeserialization returning null values - c#

I am trying to understand why I am getting null values for the following:
Json:
{
"IdentityService": {
"IdentityTtlInSeconds": "90",
"LookupDelayInMillis": "3000"
}
}
Class:
public class IdentityService
{
public string IdentityTtlInSeconds { get; set; }
public string LookupDelayInMillis { get; set; }
}
Called with :
_identityService = JsonConvert.DeserializeObject<IdentityService>(itemAsString);
The class is instantiated but the values for IdentityTtlInSeconds and LookupDelayInMillis are null. I cannot see why they should be

You need one more class - an object which has one property called IdentityService:
public class RootObject
{
public IdentityService IdentityService { get; set; }
}
You need this class because JSON that you have has one property called IdentityService, and this object has two properties, called IdentityTtlInSeconds and LookupDelayInMillis. If you are using a default serializer your classes need to reflect the structure that you have in your JSON string.
And now you can use it to deserialize your string:
var rootObject = JsonConvert.DeserializeObject<RootObject>(itemAsString);
_identityService = rootObject.IdentityService;

Related

How to automatically include type information when serializing?

Is it possible to specify that I always want type-information in the json object when serializing a property in an class?
(Ideally with Newtonsoft).
I'm thinking something like this:
public abstract class Value {...}
public class BigValue : Value {...}
public class SmallValue : Value {...}
public class ValueContainer
{
[JsonSetting(TypenameHandling = TypenameHandling.All)] // <--- Something like this?
public Value TheValue { get; set; }
}
I am aware that I could specify this behavior when doing the parsing with a custom converter.
But I want to include the typeinformation every time objects of this type is serialized, without manually having to specify which serialization options to use.
Newtonsoft.Json's JsonPropertyAttribute has TypeNameHandling property which you can set:
public class Root
{
[JsonProperty(TypeNameHandling = TypeNameHandling.All)]
public Base Prop { get; set; }
}
public class Base
{
public int IntProp { get; set; }
}
public class Child:Base
{
}
// Example:
var result = JsonConvert.SerializeObject(new Root
{
Prop = new Child()
});
Console.WriteLine(result); // prints {"Prop":{"$type":"SOAnswers.TestTypeNamehandling+Child, SOAnswers","IntProp":0}}

Deserialize collection type virtual data member

I'm trying to deserialize json to RequestWithDefault object
JSON:
{
"fields":["f1","f2"]
}
My simple class diagram:
[DataContext]
public abstract class BaseRequest
{
[DataMember]
public virtual List<string> Fields { get; set; }
}
[DataContext]
public class RequestWithDefault : BaseRequest
{
[DataMember]
public override List<string> Fields {get; set; } = new List<string> {"test"}
}
After deserializing json to RequestWithDefault object Fields property contains ["test", "f1", "f1"]. I want to be sure that this default values are applied only in case when Fields were not specified in request, or was specified as null. How I can do this? I tried with [OnDeserializing] attribute but without success. Result is the same
According to this:
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/collection-types-in-data-contracts
Looks like during deserialization DataContractSerializer calling Add method from collection. That's why I have also default value and rest of items are added. When I will replace List<string> to string[] everything works fine.
It seems WCF serialization never use setter to set the value of the DataMember with type of collection, but use Add instead. Because of this, the only way to check whether the fields has any value is to check after it has been deserialized (not while deserializing).
[DataContext]
public abstract class BaseRequest
{
[DataMember]
public virtual List<string> Fields { get; set; }
}
[DataContext]
public class RequestWithDefault : BaseRequest
{
[System.Runtime.Serialization.OnDeserialized]
void OnDeserialized(System.Runtime.Serialization.StreamingContext c)
{
if (Fields == null
|| Fields.Count < 1)
{
Fields = new List<string> { "test" };
}
}
}

Deserialization of JSON in c#

I have the following JSON
{
"employee" : {
"property1" : "value1",
"property2" : "value2",
//...
}
}
to a class like
public class employee
{
public string property1{get;set;}
public string property2{get;set;}
//...
}
In my JSON if I need to add property3 then I need to make changes in my class too.
How can I deserialize to a class even though if I change my JSON(adding another property like property3).
The serialize/De-serialize techniques like newtonsoft.json is tightly coupled with the Class.
Is there a better way/tool to deserialize these kind of JSON in portable class in c#?
Newtonsoft is not tightly coupled with strong types. You can deserialize the dynamic types too. See the similar question here (How to read the Json data without knowing the Key value)
You can try .net's JavaScriptSerializer (System.Web.Script.Serialization.JavaScriptSerializer). If some field is added or removed it deserializes object normally.
namespace ConsoleApplication8
{
public class Person
{
public int PersonID { get; set; }
//public string Name { get; set; }
public bool Registered { get; set; }
public string s1 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var s = "{\"PersonID\":1,\"Name\":\"Name1\",\"Registered\":true}";
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var o = serializer.Deserialize<Person>(s);
;
}
}
}
If we can use " Dictionary<string,string> employee" the above json can be deserilized.

Accessing subfields in json file

Assuming I have a JSON file with the following structure. How I can access the names of the properties in the metadata field.
{
"mappings": {
"basedoc_12kja": {
"properties": {
"created": {
"type": "date",
"format": "dateOptionalTime"
},
"customerID": {
"type": "string"
},
"deleted": {
"type": "boolean"
},
"documentID": {
"type": "string"
},
"id": {
"type": "string"
},
"metadata": {
"properties": {
"Cert": {
"type": "string"
},
"Exp_date": {
"format": "dateOptionalTime"
},
}
}
}
}
}
}
Mappings is an array of documents, each subfield of mappings has a different code. I want to obtain the metadata fields of each document to find out which metadata fields are common between them.
I haven't been able to instantiate this documents.
var response = esReader.GetIndicesMapping();
foreach (var mapping in response.Response.Values)
{
// Parse JSON into dynamic object, convenient!
dynamic results = JObject.Parse(mapping);
List<DocumentType> deserializedObject = JsonConvert.DeserializeObject<List<DocumentType>>(mapping);
}
Exception
{"Cannot deserialize the current JSON object (e.g. {\"name\":\"value\"}) into type 'System.Collections.Generic.List`1[DocumentType]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.\r\nTo fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.\r\nPath 'mappings', line 2, position 14."}
The desire result is to obtain the name of Cert and Exp_date fields
EDIT
public class DocumentType
{
public string Id { set { DocumentID = value; } get { return DocumentID; } }
public string DocumentID { set; get; }
public DateTime Created { set; get; }
.
.
.
public Dictionary<string, object> Metadata { set; get; }
}
The problem here is that your data structure does not match the JSON:
There are no arrays in the JSON. So there is no way you will be able to deserialize that into a C# List.
The "DocumentType" class doesn't match the JSON at all. The class has properties Created, CustomerID, and Deleted which are things like DateTime and string. But the JSON doesn't have those as DateTime or string. They are objects with subproperties named "type" and "format." The property "Metadata" isn't a dictionary: it is an object with a single property named "properties" which should probably be a dictionary.
The case doesn't match.
Don't do that weird thing with Id and DocumentId. The class should match the JSON exactly and literally. No business logic hidden in properties.
The root object has a property called "mappings" so you will need to drill-down before you get to the documents.
Once you successfully get the document, you will need to drill down to the property named "properties" to get to the fields you are interested in.
I suspect there could be multiple documents, and that the "mappings" property contains a list of those documents, where the property names are dynamic and correspond to the name of the document. It is entirely plausible to handle that but not using a deserialization + List approach.
I see 3 approaches here:
Fix the JSON. Not sure if this is possible in your case. If so, start by making mappings hold an array instead of having each document be a property named by the document name.
Fix the deserialization code to match the JSON document. json2csharp did an excellent job so start with that. It just doesn't know that "mappings" is really a Dictionary, not just a thing with a property named "basedoc12_kja."
Don't deserialize it at all. Just query for the metadata. take a look at http://www.newtonsoft.com/json/help/html/QueryingLINQtoJSON.htm which shows several ways to query JSON using JObject properties and LINQ.
Option 1
Example of a slightly cleaned-up JSON if you go that route:
{
"mappings": [
{
"name"" : "basedoc_12kja",
"properties": {
""created": "20150522",
etc.
},
Notice "mappings" is an array and the name became a property of the document. Now you can make a List<> or use JArray. Even better is to get rid of the unused stuff at the top, like this:
[
{
"name" : "basedoc_12kja",
"properties": {
"created"": "20150522",
etc.
},
]
Now it is just an array with no "mappings" at all.
** Option 2 **
Here is code that will do this via deserialization. There are two parts. Step one is to use what json2charp produced. I'll include that here for reference:
public class Created
{
public string type { get; set; }
public string format { get; set; }
}
public class CustomerID
{
public string type { get; set; }
}
public class Deleted
{
public string type { get; set; }
}
public class DocumentID
{
public string type { get; set; }
}
public class Id
{
public string type { get; set; }
}
public class Cert
{
public string type { get; set; }
}
public class ExpDate
{
public string format { get; set; }
}
public class Properties2
{
public Cert Cert { get; set; }
public ExpDate Exp_date { get; set; }
}
public class Metadata
{
public Properties2 properties { get; set; }
}
public class Properties
{
public Created created { get; set; }
public CustomerID customerID { get; set; }
public Deleted deleted { get; set; }
public DocumentID documentID { get; set; }
public Id id { get; set; }
public Metadata metadata { get; set; }
}
public class Basedoc12kja
{
public Properties properties { get; set; }
}
public class Mappings
{
public Basedoc12kja basedoc_12kja { get; set; }
}
public class RootObject
{
public Mappings mappings { get; set; }
}
Then, rename Basedoc12kja to DocumentType, and change RootObject to hold a dictionary. You get this:
public class DocumentType
{
public Properties properties { get; set; }
}
public class RootObject
{
public Dictionary<string, DocumentType> mappings { get; set; }
}
And if you want to get to properties other than just Cert and Exp_date then change Metadata to this:
public class Metadata
{
public Dictionary<string,object> properties { get; set; }
}
Now that can deserialize your document:
JObject results = JObject.Parse(mapping);
RootObject ro = results.ToObject<RootObject>()
You can enumerate through the mappings and get to the properties. They are still messy because of the JSON structure, but you can at least get there.
I hope this helps!
What you have here is a hierarchical dictionary of named properties, where each property can have a type, a format, and possibly a nested dictionary of named child properties -- metadata in your case. You can represent this with the following data model:
[DataContract]
public class PropertyData
{
[DataMember(Name="type", EmitDefaultValue=false)]
public string Type { get; set; }
[DataMember(Name = "format", EmitDefaultValue = false)]
public string Format { get; set; }
[DataMember(Name = "properties", EmitDefaultValue = false)]
public Dictionary<string, PropertyData> Properties { get; set; }
}
[DataContract]
public class Mappings
{
[DataMember(Name = "mappings", EmitDefaultValue = false)]
public Dictionary<string, PropertyData> DocumentMappings { get; set; }
}
(This data model doesn't capture the fact that a given property (probably) can only be a simple type or a complex type with nested properties - but not both. It would seem to suffice for your needs however.)
Then, given the JSON above, you would read it in and convert it to a dictionary of document name to metadata property name as follows:
var mappings = JsonConvert.DeserializeObject<Mappings>(json);
Debug.WriteLine(JsonConvert.SerializeObject(mappings, Formatting.Indented)); // Verify that all was read in.
var metadataNames = mappings.DocumentMappings.ToDictionary(doc => doc.Key, doc => doc.Value.Properties["metadata"].Properties.Select(p => p.Key).ToList());
Debug.WriteLine(JsonConvert.SerializeObject(metadataNames, Formatting.Indented)); // Inspect the resulting mapping table.
And the result is the dictionary of metadata names you want:
{
"basedoc_12kja": [
"Cert",
"Exp_date"
]
}
If you are concerned that the nested metadata might be missing sometimes and so generate NullReferenceExceptions in the query above, you can add null checks as follows:
// Extension methods to query or walk through nested properties, avoiding null reference exceptions when properties are missing
public static class PropertyDataExtensions
{
public static IEnumerable<KeyValuePair<string, PropertyData>> GetProperties(this PropertyData data)
{
if (data == null || data.Properties == null)
return Enumerable.Empty<KeyValuePair<string, PropertyData>>();
return data.Properties;
}
public static PropertyData GetProperty(this PropertyData data, string name)
{
if (data == null || data.Properties == null)
return null;
PropertyData child;
if (!data.Properties.TryGetValue(name, out child))
return null;
return child;
}
}
And then:
var metadataNamesSafe = mappings.DocumentMappings.ToDictionary(doc => doc.Key, doc => doc.Value.GetProperty("metadata").GetProperties().Select(p => p.Key).ToList());

Mongo C#Driver:Deserialize BsonArray

I have a document in mongodb that is structured similar to this:
{
"_id":xxxxx,
"business":[{
"subBusiness":[{
"subBusinessName":"Abusiness",
"a":"aaaa"
},{
"subBusinessName":"Bbusiness",
"b":"bbbbb",
"c":"ccccc"
}]
}]
}
how to make a mapping class to serialize this document?
I also have a class defined to represent dimensions (the sub document from above)
class STObject{
[BsonId]
public ObjectId id{get;set;}
[BsonElement("business")]
public List<Business> BusinessList{get;set;}
}
class Business {
[BsonElement("subBusiness")]
public List<SubBusiness> SubBuiness { get; set; }
}
[BsonDiscriminator(RootClass = true)]
[BsonKnownTypes(typeof(CSSubBusiness),typeof(ApproSubBusiness))]
public class SubBusiness {
[BsonElement("subBusinessName")]
public string SubBusinessName{get;set;}
}
public class AsubBusiness:SubBusiness{
[BsonElement("a")]
public string A{get;set;}
}
public class BsubBusiness:SubBusiness{
[BsonElement("b")]
public string B{get;set;}
[BsonElement("c")]
public string C{get;set;}
}
how to query element "b" in class STObject?
In order to deserialize class hierarchy, document should contain type discriminator field, which tells which type of subclass should be instantiated. By default this field has name _t. But if you already have documents with schema as above and can't change it, then you should override discriminator convention which is used by Mongo.
Looks like you can use subBusinessName field as type discriminator for sub business types. In order to do that, you should remove this field from base type:
[BsonDiscriminator(RootClass = true)]
[BsonKnownTypes(typeof(AsubBusiness), typeof(BsubBusiness))] // btw check types
public class SubBusiness
{
}
And you should provide discriminator values for subtypes:
[BsonDiscriminator("Abusiness")] // provide discriminator value here
public class AsubBusiness : SubBusiness
{
[BsonElement("a")]
public string A { get; set; }
}
[BsonDiscriminator("Bbusiness")]
public class BsubBusiness : SubBusiness
{
[BsonElement("b")]
public string B { get; set; }
[BsonElement("c")]
public string C { get; set; }
}
And final step - create custom convention to make mongo look on this discriminator field for instantiating correct sub class type:
public class SubBusinessDiscriminatorConvention : IDiscriminatorConvention
{
public string ElementName
{
get { return "subBusinessName"; }
}
public Type GetActualType(BsonReader bsonReader, Type nominalType)
{
var bookmark = bsonReader.GetBookmark();
bsonReader.ReadStartDocument();
var actualType = nominalType;
if (bsonReader.FindElement(ElementName))
{
var discriminator = (BsonValue)BsonValueSerializer.Instance.Deserialize(bsonReader, typeof(BsonValue), null);
actualType = BsonSerializer.LookupActualType(nominalType, discriminator);
}
bsonReader.ReturnToBookmark(bookmark);
return actualType;
}
public BsonValue GetDiscriminator(Type nominalType, Type actualType)
{
var classMap = BsonClassMap.LookupClassMap(actualType);
return classMap.Discriminator;
}
}
Now set this convention for your base type serialization:
BsonSerializer.RegisterDiscriminatorConvention(typeof(SubBusiness),
new SubBusinessDiscriminatorConvention());
And you can serialize and deserialize documents in your exact format.
UPDATE: Querying:
var collection = test.GetCollection<STObject>("collectionName");
var sto = collection.FindOne(Query.EQ("_id", new ObjectId("xxxxx")));
var businessList = sto.BusinessList.FirstOrDefault();
var bsub = businessList.SubBuiness.OfType<BsubBusiness>().FirstOrDefault();
var b = bsub.B; // returns bbbbb

Categories