-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Support filtering property names on dictionary deserialization. #79059
Comments
Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis Issue DetailsIt seems there is currently no way to make JSON documents that reference a schema just deserializa. Repro{
"$schema": "http://example.org/myschema.json",
"Key1": "Value1",
"Key2": "Value2"
} var content = "<json above>";
return JsonSerializer.Deserialize<Dictionary<string, string>>(content); Expected BehaviorIt just works. Or, based on Actual BehaviorIt causes an exception:
In order to make it deserializable, the easiest option is to manually remove the schema node from the document: if (JsonNode.Parse(content) is JsonObject jsonObject)
{
if (jsonObject.Remove("$schema"))
content = jsonObject.ToJsonString();
} The more performant alternative would be to write a custom converter, but that seems like a lot of work. If the root type of the document weren't dictionary but a type I control, then presumably I could work this around by adding a property to my class and decorate it with
|
It seems reasonable to enable this scenario if we have substantial user asks for it. Feels like "future" for now wrt urgency. I wonder if this problem is scoped to JSON schema support or whether a general solution is needed i.e a feature to express that a set of property names should be omitted from (de)serialization. I assume this would be scoped to just dictionaries since we already have that for POCO properties ( |
This isn't a JSON Schema problem (at least not properly). JSON Schema puts no requirements on JSON data. Including
|
@terrajobst did you gave try with @JamesNK Newtonsoft one? Does it work there? |
Newtonsoft.Json errors:
I'd recommend people load the JSON into a JObject and remove the |
The System.Text.Json analog for that is |
A few observations:
|
I added a customer evidence section with my thoughts. |
That’s a good point. I didn’t call it out explicitly but that wasn’t my intent. Rather, I think we could decide to never support JSON schemas (ie offer validation or an OM for reading one) while still making this scenario possible by simply ignoring the well-known property name during The deserialization. So in that sense, I think of them of as orthogonal issues. |
Would we need to ignore other well-known metadata property names like |
This sounds like a slippery slope. |
I think one way to expose this would be like that: namespace System.Text.Json;
public sealed class JsonSerializerOptions
{
public JsonDictionaryKeyFilter DictionaryKeyFilter { get; set; }
}
public enum JsonDictionaryKeyFilter
{
None,
IgnoreMetadataNames // Ignores any keys starting with $, such as `$schema`.
} One could make this slightly more flexible by borrowing from the design of strongly typed strings and create an enum-like wrapper around a func, which offers some simple well-known versions: public readonly struct JsonDictionaryKeyFilter
{
public static JsonDictionaryKeyFilter None { get; }
public static JsonDictionaryKeyFilter IgnoreMetadataNames { get; }
public JsonDictionaryKeyFilter(Func<string, bool> filter);
public bool Include(string name);
} In either design, the default would be |
I think the predicate approach works. I would prefer if we could avoid the wrapper type and just use |
Why does everyone keep using Anyway... Carry on. |
Predicate predates Func and its use is largely restricted to Linq and a few generic collection methods. I don't believe we use it in any new APIs. |
I know it's older, which is why I'm confused that it's not used more. I don't mean to derail the conversation though. Carry on. |
#82012 seems to support the fact we need to ignore arbitrary property names. |
We might want to use a design similar to namespace System.Text.Json.Serialization;
public abstract class JsonDictionaryKeyFilter
{
public static JsonDictionaryKeyFilter IgnoreMetadataNames { get; }
public abstract bool IgnoreKey(ReadOnlySpan<byte> utf8Key);
}
namespace System.Text.Json;
public partial class JsonSerializerOptions
{
public JsonDictionaryKeyFilter? DictionaryKeyFilter { get; set; } = null;
} |
Would it make sense to also have an attribute that would allow people to opt-in into this over arbitrary properties and with arbitrary values, rather than only at the options level? Somewhat related, if this can only be set from the options would this mean that if you need this you'd be unable to use the generated |
Please be aware that in JsonSchema.Net, I use the STJ serializer to deserialize actual schemas, for which the If something is done here, please make the new behavior opt-in. |
That goes without saying. Making it the default behavior would be a breaking change. |
IMO that should be something that lives on JsonTypeInfo rather than global. Also we should think of cases like |
It wouldn't be possible to specify a filter value on the attribute level unless we either exposed a proxy enum type or the type of the
That's right, although this added step should have no impact on serialization performance. We might want to consider exposing a parameter in
I'm not too sure. This feature dovetails with reference handling which is also configured globally. I don't see why couldn't expose this in |
namespace System.Text.Json.Serialization;
public abstract class JsonDictionaryKeyFilter
{
public static JsonDictionaryKeyFilter IgnoreMetadataNames { get; }
public abstract bool ShouldIgnoreKey(string key);
} namespace System.Text.Json;
public partial class JsonSerializerOptions
{
public JsonDictionaryKeyFilter? DictionaryKeyFilter { get; set; } = null;
} |
I missed the API review, small question on this API: public abstract bool ShouldIgnoreKey(string key); Even though we don't care about UTF8, is there any benefit in not making that at least |
@terrajobst presumably this doesn't apply for dictionary for extension data properties? |
@krwq It shouldn't. These are always |
Can anybody help me with PR #87868 ? |
@AlexRadch currently going through a backlog of other things, will get to yours eventually. Thanks! |
I know this issue had the title changed to be Dictionary specific, but I feel like there's a related issue for non-dictionaries: [JsonDerivedType(typeof(DerivedType), typeDiscriminator: nameof(DerivedType))]
public abstract class BaseType
{
}
public sealed class DerivedType : BaseType
{
}
internal class Program
{
static void Main()
{
var text =
"""
{
"$type": "DerivedType",
"$schema": "http://example.org/myschema.json",
}
""";
var baseObj = JsonSerializer.Deserialize<BaseType>(text);
}
} The above results in:
I am not sure what a good workaround would be as adding a Should any fix for the Dictionary case also take the above case into account or is this a separate issue that needs its own issue number? |
That's a fair question. Originally, I scoped it to dictionaries because it's a built-in type that the user can't easily change the serialization behavior for, but I guess for custom objects a generic way to turn this off would be desirable as well, even though other workarounds exist. |
EDIT See #79059 (comment) for API proposal.
It seems there is currently no way to make JSON documents that reference a schema just deserializa.
Repro
Expected Behavior
It just works. Or, based on
JsonSerializerOptions
, it could be configured to just work.Actual Behavior
It causes an exception:
In order to make it deserializable, the easiest option is to manually remove the schema node from the document:
The more performant alternative would be to write a custom converter, but that seems like a lot of work. If the root type of the document weren't dictionary but a type I control, then presumably I could work this around by adding a property to my class and decorate it with
[JsonPropertyName("$schema")]
but that feels equally hacky.Customer Evidence
It’s not entirely clear to me if this is worth prioritizing. On the one hand, it seems a decent amount of our customers might use JSON schemas (based on this super scientific poll below).
On the other hand, it would only impact customers who use dictionaries as the document type. That combination might be quite rare and may not be be worth handling directly. And based on James’s comments below, JSON.NET never handled that either. I suggest we leave it open for a bit and see what responses we get but my gut feel says cutting seems fine.
The text was updated successfully, but these errors were encountered: