1

Consider the following scenario. The input json string is deserialized to the Invoice class. The OnDeserializedMethod is called, and the validity check is performed. How can i add the validation erros from the issuer class at the invoice class?

    public class Invoice
    {
        public Dictionary<string, string> ValidationErrors = new Dictionary<string, string>();
        
        public Issuer Issuer { get; set; }
    }

    public class Issuer
    {
        public string Vat { get; set; }

        [OnDeserialized]
        internal void OnDeserializedMethod(StreamingContext context)
        {
            if (Vat == "")
                ValidationErrors.add("1", "Vat cannot be null");
        }
    }
OrElse
  • 9,709
  • 39
  • 140
  • 253
  • 1
    You would need a reference to the parent class in Issuer. – ProgrammingLlama Mar 03 '22 at 09:36
  • 1
    You'd really need to give us a [mcve]. Right now we don't know what `ValidationErrors` is and how it's part of the class hierarchy. Give us the full code to understand the problem, please. – Enigmativity Mar 03 '22 at 09:41
  • 1
    You really want those to validate themselves? I'd argue that's none of their business. Take the validation out of them and be happy again. Also have a look into [FluentValidation](https://fluentvalidation.net/) and be even happier. – Fildor Mar 03 '22 at 09:53
  • The owner (Invoice) knows of the owned class instances (Issuer, maybe others too). The owner will be asked for the validation errors. The owner can collect them from the owned classes. This way the owned class does not need to know of the owner. Note: I avoided using parent/child as it's not an inheritance hierarchy, which is where we more typically use those terms.. – Caius Jard Mar 03 '22 at 10:28
  • If you're only going to use Dictionary as a list of KeyValuePair (and not leverage it for lookups) it can be better to just use a List> – Caius Jard Mar 03 '22 at 10:31
  • Does this answer your question? https://stackoverflow.com/questions/1940165/how-to-access-to-the-parent-object-in-c-sharp – Rule Mar 03 '22 at 10:44
  • If these are DTOs, then DTOs should only validate themselves: validation of DTOs in the context of an object-graph (as in this case: a parent DTO with a child DTO) should be done by an entirely separate type that has access to the entire graph. – Dai Mar 03 '22 at 11:50
  • FWIW/IMO, use of `[OnDeserialized]` should be not be (ab)used for tasks like object-graph validation (it's meant for post-deserialization initialization, and even then it's a poor tool for that) [because the docs for it say](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.ondeserializedattribute?view=net-6.0): _"The order of deserialization relative to other objects in the graph is non-deterministic."_ - which means that you **cannot** correctly perform graph-wide operations in any `[OnDeserialized]` method. – Dai Mar 03 '22 at 11:52
  • "Parent" is not a good word here. "Parent" is used for inheritance, and there is no inheritance (is-a) here. You are using composition (has-a). There is no "easy" word for this relation. – JHBonarius Mar 03 '22 at 12:16

1 Answers1

0

Here is a rough workaround, create a class to hold the "parent" object

class InvoiceHolder
{
    public Invoice current;
}

Pass it as the context of the serializer

var context = new StreamingContext(0, new InvoiceHolder());
var settings = new JsonSerializerSettings { Context = context };
var o = JsonConvert.DeserializeObject<Invoice>(json, settings);

Set the value in the "parent" class when deserializing, then you can get the reference to the "parent" object

public class Invoice
{
    [OnDeserializing]
    internal void OnDeserializingMethod(StreamingContext context)
        => ((InvoiceHolder)context.Context).current = this;
}

public class Issuer
{
    [OnDeserialized]
    internal void OnDeserializedMethod(StreamingContext context)
    {
        if (Vat == "")
             ((InvoiceHolder)context.Context).current
                 .ValidationErrors.Add("1", "Vat cannot be null");
    }
}
shingo
  • 18,436
  • 5
  • 23
  • 42