Not able to use JIL Serializer ExcludeNulls Option - c#

I am not able to use JIL's Exclude Null option. Instead, I get an exception:
JIL.DeserializationException: 'Expected digit'
Below are code snippets.
public Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
{
if (context == null) throw new ArgumentNullException(nameof(context));
var request = context.HttpContext.Request; if (request.ContentLength == 0)
{
if (context.ModelType.GetTypeInfo().IsValueType)
return InputFormatterResult.SuccessAsync(Activator.CreateInstance(context.ModelType));
else return InputFormatterResult.SuccessAsync(null);
}
var encoding = Encoding.UTF8;//do we need to get this from the request im not sure yet
using (var reader = new StreamReader(context.HttpContext.Request.Body))
{
var model = Jil.JSON.Deserialize(reader, context.ModelType, Jil.Options.ExcludeNulls);
return InputFormatterResult.SuccessAsync(model);
}
}
1) Model type
public class PaymentTypeBORequest
{
public int pkId { get; set; }
public string description { get; set; }
public bool isSystem { get; set; }
public bool isActive { get; set; }
}
2) JSON String:
{
"pkId":null,
"description": "Adjustment",
"isSystem": true,
"isActive": true
}

The description for the excludeNulls option is:
whether or not to write object members whose value is null
(emphasis mine)
This suggests that it only affects serialisation operations and not deserialisation operations.
When serialising an object with excludeNulls set to true, Jil will not write properties to the JSON if they have null values. In your example, you're deserialising into a PaymentTypeBORequest object, which itself does not support null values for the pkId property, as it's not nullable.
In order to resolve your specific issue, you can simply set pkId to be a nullable int, like so:
public class PaymentTypeBORequest
{
public int? pkId { get; set; }
...
}
If you want to also allow null for the non-nullable isSystem and isActive properties, you can perform the same operations on those fields.

Related

C# possible null reference CS8602

