❔ JSON Serialization Weird Model
Hey all, I have a situation where I need to approach a REST service with a POST containing either XML or JSON. However, the data that I need to input has a strange (to me) model structure that I'm not sure how I would serialize without creating a ridiculous ToString override that I'd need to recreate for every other approach that would also use similar models, which is clearly a terrible idea.
The other alternative is creating a mass amount of models with a lot of identical names which would make putting in the necessary data an absolute nightmare.
While I know the basics of json serialization, I haven't had to deal with a situation like this before. Is there any way to make this much easier, or am I just stuck with coding myself through a nightmare every single time I need to approach this service?
Example of the data in json form:
32 Replies
What I would do is to just create the models, and by looking at the example JSON you've posted, you can re-use a lot of the components. By using ChatGPT, it suggested this data model:
@Malibloo
i wouldnt suggest using Object as a class name since it collides with System.object
Yeah,
Object
should and Fields
could be renamed to something more accurate of what you're trying to achieve using this model, just remember to annotate them properly within the parent component, e.g [JsonProperty("Object")]
.also you can easily whip up classes with websites like json2csharp
gives you a nice model and multiple options to make it whatever you want
Pardon, I had gotten completely sidetracked and missed this reply, thanks for the response.
The problem with this approach is that implementing the actual data will be a nightmare of continuously creating and assigning a mass amount of objects to fill out the hierarchy each time. This won't make coding it any simpler, sadly.
As a sidenote, the Element type seems to be reused amongst the different Subject parts, which would clearly have different fields, nor would they be able to contain themselves.
I can see a solution that'd end up with creating SubjectElement classes and whatnot, but that'd make it even more complex and I'm trying to find a simpler way.
Visual studio has this built in tbh
honestly the Element and Fields levels of the heirarchy seem superfluous
Its a little hard to suggest anything given the limited amount of information given. You've provided one example of how the data should be sent but I'm not sure how flexible you'd want your solution go be. I'd have to see more of the documentation or more examples to suggest something better if you don't want to use ToString
I assume there are a limited amount of types or command parameters and you could create building blocks of them to create the end result
Hmm, I figured the example was big enough. It shows that every class has a main of Element, a key outside of fields for some reason, then fields with the actual class properties, and also Objects, which I can only imagine is used as a dynamic object array, though I'll have to double check if one can have multiple attachments.
Best idea I can come up with a base class that'll have a key plus properties, but the key needs a different name for each class, so I need to somehow override the JsonProperty (is that even possible?), Then serialize all the fields that aren't null, and just use string magic to paste the element/field/objects thing.
It's still a miserable solution, but I imagine it could work for anything newly introduced?
As for documentation, there's none, just a page where you can manually enter the existing fields which will allow you to send the data, which I can intercept to get the json.
If necessary I can try to get all the fields I think are necessary.
Anyway, from the responses I'm gathering I think I can safely say that there's no real proper way to deal with this.
Considering that I'm fairly sure that the rest endpoint is also handled in .Net, I'd kill to see how they're actually handling it.
Gotcha - from your initial post I thought you had other structures you'd like to be able to handle with your solution. The only time I see an Id outside of the Fields property is in the outermost
"Element"
, the others do not have an Id outside of fields in the example given.
You may be able to write a custom converter for a class that could represent the structure you've given
You can store the ElementId as a nullable Tuple<string, int> and only write that property if it is not null
Fields could be a Dictionary
Objects could be an array of the base type
@Malibloo hopefully that could give you some ideas - I'm still not 100% sure this is the kind of answer you're looking forI was honestly hoping there was a much easier way to deal with custom json structures in the newish Text.Json, but it's been made clear that i have to get creative with both the models and customizing the serializer.
I definitely have more ideas thanks to you peeps though, much appreciated!
Was this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.Well, I'm back here again with the same issue. The idea I had before by just converting everything to a string manually is already biting me in the ass, and now that I'm already in double digits of models, this is becoming unwieldly.
The problem remains as it's in the first post. I have a model that can hold other models as collections and the structure of everything is always appended with the Element and Fields nodes.
My latest attempt was creating an interface that'd use the JsonConverter to turn the object into a json string and append the necessary nodes afterwards, which at first didn't work with inheritance, but after casting it to
object
it did. However, the problem remained that the collections also needed the same nodes, which they didn't.
This gave me the idea to create a custom converter, so it'd at the very least append the nodes, but after spending well over 4 hours trying with the help of this page https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to?pivots=dotnet-6-0 and not understanding what I was doing, or getting anywhere in general, I gave up on that.
The last idea I have is likely similar to how the Json Converter works in the first place, which is using reflection to get the field names and values when not null, and to use a stringbuilder to paste it all together, just so I can add those extra nodes. But I know for a fact that once you start doing things in reflection your program is going to slow down a lot, and what I'm creating this for will have input around the tens of thousands.
Again, I can not create the structure as it is shown in the models, because this would complicate the process by forcing myself to create those worthless objects myself every single time I need any of them in any situation. I also don't see a way around this.
Is there anyone with a new and/or better idea I can approach?What is the tldr here? Why can’t you use normal serializers again?
Was this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.Wow it'd be great if Discord actually sends me a notification.
The problem is that the model structure of the rest API is dumb (check first post for example), and I'll have many different models that need to all go through said API, and as such a need for generic methods, which don't play well with serialization.
Creating the complex hierarchy would mean needing to code for said complex hierarchy every time, which is extremely mistake prone and just annoying.
Best fitting solution I got uses reflection, but is slow as heck, which is annoying when you need to go through tens of thousands of entries.
So I'm kind of wondering if there's a better solution out there.
I’ll try and look over the post later seems like a overly complex scenario tbh; feels like any usage of the models is going to recapitulate the whole way down a strongly typed stack. This is about the only case where I’d start to recommend dynamic
I think it would be enlightening to see at least 1 object before it has been serialized. It may help to see another example of the JSON required so we can understand how generic you would like the solution
@Malibloo (so discord will notify you)
Can't say I've looked into dynamic before for this, mostly because one shouldn't really use dynamic. But yes, it is indeed an overly complex scenario. The simplest way I can explain is that every object needs additional json data that encapsulates it, and that every object can contain more objects.
As for the example, let's see...
These are more or less the models. The
JsonPropertyName
is currently there for importing sake.
It turns into something like:
As you can see, the "Element"
and "Fields"
is added to every object. Also it uses its own class name as well, which makes using generic, inheritance and dynamic a bit harder. And also if there's any other objects attached it's in its own "Objects"
array outside of "Fields"
.
I don't know what the person who made the API this way was thinking, but I doubt they made it in C#.
I hope this gives some insight? I'm sure I'll be missing something else, as I had thought the initial post had enough information.have you profiled reflection to claim it's slow?
reflection is the thing one is supposed to use for serialization
it's the primary use case for it
Didn't need to profile it, I just ran it, it took probably 20 times longer. Though I admit the code was shambly at the time, I imagine if I created some template with reflection once, instead of each time, it'd probably be faster.
if you cache some stuff it's gonna be like 5 times slower at most
which is acceptable
It's something worth pursuing if there's really no better alternative. I'm just really stuck on the idea that just adding some text to a json structure doesn't have to be this problematic.
if you're really fixed on perf, emit IL or write a source geberator
Another solution I wish I could use is to just append the json text to every object, which was the solution I used before figuring out that the API could hold multiple objects.
With source generator you mean making my own json converter?
if your objects significantly but consistently differ from their JSON representation, writing the conversions as a source generator might be easier
what I mean is if the mapping is predictable, but can't be expressed with existing tools, then it's an easy way
Source generator is a new term to me. I'mma look it up.
Is it intentional that
Subject.DossierId
and SubjectAttachment.FileStream
are not reflected in the JSON?
My reason for asking is to know whether we need to ignore some fields or notIntentional in the way that passing along an ID will sometimes not be useful, as a new entry will automatically gain the ID, whereas in other times where I need to update/put, I will need the ID. Currently it's ignored it if it's null.
As for the Filestream, it's technically required, but I forgot about it.
Also, for anyone who spent time on this so far, thank you. I really super appreciate it.
Was this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.This makes sense - I think we should be able to make something work with the information you've given so far. Were you able to find out what source generators are? I believe that solution could make this much simpler
Haven't gone through it all the way, nor have I played with it yet, my free time this week has been limited.
It does seem like it's what I need, but I've had that thought a few times now, hah.
Was this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.