File: jsonutil.go

package info (click to toggle)
xq 1.3.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 596 kB
  • sloc: xml: 196; sh: 35; makefile: 6
file content (125 lines) | stat: -rw-r--r-- 3,093 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package utils

import (
	"strings"

	"github.com/antchfx/xmlquery"
)

// NodeToJSON converts an xmlquery.Node to a JSON object. The depth parameter
// specifies how many levels of children to include in the result. A depth of 0 means
// only the text content of the node is included. A depth of 1 means the node's children
// are included, but not their children, and so on.
func NodeToJSON(node *xmlquery.Node, depth int) interface{} {
	if node == nil {
		return nil
	}

	switch node.Type {
	case xmlquery.DocumentNode:
		result := make(map[string]interface{})
		var textParts []string

		// Process the next sibling of the document node first (if any)
		if node.NextSibling != nil && node.NextSibling.Type == xmlquery.TextNode {
			text := strings.TrimSpace(node.NextSibling.Data)
			if text != "" {
				textParts = append(textParts, text)
			}
		}

		// Process all children, including siblings of the first child
		for child := node.FirstChild; child != nil; child = child.NextSibling {
			switch child.Type {
			case xmlquery.ElementNode:
				childResult := nodeToJSONInternal(child, depth)
				result[child.Data] = childResult
			case xmlquery.TextNode:
				text := strings.TrimSpace(child.Data)
				if text != "" {
					textParts = append(textParts, text)
				}
			}
		}

		if len(textParts) > 0 {
			result["#text"] = strings.Join(textParts, "\n")
		}
		return result

	case xmlquery.ElementNode:
		return nodeToJSONInternal(node, depth)

	case xmlquery.TextNode:
		return strings.TrimSpace(node.Data)

	default:
		return nil
	}
}

func nodeToJSONInternal(node *xmlquery.Node, depth int) interface{} {
	if depth == 0 {
		return getTextContent(node)
	}

	result := make(map[string]interface{})
	for _, attr := range node.Attr {
		result["@"+attr.Name.Local] = attr.Value
	}

	var textParts []string
	for child := node.FirstChild; child != nil; child = child.NextSibling {
		switch child.Type {
		case xmlquery.TextNode:
			text := strings.TrimSpace(child.Data)
			if text != "" {
				textParts = append(textParts, text)
			}
		case xmlquery.ElementNode:
			childResult := nodeToJSONInternal(child, depth-1)
			addToResult(result, child.Data, childResult)
		}
	}

	if len(textParts) > 0 {
		if len(result) == 0 {
			return strings.Join(textParts, "\n")
		}
		result["#text"] = strings.Join(textParts, "\n")
	}

	return result
}

func getTextContent(node *xmlquery.Node) string {
	var parts []string
	for child := node.FirstChild; child != nil; child = child.NextSibling {
		switch child.Type {
		case xmlquery.TextNode:
			text := strings.TrimSpace(child.Data)
			if text != "" {
				parts = append(parts, text)
			}
		case xmlquery.ElementNode:
			parts = append(parts, getTextContent(child))
		}
	}
	return strings.Join(parts, "\n")
}

func addToResult(result map[string]interface{}, key string, value interface{}) {
	if key == "" {
		return
	}
	if existing, ok := result[key]; ok {
		switch existing := existing.(type) {
		case []interface{}:
			result[key] = append(existing, value)
		default:
			result[key] = []interface{}{existing, value}
		}
	} else {
		result[key] = value
	}
}