'How to control json formatting with System.Web.OData (asp.net)
here's my situation:
I'm working on an asp.net web api project with OData. I'm using Microsoft.Aspnet.OData NuGet package.
My intention is to intercept the serialization to json when a response is built.
Using System.Web.OData, I'm trying to extend System.Web.OData.Formatter.Serialization.DefaultODataSerializerProvider:
public class MyODataSerializerProvider : DefaultODataSerializerProvider
{
public MyODataSerializerProvider(IServiceProvider rootContainer) : base(rootContainer)
{
}
}
Then, in my WebApiConfig.Register method I'd like to do the following:
config.Formatters.InsertRange(0, ODataMediaTypeFormatters.Create(new MyODataSerializerProvider(???), new DefaultODataDeserializerProvider(???)));
Where I put ??? above is the problem: with the last update of the System.Web.OData (version 6.0.0.0) they added support for Dependency Injection, but I don't know what to pass as rootContainer parameter.
I've read this: http://odata.github.io/odata.net/v7/#01-05-di-support but I don't need to create my Container Builder (at least I think so), I just wanna use the IServiceProvider that I think OData prepares. Am I right? Can i retrieve this IServiceProvider and pass it to my constructor?
A a more general question, is what I'm trying to do the right way to achieve my needs, that is to be able to control the serialization of the response?
Example of a situation: let's say that the response is a collection of entity type "Customer", with one of its properties named "Prop" of type int. When this collection is built for some specific user, I want to mask the real value of "Prop" and write "Prop" : 0 instead.
We tryed other roads to achieve this by code, but I think for our needs the best solution is to be able to control the json serialization.
Thanks.
Solution 1:[1]
Update (2022-05-19) in response to @piotreks comment:
Using Microsoft.AspNetCore.OData, Version=8.0.9.0:
using Microsoft.AspNetCore.OData.Formatter;
using Microsoft.AspNetCore.OData.Formatter.Serialization;
using Microsoft.OData;
using Microsoft.OData.Edm;
using System;
class MyODataSerializerProvider : ODataSerializerProvider
{
public MyODataSerializerProvider(IServiceProvider serviceProvider)
: base(serviceProvider)
{
}
public override IODataEdmTypeSerializer GetEdmTypeSerializer(IEdmTypeReference edmType)
{
if (edmType.Definition.TypeKind == EdmTypeKind.Entity)
return new MyODataResourceSerializer(this);
else
return base.GetEdmTypeSerializer(edmType);
}
}
class MyODataResourceSerializer : ODataResourceSerializer
{
public MyODataResourceSerializer(ODataSerializerProvider serializerProvider)
: base(serializerProvider)
{
}
public override ODataProperty CreateStructuralProperty(IEdmStructuralProperty structuralProperty, ResourceContext resourceContext)
{
ODataProperty
property = base.CreateStructuralProperty(structuralProperty, resourceContext);
if (resourceContext.StructuredType.FullTypeName() == typeof(Customer).FullName)
{
switch (property.Name)
{
case "Prop":
property.Value = 0;
break;
}
}
return property;
}
}
Registration:
public virtual void ConfigureServices(IServiceCollection services)
{
IEdmModel
model = BuildEdmModelForOData();
services
.AddControllers()
.AddOData(oDataOptions =>
{
oDataOptions
.AddRouteComponents("odata", model, serviceCollection =>
{
serviceCollection
.AddSingleton<IODataSerializerProvider>(serviceProvider => new MyODataSerializerProvider(serviceProvider));
});
});
}
public IEdmModel BuildEdmModelForOData()
{
ODataConventionModelBuilder
builder = new();
// Build model here
return builder.GetEdmModel();
}
Original Answer:
I came across the same problem today. You register MyODataSerializerProvider as per this example:
public void Register(HttpConfiguration config)
{
config.MapODataServiceRoute
(
"odata",
"odata",
BuildRoute
);
}
private void BuildRoute(IContainerBuilder builder)
{
builder
.AddService(ServiceLifetime.Singleton, s => BuildEdmModelForOData())
.AddService<ODataSerializerProvider>(ServiceLifetime.Singleton, s => new MyODataSerializerProvider(s));
}
public IEdmModel BuildEdmModelForOData()
{
ODataConventionModelBuilder
builder = new ODataConventionModelBuilder();
// Build model here
return builder.GetEdmModel();
}
In relation to your more general question, it is possible to intercept and replace object values like this:
class MyODataSerializerProvider : DefaultODataSerializerProvider
{
public MyODataSerializerProvider(IServiceProvider rootContainer)
: base(rootContainer)
{
}
public override ODataEdmTypeSerializer GetEdmTypeSerializer(IEdmTypeReference edmType)
{
if (edmType.Definition.TypeKind == EdmTypeKind.Entity)
return new MyODataResourceSerializer(this);
else
return base.GetEdmTypeSerializer(edmType);
}
}
class MyODataResourceSerializer : ODataResourceSerializer
{
public MyODataResourceSerializer(ODataSerializerProvider serializerProvider)
: base(serializerProvider)
{
}
public override ODataProperty CreateStructuralProperty(IEdmStructuralProperty structuralProperty, ResourceContext resourceContext)
{
ODataProperty
property = base.CreateStructuralProperty(structuralProperty, resourceContext);
if (resourceContext.StructuredType.FullTypeName() == typeof(Customer).FullName)
{
switch (property.Name)
{
case "Prop":
property.Value = 0;
break;
}
}
return property;
}
}
It's also worth mentioning that returning null from CreateStructuralProperty suppresses the property entirely.
Hope this is still of assistance.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
