I'm creating objects to store the JSON data I will be receiving and cannot figure out the right way to structure the objects. Basically, I can be receiving two different objects that only have differences in the body, so I wish to make a base class.
public class SampleBase
{
public string url { get; set; }
public string resource { get; set; }
public string Body body { get; set; }
}
This is an example of the base, with the Body object declared below
public abstract class Body{ }
I then have two separate files for the versions of the base object I can receive, with an example below:
public class SampleObject : SampleBase
{
public class Body
{
public string bodyproperty { get; set; }
}
}
I am doing this just to be efficient with the classes since they share some properties. The SampleBase class will never be called, instead incoming json will be deserialized into SampleObject. Is this best practice?
Edit: Going by this example, the JSON is received as
{
"url": "xxxxxxxxxx",
"resource": "xxxxxxx",
"body": {
"bodyproperty": "xxxx",
}
}
Your class structure can heavily depend on your choice of serializer. For example, the DataContractJsonSerializer can technically handle inherited classes, but it does it in somewhat of a clunky way. You need to define all the known inheritors of your base type on the base type.
Personally, I would use composition rather than inheritance in your case. Here's an example using the DataContractJsonSerializer:
[DataContract]
public class Wrapper<T> where T : Body
{
[DataMember(Name = "url")]
public string Url { get; set; }
[DataMember(Name = "resource")]
public string Resource { get; set; }
[DataMember(Name = "body")]
public string T Body { get; set; }
}
[DataContract]
public class Body
{
[DataMember(Name = "bodyproperty")]
public string BodyProperty { get; set; }
}
Then you'd use the class like any other generic.
Wrapper<Body> obj = new Wrapper<Body>();
Edit: Since this is a MVC application, you'll likely be working with the JavascriptSerializer. The DataContract and DataMember can be ignored but the structure of the classes is still relevant.
var serializer = new JavaScriptSerializer();
var data = serializer.Deserialize<Wrapper<Body>>(json);
Related
I have this generic class for my server responses:
[DataContract]
public class GenericResult<T>
{
public List<T> ListResult { get; set; }
public T Result { get; set; }
public string Message { get; set; }
}
and this GetAllBrandsTest method to return data to the client:
public async Task<GenericResult<Brand>> GetAllBrandsTest()
{
var result = await repo.GetAllAsync<Brand>();
return new GenericResult<Brand>()
{
ListResult = result.ToList(),
Message = "Success"
};
}
Everything is OK with this methods counterpart GetAllBrands:
public async Task<IList<Brand>> GetAllBrands()
{
return await repo.GetAllAsync<Brand>();
}
But when I call GetAllBrandsTest the result is empty.
[DataContract]
[KnownType(typeof(Brand))]
public class GenericResult<T>
{
[DataMember]
public List<Brand> ListResult { get; set; }
[DataMember]
public Brand Result { get; set; }
[DataMember]
public string Message { get; set; }
}
Any data type transferred between the server-side and the client-side should be explicitly specified how we serialize and deserialize it. Please use the DataContract attribute to specify the data structure the way how to serialize to XML so that the serialization and deserialization can work properly between the service-side and client-side. In addition, for unknown data types, please use the KnownType feature to specify the serialization method in advance.
[DataContract]
[KnownType(typeof(CircleType))]
[KnownType(typeof(TriangleType))]
public class CompanyLogo2
[DataMember]
private Shape ShapeOfLogo;
[DataMember]
private int ColorOfLogo;
}
[DataContract]
public class Shape { }
[DataContract(Name = "Circle")]
public class CircleType : Shape { }
[DataContract(Name = "Triangle")]
public class TriangleType : Shape { }
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/using-data-contracts
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/data-contract-known-types
We either decorate the class with both DataContract attribute and DataMember attribute or remove the DataContract attribute and DataMember attribute. Because the DataContract serializer will be used by default when the complex data type is without specifying any XML serializer.
I'm writing a console app to retrieve JSON data from a 3rd party API. I have no control over the API data structures or functionality.
Several of the calls I make will return multiple 'pages' of data. The data is a collection of objects of a certain type e.g. User.
I have created classes in my app to match the various data types from the API.
public class User
{
[JsonProperty("id")]
public int ID { get; set; }
[JsonProperty("first_name")]
public string FirstName { get; set; }
[JsonProperty("last_name")]
public string LastName { get; set; }
}
public class FooBar
{
[JsonProperty("foo")]
public string Foo { get; set; }
[JsonProperty("bar")]
public string Bar { get; set; }
}
The API response is always in the same format for these calls. While the actual object types in the "data" array will differ depending on what call has been made.
{
"paging":{"page":1},
"data":[{<object>}, {<object>}, {<object>},...]
}
I have created a class to try to deserialize these. The dynamic[] type for the Data property is for illustrative purposes and I am happy to change it if there is a better approach.
public class ApiResponseObject
{
[JsonProperty("paging")]
public Paging PagingInfo { get; set; }
[JsonProperty("data")]
public dynamic[] Data { get; set; }
}
And I would like to have the Data collection resolve to the appropriate type for the objects it contains. e.g.
string userJson = "{\"paging\":{\"page\":1},\"data\":[{\"id\":1,\"first_name\":\"Joe\",\"last_name\":\"Bloggs\"},{\"id\":2,\"first_name\":\"Jane\",\"last_name\":\"Doe\"}]}"; // json string would come from API
string foobarJson = "{\"paging\":{\"page\":1},\"data\":[{\"foo\":\"Lorem\",\"bar\":\"Ipsum\"},{\"foo\":\"Dolor\",\"bar\":\"Amet\"}]}";
var userResponse = JsonConvert.DeserializeObject<ApiResponseObject>(userJson);
var foobarResponse = JsonConvert.DeserializeObject<ApiResponseObject>(foobarJson);
The deserialization succeeds but the Data collection is of type JObject and cannot be cast into the correct type (User, FooBar).
I am trying to avoid having to write specific response object classes for each request if possible.
I will know what type of object I am expecting in the collection when I am requesting it so I could pass that type to the deserializer but I'm not clear on how to achieve that in this particular scenario.
Something like the below psuedo code would be ideal.
var userResponse = JsonConvert.DeserializeObject<ApiResponseObject<User>>(userJson);
Thanks for your help!
You can use the generic type T, like this :
public class ApiResponseObject<T>
{
[JsonProperty("paging")]
public Paging PagingInfo { get; set; }
[JsonProperty("data")]
public T[] Data { get; set; }
}
I'm having trouble understanding serialization of one of my objects.
Scenario:
I'm receiving data from a service which is in a given format. I want to take the data in as is. Due to naming conventions I have to use DataMember properties to match the incoming data to properly named class properties. I use System.Runtime.Serialization for this. Example:
[DataContract]
public class IncomingData
{
[DataMember(Name = "$Filename")]
public string Filename { get; set; }
}
This works fine and the data is mapped to the internal property name.
At some point I have to serialize this object again and I naively thought that it would serialize to the internal property name e.g.
{ "Filename":"C:\temp\lala.txt"}
however that is not true and the original propertyname "$Filename" is used instead. I assume this is because DataMember works both ways.
Is there an elegant way to have this object serialize to the propertynames and ignore DataMember? I tried if using a different serialization library works (JSON.NET) but it also seems to follow DataMember.
Do I have to wrap this object to another to acchieve?
Thanks for any hints!
blu
You could define an interface for keeping these objects in sync...
public interface IData
{
string Filename { get; set; }
}
// deserialize me.
[DataContract]
public class IncomingData : IData
{
[DataMember(Name = "$Filename")]
public string Filename { get; set; }
}
// serialize me.
public class Data : IData
{
public string Filename { get; set; }
}
...or you could use virtual properties and override them with the serialization attributes...
// serialize me.
class Data
{
public virtual string Filename { get; set; }
}
// deserialize me.
[DataContract]
class IncomingData : Data
{
[DataMember(Name = "$Filename")]
public override string Filename { get => base.Filename; set => base.Filename = value; }
}
...both of these methods would require the use of a mapper like AutoMapper to clone the IncomingData into the attribute-free Data class...
mapper.Map<IncomingData, Data>(user);
...so I appreciate this feels less than ideal.
There are probably 500 questions like this on SO, and a million websites out there all offering tidbits of information - but I just can't see the wood for the trees. This seems like it should be embarrassingly simple to do, but I just can't make it work.
I have a WCF webservice that returns a serialized JSON object:
[OperationContract(Name = "PeopleData"), WebGet(BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "people/{subset}", ResponseFormat = WebMessageFormat.Json)]
PeopleObject GetPeople(string subset);
This works - if I hit that URI from a browser, GetPeople is invoked and returns a JSON-serialized PeopleObject (actual data values redacted for privacy here):
{"HashValue":"XXXXX","People":[{"EmailAddress":"XXXXX","EmployeeID":99999,"Gender":"X","JobTitle":"XXXXX","Office":"","PreferredName":"XXXXX","Surname":"XXXXX","WorkExtensionNumber":"XXXXX","WorkPhoneNumber":"XXXXX","Department":"XXXXX","DeskNumber":"XXXXX","EmploymentClassification":"XXXXX","InternationalExtensionNumber":"XXXXX","IsFirstAider":false,"Languages":[{"LanguageID":9,"LanguageSkillID":9},{"LanguageID":9,"LanguageSkillID":9}],"QualificationInitials":"XXXXX","QualificationTitle":"XXXXX","Secretaries":null,"WorkMobilePhoneNumber":"XXXXX"}],"RecordCount":"1","SizeBytes":"12345"}
In this example the PeopleObject payload contains just one Person object in the collection, but could contain many (depending on the parameter supplied in /{subset}.
Here is the class hierarchy for PeopleObject - it's a top-level container holding some metadata about the payload, and a List<> of Person objects. Those objects in turn have a bunch of simple type attributes, plus two further nested List<> of Language and Secretary objects (which may or may not be populated):
[DataContract]
public class PeopleObject
{
[DataMember]
public string HashValue { get; set; }
[DataMember]
public List<Person> People { get; set; }
[DataMember]
public string RecordCount { get; set; }
[DataMember]
public string SizeBytes { get; set; }
}
[DataContract]
public class Person
{
[DataMember]
public string EmailAddress { get; set; }
// <-- snip - lots of fields like this, no point listing them all here
[DataMember]
public bool IsFirstAider { get; set; }
[DataMember]
public List<Language> Languages { get; set; }
[DataMember]
public List<Secretary> Secretaries { get; set; }
}
[DataContract]
public class Language
{
[DataMember]
public int LanguageID { get; set; }
[DataMember]
public int LanguageSkillID { get; set; }
}
[DataContract]
public class Secretary
{
[DataMember]
public int EmployeeID { get; set; }
[DataMember]
public char FirstSurnameLetter { get; set; }
}
So far, so good - WCF responds with a JSON structure that contains all the fields and their contents. Now to deserialize that structure in a client application (using the same class hierarchy definitions):
// I have a little helper-class to manage the WCF request and return a Stream
using (Stream response = wcfHelper.GetRequestResponseStream(MY_WCF_URI))
{
// This is debug code to prove the response arrives as expected - it does
//StreamReader sr = new StreamReader(response);
//Console.WriteLine("\nResponse:\n{0}", sr.ReadToEnd());
// Deserialise the response
DataContractJsonSerializer dc = new DataContractJsonSerializer(typeof(PeopleObject));
PeopleObject p = (PeopleObject)dc.ReadObject(response);
// The object shows 1 record (in the example) but nothing in the List<>
Console.WriteLine("\nDeserialized records: '{0}' [{1}]", p.RecordCount, p.People.Count);
}
So this correctly deserializes the container object, giving me the record count, hash value, and payload size in bytes. The object does also have a List<> of Person objects, but it's null - the content from the JSON response hasn't successfully rehydrated the List<> by creating and adding a Person object.
What am I missing? My understanding was that this rehydration of the C# object hierarchy from the JSON structure should happen automatically, so either that's not the case (and I need to write some code to make it happen) or it is, but I've missed something obvious.
I haven't done what you are doing before, but judging by the documentation, I'd assume the following would work:
List<Type> types = new List<Type>();
types.Add(typeof(Person));
types.Add(typeof(Language));
types.Add(typeof(Secretary));
DataContractJsonSerializer dc = new DataContractJsonSerializer(typeof(PeopleObject), types);
PeopleObject p = (PeopleObject)dc.ReadObject(response);
You basically need to tell the Serializer all the types it may encounter while serializing/deserializing your object.
Consider the following JSON:
{
"Code": 2,
"Body": {
"Dynamic-Key": {
"Key1": "Val1",
"Key2": "Val2"
}
}
}
Defining the following classes structure:
[DataContract]
class JsonResponse
{
[DataMember]
public string Code { get; set; }
[DataMember]
public JsonResponseBody Body { get; set; }
}
[DataContract]
class JsonResponseBody
{
[DataMember(Name = "Dynamic-Key")]
public DynamicKeyData Data { get; set; }
}
[DataContract]
class DynamicKeyData
{
[DataMember]
public string Key1 { get; set; }
[DataMember]
public string Key2 { get; set; }
}
I can deserialize the given JSON with the following code:
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(JsonResponse));
JsonResponse responseData = serializer.ReadObject(responseStream) as JsonResponse;
However in my case the "Dynamic-Key" is not known and is defined by user input value.
What is the right way to handle this kind of JSON definition?
Considerations:
The JSON is of a third party Api, so changing it is not an option.
In my case I don't care about the Dynamic-Key name, so if there is an option to always map to one generic name it will do the job.
Thanks in advance.
Do you definitely need to use WCF? Otherwise I'd recommend looking at Json.NET. There's a number of extensibility mechanisms such as Contract Resolvers
Store the JSON in a string let suppose in strResult.
Then you can deserialize it into the JsonResponse object as follows:
JsonConvert is the class in Newtonsoft.Json.JsonConvert dll. You can use it as follows:
JsonResponse object= (JsonResponse)JsonConvert.DeserializeObject(strResult, typeof(JsonResponse));