Implement xpath (by calling library)
This commit is contained in:
@@ -1,98 +1,133 @@
|
||||
package xpath
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
// XPathStep represents a single step in an XPath expression
|
||||
type XPathStep struct {
|
||||
Type StepType
|
||||
Name string
|
||||
Predicate *Predicate
|
||||
}
|
||||
|
||||
// StepType defines the type of XPath step
|
||||
type StepType int
|
||||
|
||||
const (
|
||||
// RootStep represents the root step (/)
|
||||
RootStep StepType = iota
|
||||
// ChildStep represents a child element step (element)
|
||||
ChildStep
|
||||
// RecursiveDescentStep represents a recursive descent step (//)
|
||||
RecursiveDescentStep
|
||||
// WildcardStep represents a wildcard step (*)
|
||||
WildcardStep
|
||||
// PredicateStep represents a predicate condition step ([...])
|
||||
PredicateStep
|
||||
"github.com/antchfx/xmlquery"
|
||||
)
|
||||
|
||||
// PredicateType defines the type of XPath predicate
|
||||
type PredicateType int
|
||||
|
||||
const (
|
||||
// IndexPredicate represents an index predicate [n]
|
||||
IndexPredicate PredicateType = iota
|
||||
// LastPredicate represents a last() function predicate
|
||||
LastPredicate
|
||||
// LastMinusPredicate represents a last()-n predicate
|
||||
LastMinusPredicate
|
||||
// PositionPredicate represents position()-based predicates
|
||||
PositionPredicate
|
||||
// AttributeExistsPredicate represents [@attr] predicate
|
||||
AttributeExistsPredicate
|
||||
// AttributeEqualsPredicate represents [@attr='value'] predicate
|
||||
AttributeEqualsPredicate
|
||||
// ComparisonPredicate represents element comparison predicates
|
||||
ComparisonPredicate
|
||||
)
|
||||
|
||||
// Predicate represents a condition in XPath
|
||||
type Predicate struct {
|
||||
Type PredicateType
|
||||
Index int
|
||||
Offset int
|
||||
Attribute string
|
||||
Value string
|
||||
Expression string
|
||||
}
|
||||
|
||||
// XMLNode represents a node in the result set with its value and path
|
||||
type XMLNode struct {
|
||||
Value interface{}
|
||||
Path string
|
||||
}
|
||||
|
||||
// ParseXPath parses an XPath expression into a series of steps
|
||||
func ParseXPath(path string) ([]XPathStep, error) {
|
||||
if path == "" {
|
||||
return nil, errors.New("empty path")
|
||||
}
|
||||
|
||||
// This is just a placeholder implementation for the tests
|
||||
// The actual implementation would parse the XPath expression
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// Get retrieves nodes from XML data using an XPath expression
|
||||
func Get(data interface{}, path string) ([]XMLNode, error) {
|
||||
if data == "" {
|
||||
return nil, errors.New("empty XML data")
|
||||
func Get(node *xmlquery.Node, path string) ([]*xmlquery.Node, error) {
|
||||
if node == nil {
|
||||
return nil, errors.New("nil node provided")
|
||||
}
|
||||
|
||||
// This is just a placeholder implementation for the tests
|
||||
// The actual implementation would evaluate the XPath against the XML
|
||||
return nil, errors.New("not implemented")
|
||||
// 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 node in the XML data using an XPath expression
|
||||
func Set(xmlData string, path string, value interface{}) (string, error) {
|
||||
// This is just a placeholder implementation for the tests
|
||||
// The actual implementation would modify the XML based on the XPath
|
||||
return "", errors.New("not implemented")
|
||||
// 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 matching an XPath expression in the XML data
|
||||
func SetAll(xmlData string, path string, value interface{}) (string, error) {
|
||||
// This is just a placeholder implementation for the tests
|
||||
// The actual implementation would modify all matching nodes
|
||||
return "", errors.New("not implemented")
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user