0

Serialization of simple (1:1) parent/child circular references works, as noted in mythz answer here. However, I am getting a StackOverflowException when trying to serialize a parent with a list of children who hold a ref back to their parent.

I have condensed this down to bare-bones tests and test classes at my commonGib repo on GitHub.

Tests:

/// <summary>
/// Trivial business classes with a simple 1:1 circular relationship, i.e. Parent.Child, Child.Parent.
/// </summary>
[TestMethod]
public void Simple_ServiceStack()
{
    var parent = new SimpleParent() { Text = "Foo" };
    var child = new SimpleChild() { Number = 2 };

    parent.Child = child;
    child.Parent = parent;

    var parentJson = JsonSerializer.SerializeToString(parent);

    var parentTest = JsonSerializer.DeserializeFromString<SimpleParent>(parentJson);

    Assert.IsTrue(parentTest.TextEqualsFoo());
    Assert.IsTrue(parentTest.Child.NumberEqualsTwo());
}

/// <summary>
/// Test business classes more complex by having a parent with a list of children, as opposed
/// to a 1:1 relationship, i.e. Parent.Children instead of Parent.Child.
/// </summary>
[TestMethod]
public void Complex_ServiceStack()
{
    var parent = new ComplexParent() { Text = "Foo" };
    var child = new ComplexChild() { Number = 2 };

    parent.Children = new List<ComplexChild>() { child };
    child.Parent = parent;

    var parentJson = JsonSerializer.SerializeToString(parent); //Throws stack overflow

    var parentTest = JsonSerializer.DeserializeFromString<ComplexParent>(parentJson);

    Assert.IsTrue(parentTest.TextEqualsFoo());
    foreach (var childTest in parentTest.Children)
    {
        Assert.IsTrue(childTest.NumberEqualsTwo());
    }
}

/// <summary>
/// Real-world business classes wrapped around a state class.
/// </summary>
[TestMethod]
public void VeryComplex_ServiceStack()
{
    var parentState = new VeryComplexParentState() { Text = "Foo" };
    var childState = new VeryComplexChildState() { Number = 2 };

    var parent = new VeryComplexParent() { State = parentState };
    var child = new VeryComplexChild() { State = childState };

    parent.Children = new List<VeryComplexChild>() { child };
    child.Parent = parent;

    var parentJson = JsonSerializer.SerializeToString(parent); //Throws stack overflow

    var parentTest = JsonSerializer.DeserializeFromString<ComplexParent>(parentJson);

    Assert.IsTrue(parentTest.TextEqualsFoo());
    foreach (var childTest in parentTest.Children)
    {
        Assert.IsTrue(childTest.NumberEqualsTwo());
    }
}

Test classes:

#region Simple Parent/Child

public class SimpleParent
{
    public string Text { get; set; }
    public SimpleChild Child { get; set; }

    /// <summary>
    /// This is like a validation rule on the state wrapper.
    /// </summary>
    public bool TextEqualsFoo()
    {
        return Text == "Foo";
    }
}

public class SimpleChild
{
    public int Number { get; set; }
    public SimpleParent Parent { get; set; }

    /// <summary>
    /// This is like a validation rule on the state wrapper.
    /// </summary>
    public bool NumberEqualsTwo()
    {
        return Number == 2;
    }
}

#endregion

#region Complex Parent/Child

public class ComplexParent
{
    public string Text { get; set; }
    public List<ComplexChild> Children { get; set; }

    /// <summary>
    /// This is like a validation rule on the state wrapper.
    /// </summary>
    public bool TextEqualsFoo()
    {
        return Text == "Foo";
    }
}

public class ComplexChild
{
    public int Number { get; set; }
    public ComplexParent Parent { get; set; }

    /// <summary>
    /// This is like a validation rule on the state wrapper.
    /// </summary>
    public bool NumberEqualsTwo()
    {
        return Number == 2;
    }
}

#endregion

#region Very Complex Parent/Child

public abstract class BaseSerializationTestClass<TState>// : BaseSerializationTestClass
{
    public TState State { get; set; }
}


public class VeryComplexParent : BaseSerializationTestClass<VeryComplexParentState>
{
    /// <summary>
    /// This is like a validation rule on the state wrapper.
    /// </summary>
    public bool TextEqualsFoo()
    {
        return State != null && State.Text == "Foo";
    }

    public List<VeryComplexChild> Children { get; set; }
}

public class VeryComplexParentState
{
    public string Text { get; set; }

    public List<VeryComplexChildState> MyChildrenState { get; set; }
}

public class VeryComplexChild : BaseSerializationTestClass<VeryComplexChildState>
{
    /// <summary>
    /// This is like a validation rule on the state wrapper.
    /// </summary>
    public bool NumberEqualsTwo()
    {
        return State != null && State.Number == 2;
    }

    public VeryComplexParent Parent { get; set; }
}

public class VeryComplexChildState
{
    public int Number { get; set; }

    public VeryComplexParentState MyAState { get; set; }
}

#endregion
Community
  • 1
  • 1
  • Can you please rewrite this question to bring the relevant information from the links into your question? The more work you do for us to make answering easy the better answers you will get. – Enigmativity Apr 08 '15 at 14:18
  • Ah, sorry. I didn't want to spam the entire code in the question. I can do so. –  Apr 08 '15 at 14:20
  • No, don't do that - just add the **relevant** parts. – Enigmativity Apr 08 '15 at 14:21
  • It *is* the relevant parts. I had already culled down the code in my GitHub repo. –  Apr 08 '15 at 14:24

1 Answers1

1

No. Circular references don't work. Even in your first example (Simple_ServiceStack) for the serialization I get:

{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":{"Text":"Foo","Child":{"Number":2,"Parent":}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}

That is wrong and broken (see the last "Parent":}). (tested with ServiceStack.Text 4.0.38)

As written in one of the comments:

Your example dto is not using circular references - it is using different objects for each link property

As suggested by the question you linked, use Json.NET with meta ids.

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • Hah, I didn't realize the Json text looked like that. I just was going on the test passing. I can't just go with Json.NET, as it doesn't work in Azure for me (deployed...it works locally). I need concrete type information, which has a bug in it in the version that Azure Mobile Services has frozen in the AMS server environment (6.0.4 I think). –  Apr 08 '15 at 14:50
  • http://stackoverflow.com/questions/27080363/missingmethodexception-with-newtonsoft-json-when-using-typenameassemblyformat-wi –  Apr 08 '15 at 14:53
  • @ibgib Then sadly I can't help :-( – xanatos Apr 08 '15 at 14:56
  • 1
    I have migrated my Azure Mobile Services back end to the new Azure Web/Mobile App Service, and it indeed has the environment without the Json.NET limitation of 6.0.4. And the serialization is now working deployed! (AFAICT :X) Anyone else who is having versioning issues with Azure Mobile Services should definitely check out the new and improved service from Azure and see if that works for you. Thanks for your help! –  Apr 10 '15 at 12:10
  • @ibgib No problem, and happy you solved your problem. – xanatos Apr 10 '15 at 12:17