134 lines
3.2 KiB
Go
134 lines
3.2 KiB
Go
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
|
|
}
|
|
}
|
|
}
|
|
}
|