ServiceStack.Text 具有 Parent/Children 循环引用的 StackOverflowException
ServiceStack.Text StackOverflowException with Parent/Children circular references
简单 (1:1) parent/child 循环引用的序列化有效,如 mythz 回答 here 中所述。但是,当我尝试使用将 ref 返回给其父级的子级列表来序列化父级时,我得到了 WhosebugException。
我在 GitHub.
上的 commonGib 存储库中将其精简为 tests and test classes
测试:
/// <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());
}
}
测试类:
#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
没有。循环引用不起作用。即使在您的第一个序列化示例 (Simple_ServiceStack
) 中,我也得到:
{"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":}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
那是错的和断(见最后"Parent":}
)。 (用 ServiceStack.Text 4.0.38 测试)
如评论之一所述:
Your example dto is not using circular references - it is using different objects for each link property
根据您链接的问题的建议,使用 Json.NET 和元 ID。
简单 (1:1) parent/child 循环引用的序列化有效,如 mythz 回答 here 中所述。但是,当我尝试使用将 ref 返回给其父级的子级列表来序列化父级时,我得到了 WhosebugException。
我在 GitHub.
上的 commonGib 存储库中将其精简为 tests and test classes测试:
/// <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());
}
}
测试类:
#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
没有。循环引用不起作用。即使在您的第一个序列化示例 (Simple_ServiceStack
) 中,我也得到:
{"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":}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
那是错的和断(见最后"Parent":}
)。 (用 ServiceStack.Text 4.0.38 测试)
如评论之一所述:
Your example dto is not using circular references - it is using different objects for each link property
根据您链接的问题的建议,使用 Json.NET 和元 ID。