.getJson call not working with objects that use inheritence - c#

first post... Normally I'm able to search and find answers to my problems, but this time I am not able to. I have an object that uses a bunch of other objects:
[DataContract]
public class CoolStuff
{
[DataMember]
public Field[] CoolField { get; set; }
public CoolStuff()
{
CoolField = SetCoolField();
}
private Field[] SetCoolField()
{
return new Field[]
{
new Field("Project Information", "ProjectInformation"),
new Field("Resource Information", "ResourceInformation"),
}
}
}
[DataContract]
public class Field
{
[DataMember]
public string Prompt { get; set; }
[DataMember]
public string Value { get; set; }
[DataMember]
public bool IsLocked { get; set; }
public Field(string prompt, string value = "n/a", bool isLocked = false)
{
Prompt = prompt;
Value = value;
IsLocked = isLocked;
}
}
I call my constructors from a service, and this works fine and dandy when I try to serialize it with $.getJSON(/Service.svc/coolstuff/' + id, loadCoolStuff);
The problem is, when I make my Field class inherit from another class, that .getJson call fails without really giving me a reason why.
[DataContract]
public class CoolStuff
{
[DataMember]
public FieldBase[] CoolField { get; set; }
public CoolStuff()
{
CoolField = SetCoolField();
}
private FieldBase[] SetCoolField()
{
return new FieldBase[]
{
new Field("Project Information", "ProjectInformation"),
new Field("Resource Information", "ResourceInformation"),
}
}
}
[DataContract]
public class FieldBase
{
}
[DataContract]
public class Field : FieldBase
{
[DataMember]
public string Prompt { get; set; }
[DataMember]
public string Value { get; set; }
[DataMember]
public bool IsLocked { get; set; }
public Field(string prompt, string value = "n/a", bool isLocked = false)
{
Prompt = prompt;
Value = value;
IsLocked = isLocked;
}
}
Can someone please explain why with ^ this code, my call to .getJSON fails? I'm really stuck here. Thanks so much!

OK, reading between the lines I've deduced you're using WCF for this web service. The use of DataContract attributes and url ending in .svc make that clear enough. That's relevant, because this issue seems particular to the way WCF serializes. As mentioned in my above comments, the same class structure has no problems in ASP.NET Web API using the default Newtonsoft JSON serializer.
What you need here is the KnownType attribute to properly define derived types for serialization.
This article offers some straight-forward examples. This one has a bit more detail on the concepts.
For the example you've provided, you'd need to decorate the FieldBase definition like this:
[DataContract]
[KnownType(typeof(Field))]
public class FieldBase
{
}
Note that this will add an extra field to your JSON, like "__type":"Field:#MyTestWebProject". You could just ignore that, or go hunting for ways to get rid of it if it bothers you. I wouldn't guarantee it's possible though.

Related

Send a limited/reduced class to frontend

What I want
I want to send a limited/reduced class/object to frontend (as JSON). I use .NET Core 5.
What I have
I have a model class like this:
namespace Tasks.Models
{
public class Resources
{
public Guid Id { get; set; }
public string Type { get; set; }
public string Url { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime? Createdon { get; set; }
public Guid Userid { get; set; }
public Guid Taskid { get; set; }
public int Clicked { get; set; }
public byte Active { get; set; }
+++ many more properties
}
}
Now depending on the which controller that calls this model I want to have different "kind" of models. So if the resource is file I maybe want the properties Id,Type,Name. But if the resource is URL I want Id, Url, Name.
I tried setting up a method that "initialized the fields I wanted, but that also returned all properties
public static Responses FileResponse()
{
var response = new Responses()
{
Id = new Guid(),
Name = "",
Type = "File",
};
return response;
}
Now, when I call the Resources class or this method I get all properties, and returning it to the view presents all properties, but mostly as null, because I only set the three fields in the method.
What is the recommended way of solving this?
If you want to remove the field if it's null instead of showing in json with null value.
public class Resources
{
public Guid Id { get; set; }
public string Type { get; set; }
// if null, dont show it in JSON output
[JsonIgnoreAttribute(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Url { get; set; }
// if null, dont show it in JSON output
[JsonIgnoreAttribute(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Name { get; set; }
public string Description { get; set; }
public DateTime? Createdon { get; set; }
public Guid Userid { get; set; }
public Guid Taskid { get; set; }
public int Clicked { get; set; }
public byte Active { get; set; }
}
PS: Fiddle https://dotnetfiddle.net/hiMAci
It is just limiting the Resource class I am not able to do
Yep, side effect of C# being strongly typed, with object X definitely having properties Y and Z. You need differently shaped objects - either full on classes or records - that name the reduced set of properties because the serializer is going to look a tthe object and ser every property it can find.
You could make a new class for every variation - quick and easy with records, and easy to pass around inside your C#:
public record FileThing(string Id, string Type, string Name);
//make a new one and return it
new FileThing(someResources.Id, someResources.Type, someResources.Name);
Or can consider using an anonymous type if you're literally looking to put a few properties into some json, down a socket to a consuming front end (I can't quite decide what you mean by "view" - it doesn't seem to be an MVC View) that only cares about a few props out of many
So if the resource is file I maybe want the properties Id,Type,Name. But if the resource is URL I want Id, Url, Name.
public ActionResult SomeControllerMethod(){
if(isFile)
return Ok(new { someResources.Id, someResources.Type, someResources.Name });
else if(isUrl)
return Ok(new { someResources.Id, someResources.Url, someResources.Name });
}
Anonymous types are a bit harder to work with because the compiler writes the class for you, so it's tricky to do things like declare return types from methods if the method is returning an AT.. But if you're using it as some fill-in all within one method, such as a "make this and serialize it", they work well..
I think your approach is not the right one here. I tend to follow more general OO guidelines in this situation (note, some consider these a bit dated, and other solutions exist. But they are still commonly used)
You write against an interface. So let's see what you want... A guid, type and name. All other deatils aren't important.
public interface IResourceDetails
{
public Guid Id { get; }
public string Name { get; }
public string Type { get; }
}
And you can have multiple of these interfaces.
You could then implement the interfaces per type. But I would probably combine them in a base class
public abstract class ResourceBase : IResourceDetails
{
public Guid Id { get; } = new ();
public string Name { get; init; }
public string Type { get; }
public ResourceBase(string type)
{
Type = type;
}
}
Each resource type would have it's own implementation
public class FileResource : ResourceBase
{
public FileResource() : base("File") { }
// File-specific properties.
public string Description { get; init; }
public DateTime? Createdon { get; init; }
}
The response method then could be made generic and look like this
public static IActionResult Response(IResourceDetails resource)
{
return Ok(new
{
resource.Id,
resource.Name,
resource.Type,
});
}

Using empty base class just for polymorphism purpose?

I have my ResponseDto which includes a simple string property named Answer.
public string Answer { get; set; }
Now, the requirement came such that I could either be getting an answer as a string, or as an array of int.
I decided to create two classes for this:
public class AnswerType {
public string Answer { get; set; }
}
public class OptionAnswerType {
public int[] AnswerOptionIds { get; set; }
}
I could serialize / deserialize it accordingly.
But to still keep a single response property type, I thought about creating an empty base class:
public class BaseAnswerType { }
public class AnswerType : BaseAnswerType {
public string Answer { get; set; }
}
public class OptionAnswerType : BaseAnswerType {
public Guid[] AnswerOptionIds { get; set; }
}
and change my property in ResponseDto to:
public BaseAnswerType Answer { get; set }
through which via run time, I would be returning either of the two classes.
Is this a bad approach? Any alternate would be greatly appreciated.

Web API - Return some fields from model

I'm having this model :
public class Quiz
{
public int Id { get; set; }
public string Title { get; set; }
public int CurrentQuestion { get; set; }
[JsonIgnore]
public virtual ICollection<Question> Questions { get; set; }
}
There is [JsonIgnore] which tells JSON Serializer to ignore this field(Questions). So,I'm having an action which returns serialized Quiz without Questions. I have to implement another action which will return all fields (Questions inclusive). How can I do this ? I need both actions.
It's very good practice not to return your domain models from and API. Better way is to create view model classes and return them instead.
So in your example you'd simply create:
public class QuizViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public int CurrentQuestion { get; set; }
}
and use it to return data from your API.
Obviously in some bigger classes it would be nightmare to crate the code copying all the properties, but don't worry - Automapper (http://automapper.org/) comes to rescue! :)
//Best put this line in app init code
Mapper.CrateMap<Quiz, QuizViewModel>();
//And in your API
var quiz = GetSomeQuiz();
return Mapper.Map<QuizViewModel>(quiz);
You then create another view model class with Questions field in the same way.
You need to slightly change your code as below although its very simple :)
[Serializable]
public class Quiz
{
public int Id { get; set; }
public string Title { get; set; }
public int CurrentQuestion { get; set; }
}
[Serializable]
public class QuizWithQuestions : Quiz
{
public ICollection<Question> Questions { get; set; }
}
Now when you want to include Collection of Questions as well use QuizWithQuestions class.
I had the similar problem. And my solution is:
By default I have DBContext with disable LazyLoading:
public EFDbContext()
:base("EFDbContext")
{
this.Configuration.ProxyCreationEnabled = true;
this.Configuration.LazyLoadingEnabled = false;
}
So, all navigation property (like Questions) will be NULL. Then in my WebAPIConfig I configure formatter to hide null values:
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling
= Newtonsoft.Json.ReferenceLoopHandling.Ignore;
config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling
= Newtonsoft.Json.NullValueHandling.Ignore;
When I need a result with all fields from model then I just turn on the LazyLoading in controller:
repository.SwitchLazyLoading(true);
The method in repository:
public void SwitchLazyLoading(bool value)
{
this.context.Configuration.LazyLoadingEnabled = value;
}
I do not use [JsonIgnore] I use only [IgnoreDataMember]
Please look at the Switches for LazyLoading with Repository pattern

class design: How do I create a class with nested objects?

I am currently developing a client library for connecting to Newegg using the documentation provided by Newegg and have a question on class design.
In working with various API's ( namely NetSuite and Amazon's MWS ) I come across classes that have are used like this:
recToFulfill.packageList = new ItemFulfillmentPackageList();
recToFulfill.packageList.package = new ItemFulfillmentPackage[ifitemlist.item.Length];
recToFulfill.packageList.package[i] = new ItemFulfillmentPackage();
recToFulfill.packageList.package[i].packageWeightSpecified = true;
recToFulfill.packageList.package[i].packageTrackingNumber = "trackingNumber";
The question I have is: How do I properly design the nested objects like above? I have never had to worry about this previously, so I am unsure on where to look, or start.
The bit I need to figure out looks like this ( taken from the API documentation provided):
<UpdateOrderStatusInfo>
<IsSuccess></IsSuccess>
<Result>
<OrderNumber></OrderNumber>
<SellerID></SellerID>
<OrderStatus></OrderStatus>
</Result>
</UpdateOrderStatusInfo>
All fields are type string, except order number which is an integer.
I have this currently:
public class UpdateOrderStatusInfo
{
public string IsSuccess { get; set; }
public int OrderNumber { get; set; }
public string SellerID { get; set; }
public string OrderStatus { get; set; }
}
But the returned XML Response has Results as a parent node which to me seems like it should be represented within the class itself. Would I just do this?
public UpdateOrderStatusInfo results {get; set;}
If so, where do the child nodes go?
What I need is to be able to say is something like:
UpdateOrderStatusInfo updateInfo = new UpdateOrderStatusInfo();
if(updateInfo.IsSuccess.Equals("true")
{
Console.WriteLine(updateInfo.Results.OrderStatus);
}
Any help, or advice on where to get this information is appreciated.
Easy breezy. If it has no children, it's a scalar property. If it does, it is its own class, and referenced in the parent class accordingly. If it repeats, it's a collection, and is referenced like a class (these are complex type, not primitives). Make sure you initialize them in your constructors).
class Program
{
static void Main(string[] args)
{
var myOrder = new UpdateOrderStatusInfo();
myOrder.IsSuccess = "true";
myOrder.OrderResult.OrderNumber = 1001;
myOrder.OrderResult.OrderStatus = "Pending";
myOrder.OrderResult.SellerID = "69";
}
}
public class UpdateOrderStatusInfo
{
public string IsSuccess { get; set; }
public Result OrderResult { get; set; }
public UpdateOrderStatusInfo()
{
OrderResult = new Result();
}
}
public class Result
{
public int OrderNumber { get; set; }
public string SellerID { get; set; }
public string OrderStatus { get; set; }
}
You need to define the Result as a separate class, called whatever you want, then add a Result property as that type. The Result class can be defined at the namespace level, or, if you are unlikely to use it anywhere else on its own, you can nest the class definition inside the UpdateOrderStatusInfo class:
public class UpdateOrderStatusInfo
{
public class UpdateOrderResult
{
public int OrderNumber { get; set; }
public string SellerID { get; set; }
public string OrderStatus { get; set; }
}
public UpdateOrderStatusInfo()
{
Result = new UpdateOrderResult();
}
public string IsSuccess { get; set; }
public UpdateOrderResult Result { get; set; }
}
The easy way is to use the xsd.exe tool.
The command xsd response.xml will generate the file response.xsd
The command xsd response.xsd /C will generate the file response.cs which contains the classes necessary to serialize/deserialize the xml posted.

more json c# issues

This is a continuation of a previous question of mine. The solution worked for what I was trying to do at the time but broke a whole lot of my code. I know it was bad practice not to make sure beforehand but yeah... you live and learn.
Anyhoo, here's the Q: an easy way to serialise c# objects
What I want to know is: is there any way to get the NewtonSoft Library to handle this stuff? If yes, how? If no, suggestions?
What i'm doing is chatting to a RoR3 app using json, now I cant deserialise the response. here's a little code:
The response i'm getting from RoR looks like:
[{"directory":{"created_at":"2011-07-20T22:29:38Z","drive_info":1,"id":15,"name":"New Drive","parent":0,"size":0,"updated_at":"2011-07-20T22:29:39Z","user":1}}]
I'm trying to deserialise it into a list of Directory objects using:
public static CompositeCollection deserialise<T>(string json)
{
CompositeCollection result = new CompositeCollection();
JArray arr = JArray.Parse(json);
foreach (JObject obj in arr)
{
result.Add(JsonConvert.DeserializeObject<T>(obj.First.First.ToString()));
}
return result;
}
and the relevant part of the directory class looks like:
// [Serializable]
// [DataContract]
public class Directory
{
// [DataMember]
public int id { get; set; }
// [DataMember]
public string name { get; set; }
// [DataMember]
public int parent { get; set; }
// [DataMember]
public int drive_info { get; set; }
// [DataMember]
public int size { get; set; }
// [DataMember]
public int user { get; set; }
// [DataMember]
public string state
{
get
{
/* if (parent == 0)
return _state.identify();
Directory parental;
return parental.state;*/
if (parental != null)
return parental.state;
return _state.identify();
}
set
{
_state = StateFactory.getState(value);
}
}
//[JsonIgnore]
blah...
I can deserialise most of the time by uncommenting [Serializable] (sometimes i get the following error: Object of type 'System.Int32' cannot be converted to type 'OffloadUI.Directory'. still investigating), and I can serialise by uncomenting [DataContract] and all instances of [DataMember]. What i need is something that will work in both directions.
Thanks to Zootius I found a useful guide. Here's what i did, works like a bought one:
[JsonObject(MemberSerialization.OptIn)]
public class Directory
{
[JsonProperty]
public int id { get; set; }
[JsonProperty]
public string name { get; set; }
[JsonProperty]
public int parent { get; set; }

Categories