This should be simple but I can't get ride of this null pointer warning. What can you do?
private static List<OrderHeader> orderHeaders = new List<OrderHeader>{...};
/*Delete order line item from the provided OrderHeader*/
private void DeleteOrderLine(int orderHeaderIndex, int orderLineIndex)
{
if (orderHeaders != null &&
orderHeaders[orderHeaderIndex] != null &&
orderHeaders[orderHeaderIndex].OrderLineItems != null &&
orderHeaders[orderHeaderIndex].OrderLineItems.Count > orderLineIndex
)
{
orderHeaders[orderHeaderIndex].OrderLineItems.RemoveAt(orderLineIndex);
} else
{
Console.WriteLine("Failed to delete the order line. Please try again");
}
}
Here is second attempt.. still not working.
/*Delete order line item from the provided OrderHeader*/
private void DeleteOrderLine(int orderHeaderIndex, int orderLineIndex)
{
if (orderHeaders is not null &&
orderHeaders[orderHeaderIndex] is not null &&
orderHeaders[orderHeaderIndex].OrderLineItems is not null &&
orderHeaders[orderHeaderIndex].OrderLineItems.Count > orderLineIndex
)
{
orderHeaders[orderHeaderIndex].OrderLineItems.RemoveAt(orderLineIndex);
} else
{
Console.WriteLine("Failed to delete the order line. Please try again");
}
}
Here's the the order Header definition
public class OrderHeader
{
public enum OrderTypes
{
Normal = 0,
Staff,
Mechanical,
Perishable
}
public enum OrderStatusTypes
{
New = 0,
Processing,
Complete
}
[Key]
public string OrderId { get; set; } = string.Empty;
public OrderTypes OrderType { get; set; }
public OrderStatusTypes OrderStatus { get; set; }
public DateTime CreateDate { get; set; } = DateTime.Now;
public string CustomerName { get; set; } = string.Empty;
public List<OrderLine>? OrderLineItems { get; set; }
}
Here is the orderLine definition
public class OrderLine
{
public int LineNumber { get; set; }
public string ProductCode { get; set; } = string.Empty;
public ProductTypes ProductType { get; set; } = 0;
[Column(TypeName = "decimal(18,2)")]
public decimal CostPrice { get; set; }
[Column(TypeName = "decimal(18,2)")]
public decimal SalePrice { get; set; }
public int Quantity { get; set; }
}
It is a common warning when using <Nullable>enable</Nullable>
Since you are checking that orderHeaders[orderHeaderIndex].OrderLineItems is not null then you can use ! operator to indicate that it cannot be null after that check, so try:
private void DeleteOrderLine(int orderHeaderIndex, int orderLineIndex)
{
if (orderHeaders is not null &&
orderHeaders[orderHeaderIndex] is not null &&
orderHeaders[orderHeaderIndex].OrderLineItems is not null &&
orderHeaders[orderHeaderIndex].OrderLineItems!.Count > orderLineIndex
)
{
orderHeaders[orderHeaderIndex].OrderLineItems!.RemoveAt(orderLineIndex);
} else
{
Console.WriteLine("Failed to delete the order line. Please try again");
}
}
I'd make life simpler by slightly changing your API and then making the analysis simpler for the compiler:
/*Delete order line item from the provided OrderHeader*/
private bool DeleteOrderLine(int orderHeaderIndex, int orderLineIndex)
{
if(orderHeaders is null) return false;
var header = orderHeaders[orderHeaderIndex];
if(header is null) return false;
var lineItems = header.OrderLineItems;
if(lineItems is null || lineItems.Count <= orderLineIndex) return false;
lineItems.RemoveAt(orderLineIndex);
return true;
}
Note, it's now up to the caller to check the return value and report success to the user, log an error, etc1, 2.
This simplifies the compiler's analysis because it doesn't have to reason about, for instance, whether it's possible for the OrderLineItems property to be capable of modifying the orderHeaders collection such that the next time it's indexed into a different result will be returned.
Note also that it doesn't have to reason about that at all anyway since I only index into the collection once and the compiler knows for certain that local variables don't change their nullability after they've been checked.
1I.e. It's a bit of a smell that it's currently the job of this one method both to change a collection and to know the right way to interact with the users.
2In fact I'd actually probably prefer to do an additional range check on orderHeaderIndex and be throwing ArgumentXxxExceptions rather than returning a bool but that might be too much of a change to accept at this stage
Open your .csproj file and locate the sections that has
<Nullable>enable</Nullable>
Update for more information - disabling it will tell the compiler that you not allowing nullable types on a project scale and if there are null you will handle them

Asp.Net Core Generic Repository Pattern Soft Delete

