3

I'm working on an ASP .Net Core 3.1 application, porting part of the code from another using 2.2. So far, I'd like to switch from the NewtonSoft JSON serialization library to the new one, System.Text.Json, but I have some trouble.

Consider a function to serve a HTTP-GET service with this returning type:

    [HttpGet("myservice")]
    public async Task<ActionResult<object>> GetDataAsync( ...

Then, the final part could be depicted as follows:

        var items = new List<IMyInterface>();
        int totalCount = ...
        int min = ...
        int max = ...

        return new ActionResult<object>(new
        {
            totalCount,
            min,
            max,
            items
        });

However, it doesn't work: the items collection is serialized by its declared type (IMyInterface), instead of the actual type(s). I read here that this is an expected behavior, although not so intuitive to me.

My question is: is there any convenient yet reliable way to leverage the new serializer even dealing with anonymous objects? I would avoid to create a specific object every time I can compose the result inline.

UPDATE:

doing this it seems to work, but it looks really ugly:

        return new ActionResult<object>(new
        {
            totalCount,
            min,
            max,
            items = items.Cast<object>()
        });
Mario Vernari
  • 6,649
  • 1
  • 32
  • 44
  • have you considered casting items to object within your new ActionResult object (before serializing) – Jawad Jan 03 '20 at 16:14
  • You could have a look at these two ([1](https://github.com/dotnet/corefx/issues/41347), [2](https://github.com/dotnet/corefx/issues/39031)) issues on GitHub. They might help you out. But it seems like a deliberate "feature" with polymorphic deserialization. – fredrik Jan 03 '20 at 16:14
  • @Jawad I updated the post doing a cast on each item: it works but looks quite ugly. By casting the entire collection it doesn't work, though. – Mario Vernari Jan 03 '20 at 16:21
  • or simply (object)items – Jawad Jan 03 '20 at 16:23
  • @Jawad nope: it does not work. – Mario Vernari Jan 03 '20 at 16:23
  • @Jawad - `items = (object)items,` doesn't work, see https://dotnetfiddle.net/vL6QGl – dbc Jan 03 '20 at 16:24
  • `items = items.Cast(),` does work: https://dotnetfiddle.net/KxWHTu. That's... surprising. You should make that an answer. I understand why they don't want to support polymorphic deserialization, it leaves the server open to [Friday the 13th JSON attacks](https://stackoverflow.com/q/49038055/3744182). But serializing the declared type not the actual type unless the declared type is `object` seems weird. At least the data contract serializers throw an exception on an unexpected type rather than just serializing as the base type. – dbc Jan 03 '20 at 16:25
  • my problem is not just the answer, but the convenience of using this or that library. I guess that they should give a tool which covers reliably at least what their own platforms offer (e.g. returning anonymous object in ASP APIs). I'd like more an enhancement by MS, than a point here! – Mario Vernari Jan 03 '20 at 16:29
  • @dbc I consume the server data with a browser: I don't need the type name exposed in the Net world. I guess a simple enable/disable flag in the options might fit the hole. – Mario Vernari Jan 03 '20 at 16:32
  • Serializing an anonymous object with `System.Text.Json` seems to work (though deserialization [doesn't](https://stackoverflow.com/q/59313256/3744182).) The problem I reproduced is that the contents of `items` are serialized as their declared type `IMyInterface` not their actuual type, see https://dotnetfiddle.net/vgzpFa – dbc Jan 03 '20 at 16:36

1 Answers1

1

DotNetFiddler

If you want to serialize the objects, why not initialize them as objects? Is there a requirement to create it strong typed?

    public static void Test()
    {
        var items = new List<object>() { new Class1 { Foo = "foo1", Bar1 = "Bar1" }, new Class2 { Foo = "foo1", Bar2 = "Bar2" } };
        int totalCount = 1;
        int min = 2;
        int max = 3;


        var root = new
        {
            totalCount,
            min,
            max,
            items,
        };

        var json = JsonSerializer.Serialize<object>(root, new JsonSerializerOptions { WriteIndented = true, });

        Console.WriteLine(json);
    }

If you create the items as List<object>, you dont have to change or do anything. This might be a cleaner way instead of casting each of them to object at time of creating the object.

Jawad
  • 11,028
  • 3
  • 24
  • 37
  • That's another way to surround the problem. In my case, many of these collections are generated by Linq'ed expressions, thus I must apply a Cast(). Still I can't understand why the serialization is fine by using object: why not using the runtime type all the times? – Mario Vernari Jan 04 '20 at 03:55
  • I know newtonsoft json.net works fine... System.text.json has issues. – Jawad Jan 04 '20 at 04:44