2

Structure

Class B extends Class A. A implements Interface ISerializable

The ISerializabledefines a constructor:

public A(SerializationInfo info, StreamingContext ctxt)

I need to write a specifc implementation of this constructor in B.

I have tried simply putting the constructor in B - but it will not be called. I can't seem to override it either.

The simplified problem

So A(SerializationInfo info, StreamingContext ctxt) always gets called instead of B(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt).

or

new Base() does not call new Derived()

The code that calls the (wrong) constructor:

UPDATE

  • The objects are treated as objects of A - which might be the problem!

UPDATE

List<A> list = new List<A>();
list.Add(New B());
string s = JsonConvert.SerializeObject(list);    
JsonConvert.DeserializeObject<List<A>>(s); <--- //it is called from here.

Any ideas to solve this inheritance problem?

Details

public class A: ISerializable
{
public A(int id, string name, string type, string category, string description, string data)
        {
            this.Id = id;
            this.Name = name;
            this.Type = type;
            this.Category = category;
            this.Description = description;
            this.Data = data;
        }

 protected A(SerializationInfo info, StreamingContext ctxt)
        {
            Id = (int)info.GetValue("id", typeof(int));
            Name = (String)info.GetValue("name", typeof(string));
            Type = (String)info.GetValue("type", typeof(string));
            Category = (String)info.GetValue("category", typeof(string));
            Description = (String)info.GetValue("description", typeof(string));
            Data = (String)info.GetValue("data", typeof(string));
        }
}

public class B : A
{

public B(int id, string name, string type, string category, string description, string data) : base(id, name, type, category, description, data)
            {
               // specific B code
            }