I'm trying to create a Soft Delete action in my Repository but i have to do that without creating any interfaces or classes. Let me show u to my method first,
public void Delete(T model)
{
if (model.GetType().GetProperty("IsDelete") == null )
{
T _model = model;
_model.GetType().GetProperty("IsDelete").SetValue(_model, true);//That's the point where i get the error
this.Update(_model);
}
else
{
_dbSet.Attach(model);
_dbSet.Remove(model);
}
}
im getting a Object reference not set to an instance of an object. exception. of course i know what that means but i just could not figure it out and i dont know what to do. I'm not sure if there is a better way or not.
Thanks for reading!
Guys u really gotta look at to the where i get the error. I'm editing my question.
public abstract class Base
{
protected Base()
{
DataGuidID = Guid.NewGuid();
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid DataGuidID { get; set; }
public int? CreatedUserId { get; set; }
public int? ModifiedUserId { get; set; }
public string CreatedUserType { get; set; }
public string ModifiedUserType { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime? ModifiedDate { get; set; }
public bool? IsDelete { get; set; } //That's the property
}
Every type of model class is inheriting from that Base class. When i create a new object, it takes null value. That is why im controlling that property as
==null .
First you check if the property IsDelete is null, and then you try to set the value of the property which is, obviously, null.
if (model.GetType().GetProperty("IsDelete") == null ) should be
if (model.GetType().GetProperty("IsDelete") != null )
Edit:
Now we know that you want to check the value of a nullable bool, we have to take another approach.
// first we get the property of the model.
var property = model.GetType().GetProperty("IsDelete");
// lets assume the property exists and is a nullable bool; get the value from the property.
var propertyValue = (bool?)property.GetValue(model);
// now check if the propertyValue not has a value.
if (!propertyValue.HasValue)
{
// set the value
property.SetValue(model, true);
...
}

Read a JSON property with NULL handling using JSON.Net

I have a JSON structure like below to show the details of a specific candidate
It can be either null or can contain some details like below
"details": {
"gender": {
"id": 3,
"props": {
"name": "male"
}
}
}
or as null
"details": {
"gender": null
}
To read the value of gender i tried
string _gender = (string)result["details"]["gender"]["props"]["name"];
This will works in non null cases . But if its null then this code returns an exception
So to check first is it null or not and if not null try to read the value, i tried below code
string _gender = (string)result["details"]["gender"];
if (!string.IsNullOrEmpty(_gender))
{
_gender = (string)result["details"]["gender"]["props"]["name"];
}
But i am getting the exception that not possible to convert object to string. So how to read a JSON property with proper null handling \
I strongly suggest you to deserialize the json as known type.
public class Props
{
public string name { get; set; }
}
public class Gender
{
public int id { get; set; }
public Props props { get; set; }
}
public class Details
{
public Gender gender { get; set; }
}
public class JsonObject
{
public Details details { get; set; }
}
Then perform deserialization;
var jsonObject = JsonConvert.DeserializeObject<List<JsonObject>>(json);
foreach (var o in jsonObject)
{
var genderName = o?.details?.gender?.props?.name;
}
In this way, you can handle the possible null values and get strongly typed object.
EDIT
Also, in your code, you are trying to convert an object to string and it is completely wrong. It seems that gender object is a complex type. So you can't convert it to string and you should modify your code like this;
object _gender = result["details"]["gender"];
if (_gender != null)
{
string genderName = result["details"]["gender"]["props"]["name"].ToString();
}
Keep in mind that jToken[xx..][yy..] is not a string, it is a JToken object, therefore we cannot explicitly cast it to string. If we want to convert it to string we'd have to call ToString() on it explicitly (which in terms calls JToken's overridden implementation of .ToString).
First we need to check that Jtoken has values for that we have method .HasValues.
Later when we know for certain that there is a string in ["name"]
property we can use either - explicit cast or .ToString() again
string _gender;
var genderToken = jToken["details"]["gender"];
if (genderToken.HasValues)
{
_gender = genderToken["props"]["name"].ToString();
}

C# How to initialize an object containing a list<T> with standard values

This question is related to this question. I managed to get one step further, but I am now unable to initialize my whole object with default values in order to prevent it from being null at list level. The goal of this is to hand down the "null" values to my SQL query. Ultimately what I want is one record in my DB that will express: This row has been recorded, but the related values were "null".
I have tried Brian's fiddle and it does not seem to work for me to initialize the whole model with standard values.
Expectation: Upon object initialisation the "null" values should be used and then overwritten in case there is a value coming through JSON deserialisation.
Here is what I have tried. None of this will have the desired effect. I receive this error:
Application_Error: System.ArgumentNullException: Value cannot be null.
Every time I try to access one of the lists in the data model.
namespace Project.MyJob
{
public class JsonModel
{
public JsonModel()
{
Type_X type_x = new Type_X(); // This works fine.
List<Actions> action = new List<Actions>(); // This is never visible
/*in my object either before I initialise JObject or after. So whatever
gets initialised here never makes it to my object. Only Type_X appears
to be working as expected. */
action.Add(new Actions {number = "null", time = "null", station =
"null", unitState = "null"}) // This also does not prevent my
//JsonModel object from being null.
}
public string objectNumber { get; set; }
public string objectFamily { get; set; }
public string objectOrder { get; set; }
public string location { get; set; }
public string place { get; set; }
public string inventionTime { get; set; }
public string lastUpdate { get; set; }
public string condition { get; set; }
public Type_X Type_X { get; set; }
public List<Actions> actions { get; set; }
}
public class Actions
{
public Actions()
{
// None of this seems to play a role at inititialisation.
count = "null";
datetime = "null";
place = "null";
status = "null";
}
// public string count { get; set; } = "null"; should be the same as above
// but also does not do anything.
public string count { get; set; }
public string datetime { get; set; }
public string place { get; set; }
public string status { get; set; }
}
public class Type_X
{
public Type_X
{
partA = "null"; // This works.
}
public string partA { get; set; }
public string PartB { get; set; }
public string partC { get; set; }
public string partD { get; set; }
public string partE { get; set; }
}
}
This is how I now initialize the object based on Brian's answer.
JObject = JsonConvert.DeserializeObject< JsonModel >(json.ToString(), new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore});
When I try to iterate over Actions' content, it (logically) gives me above mentioned null error.
for (int i = 0, len = JObject.actions.Count(); i < len; i++)
My current understanding of constructor initialisations:
If I define values such as count = "null"; they should appear in any new object that is created.
If default values are present I would then also expect that a list that has items with default values (such as count for ex.) would be of Count() 1 and not null. How is that even possible?
This will get you out of your bind:
private List<Actions> _actions = new List<Actions>();
public List<Actions> actions { get => _actions; set => _actions = value ?? _actions; }
This causes trying to set actions to null to set it to the previous value, and it is initially not null so it can never be null.
I'm not absolutely sure I'm reading your question right, so here's the same fragment for partA:
private string _partA = "null";
public string partA { get => _partA; set => _partA = value ?? _partA; }
I have found that in some cases, initializing generic lists with their default constructor on your model increases ease of use. Otherwise you will always want to validate they are not null before applying any logic(even something as simple as checking list length). Especially if the entity is being hydrated outside of user code, i.e. database, webapi, etc...
One option is to break up your initialization into two parts. Part 1 being the basic initialization via default constructor, and part 2 being the rest of your hydration logic:
JObject = new List < YourModel >();
... < deserialization code here >
Alternatively you could do this in your deserialization code, but it would add a bit of complexity. Following this approach will allow you to clean up your code in other areas since each access will not need to be immediately proceeded by a null check.

