ツリー構造を成すクラスの親クラス。
public abstract class Node { protected const string Slash = "/"; [XmlIgnore] public Node Parent { get; set; } public Node GetReferencedNode(string referencePath) { var path = referencePath; var target = this; while (path != "") { (path, target) = target.GetNode(path); } return target; } public abstract void SetParent(Node parent); protected abstract (string afterPath, Node result) GetNode(string path); protected (bool, string, Node) IsTargetNodeSelfOrParent(string path) { if (path.StartsWith($"..{Slash}")) { var afterPath = path.Substring(2 + Slash.Length); return (true, afterPath, Parent); } if (path.StartsWith($".{Slash}")) { var afterPath = path.Substring(1 + Slash.Length); return (true, afterPath, this); } return (false, path, null); } protected (bool, string) IsTargetNode(string path, string targetItemName) { if (path == targetItemName) { return (true, ""); } if (path.StartsWith($"{targetItemName}{Slash}")) { var index = path.IndexOf(Slash); var afterPath = path.Substring(index + 1); return (true, afterPath); } return (false, path); } protected (bool, string, int) IsTargetNodeCollection(string path, string targetItemName) { if (path == targetItemName) { return (true, "", 0); } if (path.StartsWith($"{targetItemName}{Slash}")) { var index = path.IndexOf(Slash); var afterPath = path.Substring(index + 1); return (true, afterPath, 0); } if (path.StartsWith($"{targetItemName}[")) { var arrayIndexStart = path.IndexOf("["); var arrayIndexEnd = path.IndexOf("]"); var arrayIndex = int.Parse(path.Substring(arrayIndexStart + 1, arrayIndexEnd - arrayIndexStart - 1)); var index = path.IndexOf(Slash); if (index < 0) return (true, "", arrayIndex); var afterPath = path.Substring(index + 1); return (true, afterPath, arrayIndex); } return (false, path, -1); } }
ツリー構造のサンプル。
[Serializable] public class ClassA : Node { public ClassB B ; public ClassC C { get; set; } public List<ClassD> D { get; set; } public override void SetParent(Node parent) { this.Parent = parent; B.SetParent(this); C.SetParent(this); foreach (var d in D) d.SetParent(this); } protected override (string, Node) GetNode(string path) { { var (isTarget, afterPath, node) = base.IsTargetNodeSelfOrParent(path); if (isTarget) { return (afterPath, node); } } { var (isTarget, afterPath) = base.IsTargetNode(path, nameof(B)); if (isTarget) { return (afterPath, B); } } { var (isTarget, afterPath) = base.IsTargetNode(path, nameof(C)); if (isTarget) { return (afterPath, C); } } { var (isTarget, afterPath, index) = base.IsTargetNodeCollection(path, nameof(D)); if (isTarget) { return (afterPath, D[index]); } } return (path, null); } } [Serializable] public class ClassB : Node { public string Value { get; set; } public ClassB BB { get; set; } public override void SetParent(Node parent) { this.Parent = parent; BB?.SetParent(this); } protected override (string, Node) GetNode(string path) { { var (isTarget, afterPath, node) = base.IsTargetNodeSelfOrParent(path); if (isTarget) { return (afterPath, node); } } { var (isTarget, afterPath) = base.IsTargetNode(path, nameof(BB)); if (isTarget) { return (afterPath, BB); } } return (path, null); } } [Serializable] public class ClassC : Node { public string Value { get; set; } public ClassC CC { get; set; } public override void SetParent(Node parent) { this.Parent = parent; CC?.SetParent(this); } protected override (string, Node) GetNode(string path) { { var (isTarget, afterPath, node) = base.IsTargetNodeSelfOrParent(path); if (isTarget) { return (afterPath, node); } } { var (isTarget, afterPath) = base.IsTargetNode(path, nameof(CC)); if (isTarget) { return (afterPath, CC); } } return (path, null); } } [Serializable] public class ClassD : Node { public ClassDD DD { get; set; } public override void SetParent(Node parent) { this.Parent = parent; DD?.SetParent(this); } protected override (string, Node) GetNode(string path) { { var (isTarget, afterPath, node) = base.IsTargetNodeSelfOrParent(path); if (isTarget) { return (afterPath, node); } } { var (isTarget, afterPath) = base.IsTargetNode(path, nameof(DD)); if (isTarget) { return (afterPath, DD); } } return (path, null); } } [Serializable] public class ClassDD : Node { public string Value { get; set; } public override void SetParent(Node parent) { this.Parent = parent; } protected override (string, Node) GetNode(string path) { { var (isTarget, afterPath, node) = base.IsTargetNodeSelfOrParent(path); return (afterPath, node); } } }
void Main() { var a = new ClassA() { B = new ClassB { Value = "B.Value", BB = new ClassB { Value = "BB.Value" } }, C = new ClassC { Value = "C.Value", CC = new ClassC { Value = "CC.Value" } }, D = new List<ClassD> { new ClassD{ DD = new ClassDD{ Value = "DD[0].Value"}}, new ClassD{ DD = new ClassDD{ Value = "DD[1].Value"}}, new ClassD{ DD = new ClassDD{ Value = "DD[2].Value"}}, } }; a.SetParent(null); // 途中XMLを介してみる var writer = new StringWriter(); var serializer = new XmlSerializer(typeof(ClassA)); serializer.Serialize(writer, a); var xml = writer.ToString(); xml.Dump(); var deserialized = (ClassA)serializer.Deserialize(new StringReader(xml)); deserialized.SetParent(null); var path ="../../D[0]/DD"; var target = deserialized.B.BB.GetReferencedNode(path); target.Dump(); }
取得結果。