First, to address your issues with reference loops– The PreserveReferencesHandling setting controls whether Json.Net emits $id and $ref to track inter-object references. If you have this set to None and your object graph contains loops, then you will also need to set ReferenceLoopHandling to Ignore to prevent errors.
Now, to get Json.Net to ignore all object references altogether and only serialize primitive properties (except in your Pseudocontext class of course), you do need a custom Contract Resolver, as you suggested. But don’t worry, it is not as hard as you think. The resolver has the capability to inject a ShouldSerialize method for each property to control whether or not that property should be included in the output. So, all you need to do is derive your resolver from the default one, then override the CreateProperty method such that it sets ShouldSerialize appropriately. (You do not need a custom JsonConverter here, although it is possible to solve this problem with that approach. It would require quite a bit more code, however.)
Here is the code for the resolver:
class CustomResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty prop = base.CreateProperty(member, memberSerialization);
if (prop.DeclaringType != typeof(PseudoContext) &&
prop.PropertyType.IsClass &&
prop.PropertyType != typeof(string))
{
prop.ShouldSerialize = obj => false;
}
return prop;
}
}
Here is a full demo showing the resolver in action.
class Program
{
static void Main(string[] args)
{
// Set up some dummy data complete with reference loops
Thing t1 = new Thing { Id = 1, Name = "Flim" };
Thing t2 = new Thing { Id = 2, Name = "Flam" };
Widget w1 = new Widget
{
Id = 5,
Name = "Hammer",
IsActive = true,
Price = 13.99M,
Created = new DateTime(2013, 12, 29, 8, 16, 3),
Color = Color.Red,
};
w1.RelatedThings = new List<Thing> { t2 };
t2.RelatedWidgets = new List<Widget> { w1 };
Widget w2 = new Widget
{
Id = 6,
Name = "Drill",
IsActive = true,
Price = 45.89M,
Created = new DateTime(2014, 1, 22, 2, 29, 35),
Color = Color.Blue,
};
w2.RelatedThings = new List<Thing> { t1 };
t1.RelatedWidgets = new List<Widget> { w2 };
// Here is the container class we wish to serialize
PseudoContext pc = new PseudoContext
{
Things = new List<Thing> { t1, t2 },
Widgets = new List<Widget> { w1, w2 }
};
// Serializer settings
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ContractResolver = new CustomResolver();
settings.PreserveReferencesHandling = PreserveReferencesHandling.None;
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
settings.Formatting = Formatting.Indented;
// Do the serialization and output to the console
string json = JsonConvert.SerializeObject(pc, settings);
Console.WriteLine(json);
}
class PseudoContext
{
public List<Thing> Things { get; set; }
public List<Widget> Widgets { get; set; }
}
class Thing
{
public int Id { get; set; }
public string Name { get; set; }
public List<Widget> RelatedWidgets { get; set; }
}
class Widget
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public decimal Price { get; set; }
public DateTime Created { get; set; }
public Color Color { get; set; }
public List<Thing> RelatedThings { get; set; }
}
enum Color { Red, White, Blue }
}
Output:
{
"Things": [
{
"Id": 1,
"Name": "Flim"
},
{
"Id": 2,
"Name": "Flam"
}
],
"Widgets": [
{
"Id": 5,
"Name": "Hammer",
"IsActive": true,
"Price": 13.99,
"Created": "2013-12-29T08:16:03",
"Color": 0
},
{
"Id": 6,
"Name": "Drill",
"IsActive": true,
"Price": 45.89,
"Created": "2014-01-22T02:29:35",
"Color": 2
}
]
}
Hope this is in the ballpark of what you were looking for.