C# How can I return my base class in a webservice
Asked Answered
F

6

6

I have a class Car and a derived SportsCar: Car
Something like this:

public class Car
{
    public int TopSpeed{ get; set; }
}


public class SportsCar : Car
{
    public string GirlFriend { get; set; }
}

I have a webservice with methods returning Cars i.e:

[WebMethod]
public Car GetCar()
{
    return new Car() { TopSpeed = 100 };
}

It returns:

<Car>
<TopSpeed>100</TopSpeed>
</Car>

I have another method that also returns cars like this:

[WebMethod]
public Car GetMyCar()
{
    Car mycar = new SportsCar() { GirlFriend = "JLo", TopSpeed = 300 };
    return mycar;
}

It compiles fine and everything, but when invoking it I get:
System.InvalidOperationException: There was an error generating the XML document. ---> System.InvalidOperationException: The type wsBaseDerived.SportsCar was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.

I find it strange that it can't serialize this as a straight car, as mycar is a car.

Adding XmlInclude on the WebMethod of ourse removes the error:

[WebMethod]
[XmlInclude(typeof(SportsCar))]
public Car GetMyCar()
{
    Car mycar = new SportsCar() { GirlFriend = "JLo", TopSpeed = 300 };
    return mycar;
}

and it now returns:

<Car xsi:type="SportsCar">
    <TopSpeed>300</TopSpeed>
    <GirlFriend>JLo</GirlFriend>
</Car>

But I really want the base class returned, without the extra properties etc from the derived class.

Is that at all possible without creating mappers etc?

Please say yes ;)

Fa answered 12/5, 2010 at 11:49 Comment(2)
What do you mean creating mappers? are you talking about annotating the data contract?Philine
Hi! sorry to be unclear, I mean without creating manual conversion methods to/from car/sportscar. i.e private static Car Map(SportsCar sc) { return new Car() { TopSpeed = sc.TopSpeed } } or something ugly like that :)Fa
B
2

I would implement a copy constructor in the base class.

    public class Car
    {
        public int TopSpeed { get; set; }

        public Car(Car car)
        {
            TopSpeed = car.TopSpeed;
        }

        public Car()
        {
            TopSpeed = 100;
        }
    }

    public class SportsCar : Car
    {
        public string GirlFriend { get; set; }
    }

Then you can return a new Car based on the SportsCar in the GetMyCar-method. I think this way clearly express the intent of the method.

    public Car GetMyCar()
    {
        var sportsCar = new SportsCar { GirlFriend = "JLo", TopSpeed = 300 };
        return new Car(sportsCar);
    }
Burdock answered 14/5, 2010 at 11:6 Comment(1)
Agreed, I like this the most of the "non"-solutions :) +1 for youFa
B
1

Do this:

[WebMethod]
public Car GetMyCar()
{
    Car mycar = new SportsCar() { GirlFriend = "JLo", TopSpeed = 300 };
    return new Car() {TopSpeed = mycar.TopSpeed};
}

The reason is that XMLSerializer inspects the GetType() type of the object and expects it to be the same as the declared one.

I know it's a pain but i don't know of an alternative.

Brawn answered 12/5, 2010 at 12:42 Comment(2)
Thanks, and yes that will work. But it is essentially the same as using a Mapper method which I called it, and although I will survive with this and the Car-object, it is quite tedious and ugly with a real-world object. I was looking for a more general reusable solution though.Fa
Depending on your performance constraints you could write a generic mapper using reflectionBrawn
P
0

Either use XmlIgnoreAttribute on the attributes you want to ignore. Or the even more painful way would be to implement a custom serialization for your attributes.

Good luck.

public class SportsCar : Car
{
  [XmlIgnoreAttribute]
  public string GirlFriend { get; set; }
}
Philine answered 12/5, 2010 at 11:55 Comment(2)
Thanks, but the trouble is that in real-world I have other methods that actually returns the "SuperCar", and then the extra attributes are needed.Fa
Web services work best in a "document exchange" type approach. Then you ahve classes for the document - and map. Anything else just has problems.Strepphon
F
0

Just a stab, but have you tried this? Cast on the return.

[WebMethod]
public Car GetMyCar()
{
    Car mycar = new SportsCar() { GirlFriend = "JLo", TopSpeed = 300 };
    return (Car)mycar;
}
Footboy answered 12/5, 2010 at 12:13 Comment(2)
Tried it with same result :( Thx though!Fa
I would actually have guesses the C# compiler would warn about a useless cast directive.Amice
F
0

The other comments and answers here got me thinking, and if I need to make a Mapper-method so be it:

public class Car: ICloneable
{
    public int TopSpeed{ get; set; }
    public object Clone()
    {
        return new Car() { TopSpeed = this.TopSpeed };
    }

}

and the webmethod:

[WebMethod]
public Car GetMyNewCar()
{
    Car mycar = new SportsCar() { GirlFriend = "HiLo", TopSpeed = 300 };            
    return (Car)mycar.Clone();
}

This works as expected:

<Car>
    <TopSpeed>300</TopSpeed>
</Car>

This defeats the purpose of having a derived object in my view, but until someone comes up with another solution thats the way I'll fly...

Fa answered 12/5, 2010 at 13:18 Comment(0)
I
0

If you want it to always serialize as "Car" instead of "SportsCar" add an [XmlRoot("Car")] to it.

public class Car {
  //stuff
}

[XmlRoot("Car")]
public class SportsCar {
  //Sporty stuff
}

Edit: Using the XmlSerializer like this:

XmlSerializer ser = new XmlSerializer(typeof(SportsCar));
SportsCar car = new SportsCar()
//stuff
ser.Serialize(Console.out, car)

you should get

<Car>
  <TopSpeed>300</TopSpeed>
  <GirlFriend>JLo</GirlFriend>
</Car>
Iritis answered 12/5, 2010 at 13:21 Comment(3)
Thanks, but see comment on Nix's answer, I do need Sportstuff sometimes.Fa
Hmm. tried this like this: [XmlRoot("Car")] public class SportsCar : Car {... Got the same exception I initially had, InvalidOperationSomething... Might be doing it wrong though.Fa
Did you create the serializer with the SportsCar type or the Car type? Regardless, if you DO need the sportscar sometimes (not just it's elements), this wont work unless you wanna mess with XmlAttributeOverrides when you create your serializer (huge pita) This is of course assuming your using the XmlSerializer, if your usinc wcf ignore everything ive said :PIritis

© 2022 - 2024 — McMap. All rights reserved.