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

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.

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,
});
}

Deserializing JSON to a class containing a dynamic property with System.Text.Json

The objective is to deserialize a JSON response to a wrapper response class containing a dynamic part, using the new System.Text.Json library from NET Core 3.
That is
{
"fixedProperty": "Hello",
"dynamicProperty": {
"attributeOne": "One",
"attributeTwo": "Two",
}
}
to
public class MyResponseClass
{
public string FixedProperty { get; set; }
public dynamic DynamicProperty { get; set; }
}
// Where the dynamic property is one of the classes.
// (MyDataClassOne in the particular JSON example above)
public class MyDataClassOne
{
public string AttributeOne { get; set; }
public string AttributeTwo { get; set; }
}
public class MyDataClassTwo
{
public string AttributeThree { get; set; }
public string AttributeFour { get; set; }
}
...
The type of the dynamic property in the response is always known in advance (depends on the request), and is one of, say, three different classes.
Could not figure out a clean way to do so, except for not having one wrapper class with a dynamic property but multiple distinct response classes for each of the cases (which obviously works fine but is not the desired solution).
EDIT:
The solution was to use a generic.
How about something like this?
var myResponseClass = new MyResponseClass();
dynamic myClass = JsonSerializer.Deserialize<ExpandoObject>("{\"fixedProperty\":\"Hello\",\"dynamicProperty\": {\"attributeOne\":\"One\",\"attributeTwo\":\"Two\"}}");
dynamic myProperty = JsonSerializer.Deserialize<ExpandoObject>(myClass.dynamicProperty.ToString());
myResponseClass.FixedProperty = myClass.fixedProperty.ToString();
myResponseClass.DynamicProperty = myProperty;
Since the type of the dynamic property in the response is always known in advance (depends on the request), you can use a generic root object:
public class MyResponseClass<T>
{
public string FixedProperty { get; set; }
public T DynamicProperty { get; set; }
}
And then declare T to be whatever known concrete class is required, e.g.
var root = JsonSerializer.Deserialize<MyResponseClass<MyDataClassOne>>(responseString);
var fixedProperty = root.fixedProperty;
var attributeOne = root.DynamicProperty?.AttributeOne;

Dynamic/Expando and JSON

There's a lot of Qs on this, but I need a solution without JSON.Net, etc. - I must use the canned stuff in Asp.Net MVC.
How can I serialize a POCO with a dynamic property - and get all the static properties, too? What I found was the dynamic only, or the static type which is easy.
e.g.
public class ReturnThisClassAsJSON {
public int Id {get; set; }
public string Name { get; set; }
public ContainedClass ContainedContents { get; set; }
}
public class ContainedClass {
public int Order { get; set; }
public string Label { get; set; }
public dynamic DynamicInfo { get; set; }
public List<dynamic> DynamicList { get; set }
}
My own answer:
I replaced the dynamic from the DynamicInfo and DynamicList from the ContainedClass with static types.
With the dynamic, I had 1 of 2 choices. Either serialize the dynamic to a string in its own serialization call using above SO question 5156664. (Which left me with the rest of the class I also wanted serialized and merged with it, thus this question). Or, incur this error:
"A circular reference was detected while serializing an object of type 'System.Reflection .RuntimeModule' ".
when attempting a single serialization call on the ContainedClass.
So, I transferred the dynamics into static-typed classes:
public class ColumnValue
{
public string Name { get; set; }
public string Value { get; set; }
}
public class DynamicRow
{
public List<ColumnValue> ColumnValue { get; set; }
}
and, change ContainedClass to this:
public class ContainedClass
{
public List<ColumnValue> DynamicInfo { get; set; }
public List<DynamicRow> Data { get; set; }
}
And, it serializes using out-of-the-box Asp.Net MVC:
return Json(ReturnThisClassAsJSON, JsonRequestBehaviour.AllowGet);

How to store and sort two types of classes in List<>?

I have two classes:
public class SavedQuote
{
public int ID { get; set; }
public string Text { get; set; }
public string Context { get; set; }
public string URL { get; set; }
public string Comment { get; set; }
public string WhereToSearch { get; set; }
public DateTime DateOfAdding { get; set; }
public string OwnerName { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
public class NoteOnSite
{
public int ID { get; set; }
public string URL { get; set; }
public DateTime DateOfAdding { get; set; }
public string OwnerName { get; set; }
public string Comment { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
I have also two lists: one that represents some "SavedQuotes" and one that has some "NoteOnSites". I need to sort data from those Lists by DateOfAdding and display them in one table on my webiste.
The problem is: I (probably) can't save objects with two different classes in one List<> (I need to do this to sort those objects). What do you advise me to do? How would you solve this problem?
I (probably) can't save objects with two different classes in one List<>
You can, as long as object have a common base class. In C#, all objects have a common base class System.Object, which is enough to store objects of entirely different types in a single list.
A heavyweight approach would be to put a common interface on the objects that you wish to sort:
public interface IWithDate {
public DateTime DateOfAdding { get; set; }
}
public class SavedQuote : IWithDate {
...
}
public class NoteOnSite : IWithDate {
...
}
...
var mixedList = new List<IWithDate>();
However, this may introduce more structure than you wish: making the classes related to each other through a common interface is too much, if all you need is to sort objects of these classes together.
If you wish to sort the objects on a commonly named property without adding any static structure around your classes, you can make a list of dynamic objects, and use DateOfAdding directly:
var mixedList = new List<dynamic>();
mixedList.AddRange(quotes);
mixedList.AddRange(notes);
mixedList.Sort((a, b)=>a.DateOfAdding.CompareTo(b.DateOfAdding));
Try a little Linq using JOIN
List<SavedQuote> savedQuotes = new List<SavedQuote>();
List<NoteOnSite> noteOnSites = new List<NoteOnSite>();
var results = from savedQuote in savedQuotes.OrderBy(x => x.DateOfAdding)
join noteOnSite in noteOnSites.OrderBy(x => x.DateOfAdding)
on savedQuote.ID equals noteOnSite.ID
select new { saved = savedQuotes, note = noteOnSites };​

Not sure how to implement IEnumerable on this class

Continuing to develop the API I have mentioned in previous posts, I have come across the following situation:
I need to be able to access a list of responses returned by the
webservice.
Problem is I am unsure how to implement IEnumerable on this class.
...
public class ResponseBodyResponse
{
public ResponseListResponse ResponseList { get; set; }
public class ResponseListResponse
{
public ResponseInfoResponse ResponseInfo { get; set; }
public class ResponseInfoResponse
{
public string RequestId { get; set; }
public string RequestType { get; set; }
public DateTime RequestDate { get; set; }
public string RequestStatus { get; set; }
public string Error { get; set; }
public string Memo { get; set; }
}
public ResponseListResponse()
{
ResponseInfo = new ResponseInfoResponse();
}
}
public ResponseBodyResponse()
{
ResponseList = new ResponseListResponse();
}
...
Before anyone asks I did get a copy of the xsd files, however generating the classes using xsd.exe resulted in a ridiculous mishmash of files with conflicting class names causing over 1000 ambiguous naming errors.
You really should return a concrete collection such as a list or an array from a web service instead of an implementation IEnumerable<T>, even though lists and arrays (and other colections) do implement it. Its not IEnumerable<T> that is the key for the serialization.
Aside, the nested class structure makes your code hard to consume.
Since I'm not sure of your intent with your above code, here is an example
public class Road
{
public Car[] Cars { get; set; } // this can be also `List<Car>`
}
public class Car
{
// stuff
}

Categories