Only update parameters where the value is specified by client

I have a servicestack service which accepts a DTO that looks like this:
[Route("/appointment/{id}", Verbs = "POST")]
public class UpdateAppointment
{
public Guid Id { get; set; }
public DateTime StartTime { get; set; }
public int Duration { get; set; }
public string Description { get; set; }
public Guid? MemberId { get; set; }
}
How can I check whether the MemberId value was set by the client since "null" is a valid value. Normally if NULL is not a valid value, I could use the PopulateWithNonDefaultValues() method.
So the result should be that if I don't specify MemberId in my HTTP POST payload, I want the server to not update the value.
I hope that makes sense..
This is not normally an issue if you consider that the client always provides all values when calling the UpdateAppointment Service. So I'd highly recommend that you consider every property is a "valid" value provided by the client and update all fields.
Create a separate Service if only want to update a partial property list.
If I really needed to check whether the client provided a value you can specify a different value in the Request DTO constructor, e.g:
public class UpdateAppointment
{
public UpdateAppointment()
{
MemberId = Guid.Empty;
}
//...
}
where a non Guid.Empty value means it was populated by the client.
Or you could also use a calculated Property:
public class UpdateAppointment
{
[IgnoreDataMember]
public bool HasMemberId { get; set; }
Guid? memberId;
public Guid? MemberId
{
get { return memberId; }
set
{
memberId = value;
HasMemberId = true;
}
}
}
A more fragile alternative is to buffer the Request with the global Request Filter:
appHost.PreRequestFilters.Add((httpReq, httpRes) => {
httpReq.UseBufferedStream = true;
});
Which will retain a copy of the Request Stream which you can get a copy of in your Service with:
var jsonBody = base.Request.GetRawBody();
var hasMemberId = jsonBody.ToLower().Contains("memberid");
Although note this is serializer dependent, i.e. wont work with Binary Serializers like ProtoBuf or MsgPack.
Why is MemberId nullable if null value is not allowed?
Just change its definition to:
public Guid MemberId { get; set; }
have you tried load the values first then editing it before update?
I mean this:
[HttpPost]
public ActionResult Update(UpdateAppointment UAForm){
UpdateAppointment ua = new UpdateAppointmentBll().Find(UAForm.Id);
ua.StartTime = UAForm.StartTime;
//so no...
if(UAForm.MemberId != null)
ua.MemberId = UAForm.MemberId;
new UpdateAppointmentBll().Save(ua);
return View();
}

Categories

Resources