2

In an ASP.NET Core web api I am using System.Text.Json. I am also using the Cosmos DB Change Feed Pull Method to process changed items. I need to examine the type property of each item to determine the type to de-serialize the item to. In order to do this, I am using GetChangeFeedStreamIterator. Working off the following sample code:

https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.cosmos.container.getchangefeedstreamiterator?view=azure-dotnet

FeedIterator feedIterator = this.Container.GetChangeFeedStreamIterator(
    ChangeFeedStartFrom.Beginning(),
    ChangeFeedMode.Incremental,
    options);

while (feedIterator.HasMoreResults)
{
    using (ResponseMessage response = await feedIterator.ReadNextAsync())
    {
        if (response.StatusCode == NotModified) 
        {
            // No new changes
            // Capture response.ContinuationToken and break or sleep for some time
        }
        else 
        {
            using (StreamReader sr = new StreamReader(response.Content))
            using (JsonTextReader jtr = new JsonTextReader(sr))
            {
                JObject result = JObject.Load(jtr);
            }
        }
    }
}

I came up with this:

FeedIterator feedIterator = _entities.GetChangeFeedStreamIterator(startFrom, ChangeFeedMode.Incremental);

var result = new ChangedEntitiesResult();

while (feedIterator.HasMoreResults) {
    using ResponseMessage response = await feedIterator.ReadNextAsync();
    if (response.StatusCode == HttpStatusCode.NotModified) {
        result.ContinuationToken = response.ContinuationToken;
        break;
    } else {
        using StreamReader resultStreamReader = new StreamReader(response.Content);
        using JsonTextReader resultJsonTextReader = new JsonTextReader(resultStreamReader);
        var resultJObject = JObject.Load(resultJsonTextReader);
        JArray documents = (JArray)resultJObject["Documents"];
        foreach (JObject document in documents) {
            string entityType = (string)document["type"];
            var entity = _entityFactory.GetNewEntityDtoInstance(entityType);
            _serializer.Populate(document.CreateReader(), entity);
            result.Entities.Add(entity);
        }
    }
}

The code above works as expected but I am running into issues with nested json objects elsewhere in the application. The conflict is between objects serialized with System.Text.Json and deserialized with Newtonsoft. So either I switch completely over to Newtonsoft, or I convert the above code to use System.Text.Json.

Can I accomplish the above with System.Text.Json or should I just switch the whole project to Newtonsoft?

I am open to other suggestions like approaching the problem of identifying the type and instantiating and hydrating the correct class some other way.

user1843640
  • 3,613
  • 3
  • 31
  • 44
  • 1
    *The code above works as expected but I am running into issues with nested json objects elsewhere in the application.* -- then what exactly is your question? Can you share a [mcve]? Incidentally, there is no built-in way to populate an object with System.Text.Json, see [.Net Core 3.0 JsonSerializer populate existing object](https://stackoverflow.com/q/56835040/3744182). – dbc Nov 10 '21 at 15:09
  • I added the question which is basically to ask if there is any way to accomplish the above with System.Text.Json. Also, I came across the SO question you referenced while troubleshooting this and the other issues. – user1843640 Nov 10 '21 at 16:29
  • Why are you using Streams? Is it only for System.Text.Json support? Have you tried just [changing the serialization engine to System.Text.Json](https://github.com/Azure/azure-cosmos-dotnet-v3/tree/master/Microsoft.Azure.Cosmos.Samples/Usage/SystemTextJson) – Matias Quaranta Nov 10 '21 at 16:43
  • @MatiasQuaranta I'm using streams so that I can access the document type `(string)document["type"]` before instantiating and hydrating the specific type/class. – user1843640 Nov 10 '21 at 16:57
  • But if you use JsonDocument as `` when using System.Text.Json or JObject if you want to use Newtonsoft.Json, you can check the property and then serialize it into another type, right? – Matias Quaranta Nov 10 '21 at 17:01
  • *Can I accomplish the above with System.Text.Json or should I just switch the whole project to Newtonsoft?* -- in your code sample you are already using Newtonsoft, so can you please identify where your problem lies? If you are looking for general guidance on how to do polymorphic serialization with System.Text.Json, see [Is polymorphic deserialization possible in System.Text.Json?](https://stackoverflow.com/q/58074304/3744182). – dbc Nov 10 '21 at 17:48
  • @MatiasQuaranta Back when I decided on the streams approach I think I was concerned about needlessly serializing twice. To be clear, you are suggesting: _entities.GetChangeFeedIterator. From there check the type property on the root element and use `JsonSerializer.Deserialize(rootElement.GetRawText(), serializerOptions)` ? – user1843640 Nov 10 '21 at 19:05
  • @dbc I'm using Newtonsoft for this change feed processor but I use System.Test.Json just about everywhere else. I suppose _I am_ asking how to do polymorphic serialization with System.Text.Json. Thanks for the link. I took a quick look. Lot's there to consider - including whether I should or not - although with a Cosmos container with many types not sure I have a choice. – user1843640 Nov 10 '21 at 19:09
  • If your concrete objects don't have parameterless constructors and you need to do `_entityFactory.GetNewEntityDtoInstance(entityType);` to construct them then System.Text.Json will prove inconvenient. The workarounds in [.Net Core 3.0 JsonSerializer populate existing object](https://stackoverflow.com/q/56835040/3744182) basically amount to doing the work yourself via reflection. – dbc Nov 10 '21 at 19:11

0 Answers0