AspNetCoreOData icon indicating copy to clipboard operation
AspNetCoreOData copied to clipboard

How to return an Edm.Untyped as JSON from a CLR object type?

Open sep2 opened this issue 3 years ago • 7 comments

I have an action which returns an object type, and I want this object to be returned as an untyped json structure, how can I do that?

sep2 avatar Mar 29 '22 08:03 sep2

Hi @sep2, if you want to return an object with "untyped" structure, you can achieve that using dynamic properties. There isn't strong support for Edm.Untyped in the libraries at the moment. Here's how you can achieve this using dynamic properties:

  • Create a complex open type and make it the return type of the action
  • An easy way to make a complex type and open type using the model builder is to add dictionary property bag called DynamicProperties of type IDictionary<string, object>.
  • The "unstructured" fields of the object should be stored in the dictionary.

Here's an example:

  • Define your model types:
public class ActionResultData
{
     public IDictionary<string, object> { get; set; }
    // ... you can add "structured" properties if necessary
}
  • define the return type of your action
// ...
builder.Action("MyAction").Returns<ActionResultData>();
// ...

Now in your controller, you can return a new instance of the complex type:

[HttpPost]
public IActionResult MyAction(ODataActionParameters)
{
    var resultData = new ActionResultData
    {
        DynamicProperties = new Dictionary<string, object>
        {
            { "Field1", 1 },
            { "Field2", "string" },
            // this assumes that Address is complex type declared in the model
            { "Address", new Address { Street = "Street", City = "City" } },
            // you can nest open types
            {
                "NestedUnstructuredData",
                new ActionResultData
                {
                    DynamicProperties = new Dictionary<string, object>()
                    {
                        // nested fields
                    }
                }
            }
        }
    };

    return Ok(resultData);
}

Note that inside the DynamicProperties you can add primitive values as well as complex or entity types defined in your model. If you add a value of a type that's not declared in your model, then an exception will be thrown.

Let me know if this works for you.

This blog post shares more information on dynamic properties.

habbes avatar Mar 29 '22 12:03 habbes

Thanks for your reply! I will assume that means if I have an untyped object (which is a json structure returned from other API with unknown schema), I should recursively walk through the json property tree to build a nested DynamicProperties object?

sep2 avatar Mar 29 '22 13:03 sep2

@sep2 how are you getting this object? Is it sent to your OData API via a request or do you obtain it through some other means? And does it exist in your app as an instance of a concrete class or as a JsonDocument with properties and values that are not known in advance?

If the object is sent to your application via an OData request, then OData should be able to deserialize a request body that's made of an open type instance. Nested properties may need to be annotated with @odata.type so that OData knows what is' dealing with. OData will take care of inserting the dynamic properties in the dictionary during deserialization.

If, on the other hand, you get the object as JsonDocument or dynamic object whose properties you do not know ahead of time, then you may need to recursively walk through the JSON tree to populate the DynamicProperties dictionaries as you have suggested. I'm not sure if there's a simple way. Any thoughts @xuzhg?

habbes avatar Mar 30 '22 04:03 habbes

@habbes @sep2 8.x model builder will build the "System.Object" property as "Edm.Untyped" property.

However, 8.x serialization hasn't fully supported to covert the System.Object into ODataUntypedValue. You can customize the serializer by yourself to handle it.

xuzhg avatar Mar 30 '22 05:03 xuzhg

I did try to implement a custom serializer for JsonDocument https://github.com/OData/AspNetCoreOData/issues/309#issuecomment-1080141409, but there are some cases I cannot find a way to handle.

I will try to implement a serializer for System.Object to Edm.Untyped and see how it goes.

sep2 avatar Mar 30 '22 09:03 sep2

Thanks @sep2 Let us know how it goes.

habbes avatar Apr 04 '22 05:04 habbes

Haven't got time to work on this, sorry.

sep2 avatar Apr 10 '22 09:04 sep2