package xpath import ( "errors" "fmt" "github.com/antchfx/xmlquery" ) // Get retrieves nodes from XML data using an XPath expression func Get(node *xmlquery.Node, path string) ([]*xmlquery.Node, error) { if node == nil { return nil, errors.New("nil node provided") } // Execute xpath query directly nodes, err := xmlquery.QueryAll(node, path) if err != nil { return nil, fmt.Errorf("failed to execute XPath query: %v", err) } return nodes, nil } // Set updates a single node in the XML data using an XPath expression func Set(node *xmlquery.Node, path string, value interface{}) error { if node == nil { return errors.New("nil node provided") } // Find the node to update nodes, err := xmlquery.QueryAll(node, path) if err != nil { return fmt.Errorf("failed to execute XPath query: %v", err) } if len(nodes) == 0 { return fmt.Errorf("no nodes found for path: %s", path) } // Update the first matching node updateNodeValue(nodes[0], value) return nil } // SetAll updates all nodes that match the XPath expression func SetAll(node *xmlquery.Node, path string, value interface{}) error { if node == nil { return errors.New("nil node provided") } // Find all nodes to update nodes, err := xmlquery.QueryAll(node, path) if err != nil { return fmt.Errorf("failed to execute XPath query: %v", err) } if len(nodes) == 0 { return fmt.Errorf("no nodes found for path: %s", path) } // Update all matching nodes for _, matchNode := range nodes { updateNodeValue(matchNode, value) } return nil } // Helper function to update a node's value func updateNodeValue(node *xmlquery.Node, value interface{}) { strValue := fmt.Sprintf("%v", value) // Handle different node types switch node.Type { case xmlquery.AttributeNode: // For attribute nodes, update the attribute value parent := node.Parent if parent != nil { for i, attr := range parent.Attr { if attr.Name.Local == node.Data { parent.Attr[i].Value = strValue break } } } case xmlquery.TextNode: // For text nodes, update the text content node.Data = strValue case xmlquery.ElementNode: // For element nodes, clear existing text children and add a new text node // First, remove all existing text children var nonTextChildren []*xmlquery.Node for child := node.FirstChild; child != nil; child = child.NextSibling { if child.Type != xmlquery.TextNode { nonTextChildren = append(nonTextChildren, child) } } // Clear all children node.FirstChild = nil node.LastChild = nil // Add a new text node textNode := &xmlquery.Node{ Type: xmlquery.TextNode, Data: strValue, Parent: node, } // Set the text node as the first child node.FirstChild = textNode node.LastChild = textNode // Add back non-text children for _, child := range nonTextChildren { child.Parent = node // If this is the first child being added back if node.FirstChild == textNode && node.LastChild == textNode { node.FirstChild.NextSibling = child child.PrevSibling = node.FirstChild node.LastChild = child } else { // Add to the end of the chain node.LastChild.NextSibling = child child.PrevSibling = node.LastChild node.LastChild = child } } } }