 protected B(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt){
    // THIS NEVER GETS CALLED   
   // specific B code
}


}
capcapdk
  • 179
  • 1
  • 15
  • 6
    `ISerializable` does not define a constructor. In fact, no `interface` can define a constructor... – haim770 Jul 24 '16 at 15:03
  • Well, there must be a constructor taking `SerializationInfo` and `StreamingContext` for the serialization to work. – capcapdk Jul 24 '16 at 15:05
  • Removing it gives the following Exception: `A does not have a valid constructor. To correctly implement ISerializable a constructor that takes SerializationInfo and StreamingContext parameters should be present.` – capcapdk Jul 24 '16 at 15:07
  • 2
    Apparently, I was wrong. `ISerializable` is indeed getting a special treatment from the compiler. Sorry. – haim770 Jul 24 '16 at 15:11
  • Can you show [mcve]? – user4003407 Jul 24 '16 at 15:19
  • @PetSerAl i updated the question, and i believe that the example is complete. `A(SerializationInfo info, StreamingContext ctxt)` gets called instead of `B(SerializationInfo info, StreamingContext ctxt)` where `B` extends `A`. – capcapdk Jul 24 '16 at 15:21
  • You should have `GetObjectData` with that parameter list but not constructor. – Hamlet Hakobyan Jul 24 '16 at 15:23
  • `I have tried simply putting the constructor in B - but it will not be called.` What are your actions when this happens? Show the code. – Hamlet Hakobyan Jul 24 '16 at 15:24
  • @HamletHakobyan I also have `GetObjectData` in `A`. It has nothing to do with the problem of the constructor. :) – capcapdk Jul 24 '16 at 15:24
  • @HamletHakobyan - as i wrote: `A(SerializationInfo info, StreamingContext ctxt)` always gets called instead of `B(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt)` which is ignored. – capcapdk Jul 24 '16 at 15:25
  • 1
    The `A(SerializationInfo info, StreamingContext ctxt)` can't be called by itself. Show the code. – Hamlet Hakobyan Jul 24 '16 at 15:26
  • @HamletHakobyan i updated the question. – capcapdk Jul 24 '16 at 15:29
  • @poke I don't find it related.. But thanks for trying to help. – capcapdk Jul 24 '16 at 15:35
  • @haim770 `ISerializable` has zero special treatment from compiler. OP's question essentially was "Why `new Base()` does not call `new Derived()`" at the time you've made the comment... – Alexei Levenkov Jul 24 '16 at 15:47
  • @AlexeiLevenkov That is not true. I see by break points that the serialization is called from the `JsonConvert.DeSerializeObject`. The question is exactly what i want to achieve. I want to reuse some code from the constructor in `A` with additional code in constructor of `B`. – capcapdk Jul 24 '16 at 15:51
  • Does B have two constructors, B() and the B(SerializationInfo info, StreamingContext ctxt)? – Stephen Zeng Jul 24 '16 at 16:11
  • @StephenZeng Yes. But none of these are called by the deserialization. Only `A(info, ctxt)` gets called... – capcapdk Jul 24 '16 at 16:15
  • @AlexeiLevenkov, See https://msdn.microsoft.com/en-us/library/ms182343.aspx – haim770 Jul 24 '16 at 16:15
  • @capcapdk can you show more concrete details of A and B, especially their implementations of constructors? – Stephen Zeng Jul 24 '16 at 16:18
  • @StephenZeng I updated the question with some details of A and B – capcapdk Jul 24 '16 at 16:25
  • @AlexeiLevenkov - although not strictly a case of the compiler handling ISerializable classes specially, be aware that deserializing a WCF Data Contract class bypasses constructors entirely. They only get fired when the object is created via a "= new" command. When deserializing via WCF the constuctors are not called, principally to prevent initialization code getting called multiple times on a class instance. – PhillipH Jul 24 '16 at 16:30
  • @capcapdk I just quickly tried some similar code and both contructors (SerializationInfo info, StreamingContext ctxt) got called. Remember though, in .net base constructor is called before derived class's ones. – Stephen Zeng Jul 24 '16 at 16:34
  • @StephenZeng I don't understand why it is like that... I even tried implementing this: `protected B(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt){ throw new Exception("test");}` and it does **not** get called... – capcapdk Jul 24 '16 at 16:38
  • @capcapdk is there any exceptions possibly thrown in A's constructor? that will prevent B's gets called. Maybe you can simplify A's constructor see if it works? – Stephen Zeng Jul 24 '16 at 16:40
  • @StephenZeng One extra detail: The Json.DeSerialize is actually not called like in the question. It is called on a `List ` where some of these objects in the list are objects of `B`. – capcapdk Jul 24 '16 at 16:42
  • @haim770 no you were not wrong. constrcutors cannot be defined in interfaces in c#. – Stephen Zeng Jul 24 '16 at 16:43
  • @capcapdk can you show more details of it? – Stephen Zeng Jul 24 '16 at 16:44
  • @StephenZeng I updated the part of the question where the code is called from. – capcapdk Jul 24 '16 at 16:46
  • @capcapdk that's why. It is treated as a type of List. – Stephen Zeng Jul 24 '16 at 16:48
  • @StephenZeng Allright. Then we found the problem ;) How we do solve this? I need the list to be a `List`, so i can't change that. – capcapdk Jul 24 '16 at 16:50
  • @capcapdk I've removed my wrong statement about ISerializable not used by Json.Net . – Alexei Levenkov Jul 24 '16 at 17:06
  • 1
    (Note that answer to your actual question after you've added [MCVE] - http://stackoverflow.com/questions/8513042/json-net-serialize-deserialize-derived-types) – Alexei Levenkov Jul 24 '16 at 17:08

1 Answers1

0

This should help you to call some code in derived class in your case. It just tries to give you an option. Without knowing the context I cannot do the judge. Also check out what is virtual member call.

You can define a virtual method in A, call it in A(SerializationInfo info, StreamingContext ctxt).

public class A : ISerializable
{
    public A(string name)
    {
    }

    public virtual void Foo()
    {
    }

    protected A(SerializationInfo info, StreamingContext ctxt)
    {
        Foo();
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
    }
}

Then in B copy/move some of logic in to the overrided method. When A(SerializationInfo info, StreamingContext ctxt) gets called B's Foo() will get called as well.

public sealed class B : A
{
    public B(string name) : base(name)
    {
    }

    public override void Foo()
    {
        Tag = "B";
    }

    public string Tag { get; set; }

    protected B(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt)
    {
    }
}
Stephen Zeng
  • 2,748
  • 21
  • 18