How to find and update a record in a recursive model?

I have the following classes:
public sealed record NodeItem {
public string Name { get; set; } = string.Empty;
public string Value { get; set; } = string.Empty;
public List<NodeAttribute> Attributes { get; set; } = [];
public List<NodeItem> Children { get; set; } = [];
}

public sealed record NodeAttribute {
public string Name { get; set; } = string.Empty;
public string Value { get; set; } = string.Empty;
}
public sealed record NodeItem {
public string Name { get; set; } = string.Empty;
public string Value { get; set; } = string.Empty;
public List<NodeAttribute> Attributes { get; set; } = [];
public List<NodeItem> Children { get; set; } = [];
}

public sealed record NodeAttribute {
public string Name { get; set; } = string.Empty;
public string Value { get; set; } = string.Empty;
}
I'm using it in this code:
NodeItem? _node;
NodeItem? _selectedNode;

private void OnUpload(NodeItem node) {
_node = node;
}

private void OnNodeSelected(NodeItem node) {
_selectedNode = node;
}

private void OnNodeUpdated(NodeItem node) {
_selectedNode = node;
}
NodeItem? _node;
NodeItem? _selectedNode;

private void OnUpload(NodeItem node) {
_node = node;
}

private void OnNodeSelected(NodeItem node) {
_selectedNode = node;
}

private void OnNodeUpdated(NodeItem node) {
_selectedNode = node;
}
I thought I was sharing the reference, but that wasn't right. So either I figure out how to properly share the node through a pointer or I need to search through _node for the proper node to select or update. Any suggestions? Any articles to help me understand this better?
9 Replies
Saiyanslayer
Saiyanslayer3d ago
Background info: these nodes represent a XML file. It might be easier to rely on XDocument instead, but I want to learn more about recursive design.
Clint
Clint3d ago
What is it you're actually trying to do here? The code isn't super clear about what the problem you're facing is. My recommendation would be to rely on XDocument as you get all the tree traversal built-in via Descendants .
Saiyanslayer
Saiyanslayer3d ago
Making a web app to edit xml. I have a component that shows the data of the selected node That component allows editing and returns the changed node in OnNodeUpdated
Clint
Clint3d ago
Hmmmmm, unfortunately doesn't really give me the info I'd need to understand your precise challenge with this here, anything I'd be likely to suggest would be too subjective.
Saiyanslayer
Saiyanslayer3d ago
Ya I was worried about that. I can share the project if that helps, or show the razor html
SleepWellPupper
You wrote
I thought I was sharing the reference, but that wasn't right
What do you mean by this? I'm also having trouble understanding your actual problem here. Are there exceptions? Unexpected behaviors? $details please
MODiX
MODiX3d ago
When you ask a question, make sure you include as much detail as possible. Such as code, the issue you are facing, what you expect the result to be, what .NET version you are using and what platform/environment (if any) are relevant to your question. Upload code here https://paste.mod.gg/, save, and copy the link into chat for others to see your shared code! (see $code for more information on how to paste your code)
SleepWellPupper
(because btw, you're not traversing your tree in the methods you shared so any help we could give would be guesswork at best) If you're having issues with traversing the tree and seeing e.g. unexpected descendants, I'd suggest sharing that specific bit of code. Your tree data structure seems fine, although there is imo no point using a record for this.
Saiyanslayer
Saiyanslayer2d ago
My issue was finding and editing a node especially if two or more nodes had the same name without any id. Clint made a good point and I've refactored the code
public sealed record NodeItem {
public XElement XmlElement { get; init; }
public string Name => XmlElement.Name.LocalName;
public string Value => !XmlElement.HasElements && !string.IsNullOrWhiteSpace(XmlElement.Value)
? XmlElement.Value
: string.Empty;

public bool HasChildren => XmlElement.HasElements;
public bool HasAttributes => XmlElement.HasAttributes;

public List<NodeItem> Children => XmlElement.Elements().Select(x => new NodeItem(x)).ToList();
public List<NodeAttribute> Attributes
=> XmlElement.Attributes().Select(x => new NodeAttribute(x.Name.LocalName, x.Value)).ToList();
public NodeItem(XElement element) => XmlElement = element;
public NodeItem Copy() => new NodeItem(this);
public void SetValue(string value) => XmlElement.SetValue(value);
}

public sealed record NodeAttribute(string Name, string Value);
public sealed record NodeItem {
public XElement XmlElement { get; init; }
public string Name => XmlElement.Name.LocalName;
public string Value => !XmlElement.HasElements && !string.IsNullOrWhiteSpace(XmlElement.Value)
? XmlElement.Value
: string.Empty;

public bool HasChildren => XmlElement.HasElements;
public bool HasAttributes => XmlElement.HasAttributes;

public List<NodeItem> Children => XmlElement.Elements().Select(x => new NodeItem(x)).ToList();
public List<NodeAttribute> Attributes
=> XmlElement.Attributes().Select(x => new NodeAttribute(x.Name.LocalName, x.Value)).ToList();
public NodeItem(XElement element) => XmlElement = element;
public NodeItem Copy() => new NodeItem(this);
public void SetValue(string value) => XmlElement.SetValue(value);
}

public sealed record NodeAttribute(string Name, string Value);
public sealed class NodeTree {
public XDocument? Xml { get; set; } = new XDocument();
public NodeItem? Root => Xml is null || Xml.Document is null || Xml.Document.Root is null
? null
: new NodeItem(Xml.Document.Root);

public void UpdateNode(NodeItem oldNode, NodeItem newNode) {
if ( Xml is null || Xml.Document is null) { return; }

Xml.Document.Descendants().First(x => x == oldNode.XmlElement).ReplaceWith(newNode.XmlElement);
}
}
public sealed class NodeTree {
public XDocument? Xml { get; set; } = new XDocument();
public NodeItem? Root => Xml is null || Xml.Document is null || Xml.Document.Root is null
? null
: new NodeItem(Xml.Document.Root);

public void UpdateNode(NodeItem oldNode, NodeItem newNode) {
if ( Xml is null || Xml.Document is null) { return; }

Xml.Document.Descendants().First(x => x == oldNode.XmlElement).ReplaceWith(newNode.XmlElement);
}
}
I wrapped the XDocument stuff because I wanted flexibility on how to access the info: if I change my mind on which package or method to do something, I'll have one place to change it, not dozens This is what solved my issue:
Xml.Document.Descendants().First(x => x == oldNode.XmlElement).ReplaceWith(newNode.XmlElement);
Xml.Document.Descendants().First(x => x == oldNode.XmlElement).ReplaceWith(newNode.XmlElement);
Want results from more Discord servers?
Add your server