Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Writing top-level property with ODataJsonElementValue fails with exception stream value not supported #3162

Open
habbes opened this issue Jan 7, 2025 · 0 comments · May be fixed by #3166
Open
Assignees
Labels

Comments

@habbes
Copy link
Contributor

habbes commented Jan 7, 2025

Short summary (3-5 sentences) describing the issue.

ODataMessageWriter supports writing a top-level property (one that is not embedded in a resource). One use case for this is to write the result of an action or function call that returns a primitive value.

As demonstrated in the code snippet below, when you write a top-level property with a primitive value using ODataMessageWriter, you usually get an object with a field called value containing the serialized value (the property's original value is ignored) and an odata.context field.

var messageWriter = new ODataMessageWriter(responseMessage, messageWriterSettings);
await messageWriter.WritePropertyAsync(new ODataProperty
{
    Name = "propName",
    Value = true
});

Result:

{"@odata.context":"https://tempuri.org/$metadata#Edm.Boolean","value":true }

However, if the value is inside a JsonElement, and you use ODataJsonElementValue to store in an OData property a so:

var messageWriter = new ODataMessageWriter(asyncResponseMessage, this.messageWriterSettings);
var jsonValue = JsonDocument.Parse("true").RootElement;
await messageWriter.WritePropertyAsync(new ODataProperty
{
    Name = "Id",
    Value = new ODataJsonElementValue(jsonValue)
});

You will get the following exception

Microsoft.OData.ODataException: 'The stream value must be a property of an ODataResource instance.'
   at Microsoft.OData.ODataContextUrlInfo.GetTypeNameForValue(ODataValue value, IEdmModel model) in C:\odata.net\src\Microsoft.OData.Core\ODataContextUrlInfo.cs:line 458
   at Microsoft.OData.ODataContextUrlInfo.Create(ODataValue value, ODataVersion version, ODataUri odataUri, IEdmModel model) in C:\odata.net\src\Microsoft.OData.Core\ODataContextUrlInfo.cs:line 81
   at Microsoft.OData.Json.ODataJsonPropertySerializer.GetContextUrlInfo(ODataProperty property) in C:\odata.net\src\Microsoft.OData.Core\Json\ODataJsonPropertySerializer.cs:line 884
   at Microsoft.OData.Json.ODataJsonPropertySerializer.<>c.<<WriteTopLevelPropertyAsync>b__9_0>d.MoveNext() in C:\odata.net\src\Microsoft.OData.Core\Json\ODataJsonPropertySerializer.cs:line 286
   at Microsoft.OData.Json.ODataJsonSerializer.<WriteTopLevelPayloadAsync>d__30`2.MoveNext() in C:\odata.net\src\Microsoft.OData.Core\Json\ODataJsonSerializer.cs:line 588
   at Microsoft.OData.Json.ODataJsonOutputContext.<WritePropertyImplementationAsync>d__69.MoveNext() in C:\odata.net\src\Microsoft.OData.Core\Json\ODataJsonOutputContext.cs:line 1084
   at Microsoft.OData.Json.ODataJsonOutputContext.<WritePropertyAsync>d__35.MoveNext() in C:\odata.net\src\Microsoft.OData.Core\Json\ODataJsonOutputContext.cs:line 461
   at Microsoft.OData.ODataMessageWriter.<WriteToOutputAsync>d__101.MoveNext() in C:\odata.net\src\Microsoft.OData.Core\ODataMessageWriter.cs:line 1292

Assemblies affected

Microsoft.OData.Core 8.x

Reproduce steps

call ODataMessageWriter.WritePropertyAsync with an ODataProperty whose Value is an ODataJsonElementValue.

Expected result

If the JsonElement is a primitive value, then it should be handled the same way as writing the corresponding ODataPrimitiveValue. If it's an array or object, then it's a bit tricky, maybe it could be treated as a resource or untyped?

Actual result

Throws an exception (see summary above).

Additional detail

This exception occurs because OData Writer is trying to generate a context URL for the value but it doesn't know the type. Specifically, it gets thrown in ODataContextUrlInfo.GetTypeNameForValue, this method handles the different child types of ODataValue, but doesn't handle ODataJsonElementValue. We should add a case to handle ODataJsonElementValue.

ODataJsonElementValue is a niche feature added in 2023 to provide an optimization for scenario where the payload input is already JSON document parsed by System Text Json (JsonElement). If the server already knows that the contents of the parsed JsonElement is valid OData, then it could save a lot of memory and CPU cycles to pass it down as-is to the underlying ODataUtf8JsonWriter without breaking it down. The use case for this would be some gateway that proxies responses from an OData service.

@habbes habbes added the P2 label Jan 10, 2025
@habbes habbes assigned habbes and unassigned habbes Jan 10, 2025
@habbes habbes linked a pull request Jan 17, 2025 that will close this issue
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant