Implement parsing xml to and from lua

A lot more complex than json.........
This commit is contained in:
2025-03-26 01:36:49 +01:00
parent e31c0e4e8f
commit e5092edf53
2 changed files with 451 additions and 53 deletions

View File

@@ -59,68 +59,55 @@ func (p *XMLProcessor) ProcessContent(content string, path string, luaExpr strin
}
log.Printf("%#v", result)
// Apply modification
// if node.Type == xmlquery.AttributeNode {
// // For attribute nodes, update the attribute value
// node.Parent.Attr = append([]xmlquery.Attr{}, node.Parent.Attr...)
// for i, attr := range node.Parent.Attr {
// if attr.Name.Local == node.Data {
// node.Parent.Attr[i].Value = newValue
// break
// }
// }
// } else if node.Type == xmlquery.TextNode {
// // For text nodes, update the text content
// node.Data = newValue
// } else {
// // For element nodes, replace inner text
// // Simple approach: set the InnerText directly if there are no child elements
// if node.FirstChild == nil || (node.FirstChild != nil && node.FirstChild.Type == xmlquery.TextNode && node.FirstChild.NextSibling == nil) {
// if node.FirstChild != nil {
// node.FirstChild.Data = newValue
// } else {
// // Create a new text node and add it as the first child
// textNode := &xmlquery.Node{
// Type: xmlquery.TextNode,
// Data: newValue,
// }
// node.FirstChild = textNode
// }
// } else {
// // Complex case: node has mixed content or child elements
// // Replace just the text content while preserving child elements
// // This is a simplified approach - more complex XML may need more robust handling
// for child := node.FirstChild; child != nil; child = child.NextSibling {
// if child.Type == xmlquery.TextNode {
// child.Data = newValue
// break // Update only the first text node
// }
// }
// }
// }
// Apply modification based on the result
if updatedValue, ok := result.(string); ok {
// If the result is a simple string, update the node value directly
xpath.Set(doc, path, updatedValue)
} else if nodeData, ok := result.(map[string]interface{}); ok {
// If the result is a map, apply more complex updates
updateNodeFromMap(node, nodeData)
}
modCount++
}
// Serialize the modified XML document to string
// if doc.FirstChild != nil && doc.FirstChild.Type == xmlquery.DeclarationNode {
// // If we have an XML declaration, start with it
// declaration := doc.FirstChild.OutputXML(true)
// // Remove the firstChild (declaration) before serializing the rest of the document
// doc.FirstChild = doc.FirstChild.NextSibling
// return declaration + doc.OutputXML(true), modCount, matchCount, nil
// }
if doc.FirstChild != nil && doc.FirstChild.Type == xmlquery.DeclarationNode {
// If we have an XML declaration, start with it
declaration := doc.FirstChild.OutputXML(true)
// Remove the firstChild (declaration) before serializing the rest of the document
doc.FirstChild = doc.FirstChild.NextSibling
return declaration + doc.OutputXML(true), modCount, matchCount, nil
}
// return doc.OutputXML(true), modCount, matchCount, nil
return "", modCount, matchCount, nil
return doc.OutputXML(true), modCount, matchCount, nil
}
// ToLua converts XML node values to Lua variables
func (p *XMLProcessor) ToLua(L *lua.LState, data interface{}) error {
table, err := ToLua(L, data)
if err != nil {
return err
// Check if data is an xmlquery.Node
node, ok := data.(*xmlquery.Node)
if !ok {
return fmt.Errorf("expected xmlquery.Node, got %T", data)
}
// Create a simple table with essential data
table := L.NewTable()
// For element nodes, just provide basic info
L.SetField(table, "type", lua.LString(nodeTypeToString(node.Type)))
L.SetField(table, "name", lua.LString(node.Data))
L.SetField(table, "value", lua.LString(node.InnerText()))
// Add attributes if any
if len(node.Attr) > 0 {
attrs := L.NewTable()
for _, attr := range node.Attr {
L.SetField(attrs, attr.Name.Local, lua.LString(attr.Value))
}
L.SetField(table, "attributes", attrs)
}
L.SetGlobal("v", table)
return nil
}
@@ -128,5 +115,149 @@ func (p *XMLProcessor) ToLua(L *lua.LState, data interface{}) error {
// FromLua gets modified values from Lua
func (p *XMLProcessor) FromLua(L *lua.LState) (interface{}, error) {
luaValue := L.GetGlobal("v")
return FromLua(L, luaValue)
// Handle string values directly
if luaValue.Type() == lua.LTString {
return luaValue.String(), nil
}
// Handle tables (for attributes and more complex updates)
if luaValue.Type() == lua.LTTable {
return luaTableToMap(L, luaValue.(*lua.LTable)), nil
}
return luaValue.String(), nil
}
// Simple helper to convert a Lua table to a Go map
func luaTableToMap(L *lua.LState, table *lua.LTable) map[string]interface{} {
result := make(map[string]interface{})
table.ForEach(func(k, v lua.LValue) {
if k.Type() == lua.LTString {
key := k.String()
if v.Type() == lua.LTTable {
result[key] = luaTableToMap(L, v.(*lua.LTable))
} else {
result[key] = v.String()
}
}
})
return result
}
// Simple helper to convert node type to string
func nodeTypeToString(nodeType xmlquery.NodeType) string {
switch nodeType {
case xmlquery.ElementNode:
return "element"
case xmlquery.TextNode:
return "text"
case xmlquery.AttributeNode:
return "attribute"
default:
return "other"
}
}
// Helper function to update an XML node from a map
func updateNodeFromMap(node *xmlquery.Node, data map[string]interface{}) {
// Update node value if present
if value, ok := data["value"]; ok {
if strValue, ok := value.(string); ok {
// For element nodes, replace text content
if node.Type == xmlquery.ElementNode {
// Find the first text child if it exists
var textNode *xmlquery.Node
for child := node.FirstChild; child != nil; child = child.NextSibling {
if child.Type == xmlquery.TextNode {
textNode = child
break
}
}
if textNode != nil {
// Update existing text node
textNode.Data = strValue
} else {
// Create new text node
newText := &xmlquery.Node{
Type: xmlquery.TextNode,
Data: strValue,
Parent: node,
}
// Insert at beginning of children
if node.FirstChild != nil {
newText.NextSibling = node.FirstChild
node.FirstChild.PrevSibling = newText
node.FirstChild = newText
} else {
node.FirstChild = newText
node.LastChild = newText
}
}
} else if node.Type == xmlquery.TextNode {
// Directly update text node
node.Data = strValue
} else if node.Type == xmlquery.AttributeNode {
// Update attribute value
if node.Parent != nil {
for i, attr := range node.Parent.Attr {
if attr.Name.Local == node.Data {
node.Parent.Attr[i].Value = strValue
break
}
}
}
}
}
}
// Update attributes if present
if attrs, ok := data["attributes"].(map[string]interface{}); ok && node.Type == xmlquery.ElementNode {
for name, value := range attrs {
if strValue, ok := value.(string); ok {
// Look for existing attribute
found := false
for i, attr := range node.Attr {
if attr.Name.Local == name {
node.Attr[i].Value = strValue
found = true
break
}
}
// Add new attribute if not found
if !found {
node.Attr = append(node.Attr, xmlquery.Attr{
Name: struct {
Space, Local string
}{Local: name},
Value: strValue,
})
}
}
}
}
}
// Helper function to get a string representation of node type
func nodeTypeName(nodeType xmlquery.NodeType) string {
switch nodeType {
case xmlquery.ElementNode:
return "element"
case xmlquery.TextNode:
return "text"
case xmlquery.AttributeNode:
return "attribute"
case xmlquery.CommentNode:
return "comment"
case xmlquery.DeclarationNode:
return "declaration"
default:
return "unknown"
}
}