Skip to content

Commit 0dce939

Browse files
committed
Add --tree option to show the xpath tree of the given file
1 parent b48428c commit 0dce939

File tree

2 files changed

+103
-2
lines changed

2 files changed

+103
-2
lines changed

README.adoc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ This tool reads a whole XML file into memory, extracts its XML Namespaces using
1111
When there is a default namespace, it automatically loads it under the namespace ID *ns*.
1212
An example XPath would then look like `/ns:a/ns:b/ns:c`.
1313

14+
1415
== SYNOPSIS
1516

16-
*xmlsect* _file_ [_xpath_]
17+
*xmlsect* _file_ [_xpath_] [_relative_xpath] [*--tree*]
1718

1819
== Installation
1920

xmlsect.go

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"log"
1717
"os"
1818
"regexp"
19+
"strings"
1920

2021
"github.com/DavidGamba/go-getoptions"
2122
"github.com/DavidGamba/xmlsect/semver"
@@ -61,6 +62,100 @@ func printNodeSet(n []dom.Node) {
6162
}
6263
}
6364

65+
func printNodeSetTree(n []dom.Node) {
66+
var str string
67+
l := len(n)
68+
for i, e := range n {
69+
str += printTreeNode(e, 0)
70+
if i+1 < l {
71+
str += "\n"
72+
}
73+
}
74+
fmt.Println(str)
75+
}
76+
77+
// Taken and modified from github.com/santhosh-tekuri/dom/marshal.go
78+
//
79+
// Copyright 2017 Santhosh Kumar Tekuri. All rights reserved.
80+
// Use of this source code is governed by a BSD-style
81+
// license that can be found in the LICENSE file.
82+
//
83+
func printName(n *dom.Name) string {
84+
var str string
85+
if n.Prefix != "" {
86+
str += n.Prefix
87+
str += ":"
88+
}
89+
str += n.Local
90+
return str
91+
}
92+
93+
// Taken and modified from github.com/santhosh-tekuri/dom/marshal.go
94+
//
95+
// Copyright 2017 Santhosh Kumar Tekuri. All rights reserved.
96+
// Use of this source code is governed by a BSD-style
97+
// license that can be found in the LICENSE file.
98+
//
99+
func printTreeNode(n dom.Node, level int) string {
100+
var str string
101+
switch n := n.(type) {
102+
case *dom.Document:
103+
log.Printf("Document. Children %d\n", len(n.Children()))
104+
for _, c := range n.Children() {
105+
str += printTreeNode(c, level+1)
106+
}
107+
case *dom.Element:
108+
str += "/"
109+
str += printName(n.Name)
110+
for prefix, _ := range n.NSDecl {
111+
str += " "
112+
str += "xmlns"
113+
if prefix != "" {
114+
str += ":"
115+
str += prefix
116+
}
117+
}
118+
for _, attr := range n.Attrs {
119+
str += " @"
120+
str += printName(attr.Name)
121+
}
122+
if len(n.Children()) != 0 {
123+
log.Printf("Element '%s' Children %d\n", n.Name, len(n.ChildNodes))
124+
unique := make(map[string]int)
125+
for _, c := range n.Children() {
126+
tmpStr := printTreeNode(c, level+1)
127+
// Skip empty results
128+
if tmpStr == "" {
129+
continue
130+
}
131+
if v, ok := unique[tmpStr]; ok {
132+
count := v + 1
133+
unique[tmpStr] = count
134+
} else {
135+
unique[tmpStr] = 1
136+
}
137+
}
138+
for k, v := range unique {
139+
if strings.HasPrefix(k, "/") {
140+
str += "\n"
141+
str += strings.Repeat(" ", level+1)
142+
str += fmt.Sprintf("[%d] %s", v, k)
143+
} else {
144+
str += fmt.Sprintf("%s", k)
145+
}
146+
}
147+
}
148+
case *dom.Text:
149+
r := regexp.MustCompile(`^\n\s+$|^\s+$`)
150+
if !r.Match([]byte(n.Data)) {
151+
str += " text"
152+
}
153+
case *dom.ProcInst:
154+
// TODO
155+
}
156+
return str
157+
}
158+
64159
func printDoc(doc *dom.Document) {
65160
buf := new(bytes.Buffer)
66161
if err := dom.Marshal(doc, buf); err != nil {
@@ -72,7 +167,7 @@ func printDoc(doc *dom.Document) {
72167

73168
func synopsis() {
74169
synopsis := `# USAGE:
75-
xsect <file> [<xpath>] [<relative_xpath>]
170+
xsect <file> [<xpath>] [<relative_xpath>] [--tree]
76171
77172
xsect [--help]
78173
`
@@ -84,6 +179,7 @@ func main() {
84179
opt.Bool("help", false)
85180
opt.Bool("debug", false)
86181
opt.Bool("version", false)
182+
opt.Bool("tree", false)
87183
remaining, err := opt.Parse(os.Args[1:])
88184
if err != nil {
89185
fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
@@ -148,6 +244,10 @@ func main() {
148244
os.Exit(1)
149245
}
150246
log.Printf("results: %d\n", len(nodeSet))
247+
if opt.Called("tree") {
248+
printNodeSetTree(nodeSet)
249+
os.Exit(0)
250+
}
151251
printNodeSet(nodeSet)
152252
if xpathRelQuery != "" {
153253
doc := &dom.Document{nodeSet}

0 commit comments

Comments
 (0)