1

I have a third-party class (lets call it Class1) which I need to serialize to JSON. If I try to do this as is, I either receive StackOverflowException or JsonSerializationException with message "Self referencing loop detected with type". I've tried to apply the following settings for the JsonConvert but it didn't help me to avoid StackOverflowException

var settings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.None,
    PreserveReferencesHandling = PreserveReferencesHandling.None,
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};

After decompiling the Class1 I found out that a lot of properties of the Class1 are marked with [ScriptIgnore] attribute which is an analogue of [JsonIgnore] and is used by System.Web.Script.Serialization.JavaScriptSerializer but I need to use Newtonsoft serializer.

As far as Class1 is a third-party class I can't add [JsonIgnore] attribute to the needed properties. I know that I can develop my own implementation of IContractResolver, and handle the problematic properties there, but I'd like to avoid this option. Maybe there is a way somehow configure Newtonsoft serializer to take into consideration [ScriptIgnore] attribute as well as [JsonIgnore]. And do this configuration like it is done with ReferenceLoopHandling?

I would appreciate for any ideas.

  • 1
    There's no configuration option for this. If you [search on github](https://github.com/JamesNK/Newtonsoft.Json/search?q=ScriptIgnoreAttribute&unscoped_q=ScriptIgnoreAttribute), `ScriptIgnoreAttribute` doesn't even appear in the Json.NET source tree. A [custom contract resolver](https://www.newtonsoft.com/json/help/html/contractresolver.htm#CustomIContractResolverExamples) is the way to go. Should I make it an answer? – dbc Nov 14 '18 at 19:13
  • 1
    Also, can you explain why you don't want to implement a custom contract resolver? It's actually really easy. – dbc Nov 14 '18 at 19:24
  • 1
    To make Json.Net respect `[ScriptIgnore]` the same as `[JsonIgnore]` can be done easily with a custom contract resolver as @dbc said. It is just a few lines of code. If you reject that solution out of hand, then the only other options I can think of would be forking and modifying the Json.Net source code to add support for the attribute, or mapping your `Class1` to a surrogate DTO class (or building up a JObject from it) and serializing that instead. – Brian Rogers Nov 14 '18 at 21:09
  • 1
    You could also try setting `PreserveReferencesHandling` to `Objects`; that should get rid of the `StackOverflowException` problem, but it will change the JSON output quite a bit. See [What is the difference between PreserveReferencesHandling and ReferenceLoopHandling in Json.Net?](https://stackoverflow.com/q/23453977/10263) for more details. – Brian Rogers Nov 14 '18 at 21:32
  • @BrianRogers , thank you, I've tried to use `Objects` as `PreserveReferencesHandling` but unfortunately without result, my issues still reproduces. – Alexander Lysenko Nov 15 '18 at 08:21
  • @dbc, @BrianRogers, thank you guys, I've already implemented my own `IContractResolver` and it's really not so big and complex. I think in my situation it's the best solution. – Alexander Lysenko Nov 15 '18 at 08:25
  • @dbc, yes, please make your option as answer, I'll vote for it – Alexander Lysenko Nov 15 '18 at 08:26

1 Answers1

2

There's no configuration option for this. If you search on github, ScriptIgnoreAttribute doesn't even appear in the Json.NET source tree.

Even though you don't want to implement your own IContractResolver, this would be the straightforward solution and very easy. First, define the following subclass of DefaultContractResolver as follows:

public class ScriptIgnoreContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        if (!property.Ignored)
        {
            if (property.AttributeProvider.GetAttributes(true).Any(p => p.GetType().FullName == "System.Web.Script.Serialization.ScriptIgnoreAttribute"))
            {
                property.Ignored = true;
            }
        }
        return property;
    }
}

Then serialize as follows:

// Define a static member
static readonly IContractResolver myResolver = new ScriptIgnoreContractResolver();

// And use it in your serialization method
var settings = new JsonSerializerSettings
{
    ContractResolver = myResolver,
};
var json = JsonConvert.SerializeObject(rootObject, settings);

You may want to cache the contract resolver for best performance.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • My option is a little bit more compact: `public class ScriptIgnoreCompatibleContractResolver : DefaultContractResolver { protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { var property = base.CreateProperty(member, memberSerialization); property.Ignored = member.GetCustomAttribute() != null; return property; } }` maybe use it in the answer? Sorry for formatting, I can't format the code properly in the reply. – Alexander Lysenko Nov 15 '18 at 08:35
  • @AlexanderLysenko - I think I wanted to avoid needing to add a reference to `System.Web.Extensions.dll`, which is why I checked the type name. But your way could reset `Ignored` from `true` to `false` if it had been ignored for some other reason. Is that what you want? – dbc Nov 15 '18 at 09:07
  • Ok, now I see that my option has its cons, so just forget my comment :) – Alexander Lysenko Nov 15 '18 at 16:39