diff --git a/.github/workflows/code_quality.yml b/.github/workflows/code_quality.yml
new file mode 100644
index 0000000..85b7024
--- /dev/null
+++ b/.github/workflows/code_quality.yml
@@ -0,0 +1,22 @@
+name: Code quality
+
+on:
+ push:
+ branches:
+ - master
+
+jobs:
+ code-quality:
+ name: Code quality
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@master
+
+ - name: Set up Go
+ uses: actions/setup-go@v4
+ with:
+ go-version: '1.17'
+
+ - name: Unit tests
+ run: go test -v ./...
diff --git a/Makefile b/Makefile
index 4dc4163..9cc3a15 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ SHELL = /bin/bash
TARGETS = zek
PKGNAME = zek
ARCH = amd64
-VERSION = 0.1.16
+VERSION = 0.1.21
.PHONY: all
all: $(TARGETS)
@@ -52,7 +52,7 @@ rpm: $(TARGETS) docs/$(PKGNAME).1
.PHONY: release
release:
- @export GITHUB_TOKEN="..."
+ @echo export GITHUB_TOKEN="..."
@echo go tag $(VERSION)
@echo goreleaser release --rm-dist
diff --git a/README.md b/README.md
index 0389271..3e71cb1 100644
--- a/README.md
+++ b/README.md
@@ -78,7 +78,8 @@ type Rss struct {
## Online
-Try it online at [https://www.onlinetool.io/xmltogo/](https://www.onlinetool.io/xmltogo/) -- thanks, [kjk](https://github.com/kjk)!
+* try online via WASM: [https://xml-to-go.github.io/](https://xml-to-go.github.io/), thanks [YaroslavPodorvanov](https://github.com/YaroslavPodorvanov)!
+* try it online at [https://blog.kowalczyk.info/tools/xmltogo/](https://blog.kowalczyk.info/tools/xmltogo/) -- thanks, [kjk](https://github.com/kjk)!
## About
@@ -98,7 +99,7 @@ Downsides:
* experimental, early, buggy, unstable prototype,
* no support for recursive types (similar to *Russian Doll* strategy, [[1](https://medbiq.org/std_specs/techguidelines/xmldesignguidelines.pdf#page=7)])
-* no type inference, everything is accessible as string.
+* no type inference, everything is accessible as string (without a schema, type inference may fail if the type *guess* is wrong)
Bugs:
@@ -113,6 +114,7 @@ Related projects:
* https://github.com/bemasher/JSONGen
* https://github.com/dutchcoders/XMLGen
* https://github.com/gnewton/chidley
+* https://github.com/twpayne/go-xmlstruct
And other [awesome XML utilities](https://github.com/avelino/awesome-go#xml).
@@ -144,6 +146,8 @@ Usage of zek:
-F skip formatting
-P string
if set, write out struct within a package with the given name
+ -S int
+ read at most this many tags, approximately (0=unlimited)
-c emit more compact struct (noop, as this is the default since 0.1.7)
-d debug output
-e add comments with example
diff --git a/cmd/zek/main.go b/cmd/zek/main.go
index 90102fc..9c4a8ae 100644
--- a/cmd/zek/main.go
+++ b/cmd/zek/main.go
@@ -23,6 +23,7 @@ var (
debug = flag.Bool("d", false, "debug output")
createExampleProgram = flag.Bool("p", false, "write out an example program")
tagName = flag.String("t", "", "emit struct for tag matching this name")
+ notInlineStructs = flag.Bool("I", false, "do not inline children tags as anonymous structs")
skipFormatting = flag.Bool("F", false, "skip formatting")
strict = flag.Bool("s", false, "strict parsing and writing")
exampleMaxChars = flag.Int("x", 25, "max chars for example")
@@ -35,6 +36,7 @@ var (
outputFile = flag.String("o", "", "if set, write to output file, not stdout")
packageName = flag.String("P", "", "if set, write out struct within a package with the given name")
fixedBanner = flag.Bool("B", false, "use a fixed banner string (e.g. for CI)")
+ readAtMost = flag.Int64("S", 0, "read at most this many tags, approximately (0=unlimited)")
)
func main() {
@@ -73,7 +75,11 @@ func main() {
}
reader = io.MultiReader(rs...)
}
- if _, err := root.ReadFrom(reader); err != nil {
+ opts := zek.ReadOpts{
+ MaxExamples: *maxExamples,
+ MaxTokens: *readAtMost,
+ }
+ if _, err := root.ReadFrom(reader, &opts); err != nil {
log.Fatal(err)
}
// Move root, if we have a tagName. Ignore unknown names.
@@ -99,6 +105,7 @@ func main() {
sw.Strict = *strict
sw.ExampleMaxChars = *exampleMaxChars
sw.Compact = !*nonCompact
+ sw.InlineStructs = !*notInlineStructs
sw.UniqueExamples = *uniqueExamples
sw.OmitEmptyText = *omitEmptyText
if *fixedBanner {
diff --git a/docs/zek.md b/docs/zek.md
index 1d81fe5..8299e89 100644
--- a/docs/zek.md
+++ b/docs/zek.md
@@ -75,6 +75,9 @@ at https://www.onlinetool.io/xmltogo/.
-s
: Strict parsing.
+-S
+: raed at most this many tokens, approximately (0=unlimited)
+
-t
: Emit struct for tag matching this name.
diff --git a/go.mod b/go.mod
index e85cd95..52a29b7 100644
--- a/go.mod
+++ b/go.mod
@@ -1,10 +1,10 @@
module github.com/miku/zek
-go 1.12
+go 1.17
require (
- github.com/sethgrid/pester v0.0.0-20190127155807-68a33a018ad0
- golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 // indirect
- golang.org/x/net v0.0.0-20210917221730-978cfadd31cf
- golang.org/x/text v0.3.7 // indirect
+ github.com/sethgrid/pester v1.2.0
+ golang.org/x/net v0.19.0
)
+
+require golang.org/x/text v0.14.0 // indirect
diff --git a/go.sum b/go.sum
index 6816a97..7069ebf 100644
--- a/go.sum
+++ b/go.sum
@@ -1,18 +1,43 @@
-github.com/sethgrid/pester v0.0.0-20190127155807-68a33a018ad0 h1:X9XMOYjxEfAYSy3xK1DzO5dMkkWhs9E9UCcS1IERx2k=
-github.com/sethgrid/pester v0.0.0-20190127155807-68a33a018ad0/go.mod h1:Ad7IjTpvzZO8Fl0vh9AzQ+j/jYZfyp2diGwI8m5q+ns=
+github.com/sethgrid/pester v1.2.0 h1:adC9RS29rRUef3rIKWPOuP1Jm3/MmB6ke+OhE5giENI=
+github.com/sethgrid/pester v1.2.0/go.mod h1:hEUINb4RqvDxtoCaU0BNT/HV4ig5kfgOasrf1xcvr0A=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
-golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20210917221730-978cfadd31cf h1:R150MpwJIv1MpS0N/pc+NhTM8ajzvlmxlY5OYsrevXQ=
-golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
+golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
+golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/node.go b/node.go
index ff0bf52..305639d 100644
--- a/node.go
+++ b/node.go
@@ -45,10 +45,16 @@ type Node struct {
childFreqs map[xml.Name]int // Count child tag occurrences, used temporarily.
}
+// ReadOpts groups options for parsing.
+type ReadOpts struct {
+ MaxExamples int
+ MaxTokens int64
+}
+
// readNode reads XML from a reader and returns a parsed node. If node is
// given, it is reused, allowing for multiple passes (e.g. from multiple
// files). XXX: maxExamples should be factored out into options.
-func readNode(r io.Reader, root *Node, maxExamples int) (node *Node, n int64, err error) {
+func readNode(r io.Reader, root *Node, opts *ReadOpts) (node *Node, n int64, err error) {
var (
cw = countwriter{}
dec = xml.NewDecoder(io.TeeReader(r, &cw))
@@ -60,6 +66,8 @@ func readNode(r io.Reader, root *Node, maxExamples int) (node *Node, n int64, er
}
stack := Stack{}
stack.Put(root)
+ var i int64
+OUTER:
for {
token, err := dec.Token()
if err == io.EOF {
@@ -68,6 +76,7 @@ func readNode(r io.Reader, root *Node, maxExamples int) (node *Node, n int64, er
if err != nil {
return root, cw.n, err
}
+ i++
switch t := token.(type) {
case xml.StartElement:
parent := stack.Peek().(*Node)
@@ -76,13 +85,16 @@ func readNode(r io.Reader, root *Node, maxExamples int) (node *Node, n int64, er
case xml.EndElement:
n := stack.Pop().(*Node)
n.End()
+ if opts.MaxTokens > 0 && i > opts.MaxTokens {
+ break OUTER
+ }
case xml.CharData:
v := strings.TrimSpace(string(t))
if v == "" {
break
}
n := stack.Peek().(*Node)
- if len(n.Examples) < maxExamples {
+ if len(n.Examples) < opts.MaxExamples {
// XXX: sample better, e.g. reservoir dictionary.
n.Examples = append(n.Examples, v)
}
@@ -92,9 +104,9 @@ func readNode(r io.Reader, root *Node, maxExamples int) (node *Node, n int64, er
return root, cw.n, nil
}
-// ReadFrom reads XML from a reader.
-func (node *Node) ReadFrom(r io.Reader) (int64, error) {
- nn, n, err := readNode(r, nil, node.MaxExamples)
+// ReadFrom reads XML from a reader. TODO: pass read options.
+func (node *Node) ReadFrom(r io.Reader, opts *ReadOpts) (int64, error) {
+ nn, n, err := readNode(r, nil, opts)
if err != nil {
return n, err
}
diff --git a/node_test.go b/node_test.go
index f409df8..f4eeac3 100644
--- a/node_test.go
+++ b/node_test.go
@@ -419,7 +419,7 @@ func TestNodeReadFrom(t *testing.T) {
for _, c := range cases {
r := strings.NewReader(c.input)
node := new(Node)
- _, err := node.ReadFrom(r)
+ _, err := node.ReadFrom(r, &ReadOpts{MaxExamples: 10})
if err != c.err {
t.Errorf("got %v, want %v", err, c.err)
}
@@ -462,7 +462,7 @@ func TestHeight(t *testing.T) {
for _, c := range cases {
r := strings.NewReader(c.input)
node := new(Node)
- _, err := node.ReadFrom(r)
+ _, err := node.ReadFrom(r, &ReadOpts{MaxExamples: 10})
if err != nil {
t.Errorf("failed to parse tree: %s", err)
}
@@ -475,7 +475,7 @@ func TestHeight(t *testing.T) {
func TestByName(t *testing.T) {
r := strings.NewReader(``)
root := new(Node)
- if _, err := root.ReadFrom(r); err != nil {
+ if _, err := root.ReadFrom(r, &ReadOpts{MaxExamples: 10}); err != nil {
t.Errorf("got %v, want nil", err)
}
@@ -892,7 +892,7 @@ func TestNodeReadFromAll(t *testing.T) {
}
node := new(Node)
- _, err := node.ReadFrom(io.MultiReader(readers...))
+ _, err := node.ReadFrom(io.MultiReader(readers...), &ReadOpts{MaxExamples: 10})
if err != c.err {
t.Errorf("got %v, want %v", err, c.err)
}
diff --git a/packaging/deb/control.amd64 b/packaging/deb/control.amd64
index 9ead8fa..d1ee457 100644
--- a/packaging/deb/control.amd64
+++ b/packaging/deb/control.amd64
@@ -1,5 +1,5 @@
Package: zek
-Version: 0.1.16
+Version: 0.1.21
Section: utils
Priority: optional
Architecture: amd64
diff --git a/packaging/deb/control.any b/packaging/deb/control.any
index 0524882..71e5712 100644
--- a/packaging/deb/control.any
+++ b/packaging/deb/control.any
@@ -1,5 +1,5 @@
Package: zek
-Version: 0.1.16
+Version: 0.1.21
Section: utils
Priority: optional
Architecture: any
diff --git a/packaging/deb/zek/DEBIAN/control b/packaging/deb/zek/DEBIAN/control
index 9ead8fa..d1ee457 100644
--- a/packaging/deb/zek/DEBIAN/control
+++ b/packaging/deb/zek/DEBIAN/control
@@ -1,5 +1,5 @@
Package: zek
-Version: 0.1.16
+Version: 0.1.21
Section: utils
Priority: optional
Architecture: amd64
diff --git a/packaging/rpm/zek.spec b/packaging/rpm/zek.spec
index 5ab0deb..e469b57 100644
--- a/packaging/rpm/zek.spec
+++ b/packaging/rpm/zek.spec
@@ -1,6 +1,6 @@
Summary: Generate a Go struct from an XML document.
Name: zek
-Version: 0.1.16
+Version: 0.1.21
Release: 0
License: GPL
BuildArch: x86_64
diff --git a/structwriter.go b/structwriter.go
index ece6f37..17840e0 100644
--- a/structwriter.go
+++ b/structwriter.go
@@ -100,7 +100,8 @@ type StructWriter struct {
Strict bool // Whether to ignore implementation holes.
WithJSONTags bool // Include JSON struct tags.
Compact bool // Emit more compact struct.
- UniqueExamples bool // Filter out duplicated examples
+ InlineStructs bool // Inlines children tags as anonymous structs.
+ UniqueExamples bool // Filter out duplicated examples.
OmitEmptyText bool // Don't generate Text fields if no example elements have chardata.
}
@@ -242,53 +243,59 @@ func (sw *StructWriter) writeNode(node *Node, top bool) (err error) {
fmt.Fprintf(sew, "%s\n", s)
return err
}
- io.WriteString(sew, "struct {\n")
- if top {
- sw.writeNameField(sew, node)
- }
- if !sw.OmitEmptyText || len(node.Examples) > 0 {
- sw.writeChardataField(sew, node)
- }
- // Helper to check for name clash of attribute with any generated field name.
- isValidName := func(name string) bool {
- if name == sw.TextFieldNames[0] {
- return false
+
+ if sw.InlineStructs || top {
+ io.WriteString(sew, "struct {\n")
+ if top {
+ sw.writeNameField(sew, node)
}
- for _, child := range node.Children {
- if name == sw.NameFunc(child.Name.Local) {
+ if !sw.OmitEmptyText || len(node.Examples) > 0 {
+ sw.writeChardataField(sew, node)
+ }
+ // Helper to check for name clash of attribute with any generated field name.
+ isValidName := func(name string) bool {
+ if name == sw.TextFieldNames[0] {
return false
}
- }
- return true
- }
- // Write attributes. XXX: Better handling of duplicate attributes.
- written := make(map[string]bool)
- for _, attr := range node.Attr {
- name := sw.NameFunc(attr.Name.Local)
- for _, prefix := range sw.AttributePrefixes {
- if isValidName(name) {
- break
+ for _, child := range node.Children {
+ if name == sw.NameFunc(child.Name.Local) {
+ return false
+ }
}
- name = fmt.Sprintf("%s%s", prefix, name)
- }
- if !isValidName(name) {
- return fmt.Errorf("name clash: %s", attr.Name.Local)
+ return true
}
- if _, ok := written[attr.Name.Local]; ok {
- if sw.Strict {
- log.Fatalf("[not implemented] duplicate local attribute name: %s", attr)
- } else {
- log.Printf("warning: duplicate local attribute name: %s", attr)
+ // Write attributes. XXX: Better handling of duplicate attributes.
+ written := make(map[string]bool)
+ for _, attr := range node.Attr {
+ name := sw.NameFunc(attr.Name.Local)
+ for _, prefix := range sw.AttributePrefixes {
+ if isValidName(name) {
+ break
+ }
+ name = fmt.Sprintf("%s%s", prefix, name)
+ }
+ if !isValidName(name) {
+ return fmt.Errorf("name clash: %s", attr.Name.Local)
+ }
+ if _, ok := written[attr.Name.Local]; ok {
+ if sw.Strict {
+ log.Fatalf("[not implemented] duplicate local attribute name: %s", attr)
+ } else {
+ log.Printf("warning: duplicate local attribute name: %s", attr)
+ }
+ continue
}
- continue
+ sw.writeAttrField(sew, name, "string", attr)
+ written[attr.Name.Local] = true
}
- sw.writeAttrField(sew, name, "string", attr)
- written[attr.Name.Local] = true
- }
- for _, child := range node.Children {
- sw.writeNode(child, false)
+ for _, child := range node.Children {
+ sw.writeNode(child, false)
+ }
+ io.WriteString(sew, "} ")
+ } else {
+ io.WriteString(sew, sw.NameFunc(node.Name.Local)+" ")
}
- io.WriteString(sew, "} ")
+
if !top {
sw.writeStructTag(sew, node)
}
diff --git a/structwriter_test.go b/structwriter_test.go
index 2eec9b5..8bbeab8 100644
--- a/structwriter_test.go
+++ b/structwriter_test.go
@@ -78,72 +78,85 @@ func TestWriteNode(t *testing.T) {
withComments bool
omitEmptyText bool
uniqueExamples bool
+ inlineStructs bool
err error
}{
{
- input: "testdata/w.1.xml",
- result: "testdata/w.1.go",
- err: nil,
+ input: "testdata/w.1.xml",
+ result: "testdata/w.1.go",
+ inlineStructs: true,
+ err: nil,
},
{
- input: "testdata/w.2.xml",
- result: "testdata/w.2.go",
- err: nil,
+ input: "testdata/w.2.xml",
+ result: "testdata/w.2.go",
+ inlineStructs: true,
+ err: nil,
},
{
- input: "testdata/w.3.xml",
- result: "testdata/w.3.go",
- err: nil,
+ input: "testdata/w.3.xml",
+ result: "testdata/w.3.go",
+ inlineStructs: true,
+ err: nil,
},
{
- input: "testdata/w.4.xml",
- result: "testdata/w.4.go",
- err: nil,
+ input: "testdata/w.4.xml",
+ result: "testdata/w.4.go",
+ inlineStructs: true,
+ err: nil,
},
{
- input: "testdata/w.5.xml",
- result: "testdata/w.5.go",
- err: nil,
+ input: "testdata/w.5.xml",
+ result: "testdata/w.5.go",
+ inlineStructs: true,
+ err: nil,
},
{
- input: "testdata/w.6.xml",
- result: "testdata/w.6.go",
- err: nil,
+ input: "testdata/w.6.xml",
+ result: "testdata/w.6.go",
+ inlineStructs: true,
+ err: nil,
},
{
- input: "testdata/w.7.xml",
- result: "testdata/w.7.go",
- err: nil,
+ input: "testdata/w.7.xml",
+ result: "testdata/w.7.go",
+ inlineStructs: true,
+ err: nil,
},
{
- input: "testdata/w.8.xml",
- result: "testdata/w.8.go",
- withComments: true,
- err: nil,
+ input: "testdata/w.8.xml",
+ result: "testdata/w.8.go",
+ withComments: true,
+ inlineStructs: true,
+ err: nil,
},
{
- input: "testdata/w.9.xml",
- result: "testdata/w.9.go",
- withComments: true,
- err: nil,
+ input: "testdata/w.9.xml",
+ result: "testdata/w.9.go",
+ withComments: true,
+ inlineStructs: true,
+ err: nil,
},
{
- input: "testdata/w.10.xml",
- result: "testdata/w.10.go",
- withComments: true,
- err: nil,
+ input: "testdata/w.10.xml",
+ result: "testdata/w.10.go",
+ withComments: true,
+ inlineStructs: true,
+ err: nil,
},
{
- input: "testdata/w.11.xml",
- result: "testdata/w.11.go",
- withComments: true,
- err: nil,
+ input: "testdata/w.11.xml",
+ result: "testdata/w.11.go",
+ withComments: true,
+ inlineStructs: true,
+ err: nil,
},
{
input: "testdata/w.12.xml",
result: "testdata/w.12.go",
withComments: true,
uniqueExamples: false,
+ inlineStructs: true,
err: nil,
},
{
@@ -151,6 +164,7 @@ func TestWriteNode(t *testing.T) {
result: "testdata/w.13.go",
withComments: true,
uniqueExamples: true,
+ inlineStructs: true,
err: nil,
},
{
@@ -158,13 +172,21 @@ func TestWriteNode(t *testing.T) {
result: "testdata/w.14.go",
withComments: true,
uniqueExamples: true,
+ inlineStructs: true,
omitEmptyText: true,
err: nil,
},
{
- input: "testdata/w.15.xml",
- result: "testdata/w.15.go",
- err: nil,
+ input: "testdata/w.15.xml",
+ result: "testdata/w.15.go",
+ inlineStructs: true,
+ err: nil,
+ },
+ {
+ input: "testdata/w.16.xml",
+ result: "testdata/w.16.go",
+ inlineStructs: false,
+ err: nil,
},
}
@@ -178,7 +200,7 @@ func TestWriteNode(t *testing.T) {
node := new(Node)
node.MaxExamples = 10
- if _, err := node.ReadFrom(f); err != nil {
+ if _, err := node.ReadFrom(f, &ReadOpts{MaxExamples: 10}); err != nil {
t.Errorf("failed to read XML input: %s", err)
}
@@ -187,6 +209,7 @@ func TestWriteNode(t *testing.T) {
sw.WithComments = c.withComments
sw.UniqueExamples = c.uniqueExamples
sw.OmitEmptyText = c.omitEmptyText
+ sw.InlineStructs = c.inlineStructs
if err := sw.WriteNode(node); err != c.err {
t.Errorf("WriteNode failed: got %v, want %v", err, c.err)
diff --git a/testdata/w.16.go b/testdata/w.16.go
new file mode 100644
index 0000000..8d8aeb7
--- /dev/null
+++ b/testdata/w.16.go
@@ -0,0 +1,10 @@
+package main
+
+import "encoding/xml"
+
+type A struct {
+ XMLName xml.Name `xml:"a"`
+ Text string `xml:",chardata"`
+ B B `xml:"b"`
+ A A `xml:"a"`
+}
diff --git a/testdata/w.16.xml b/testdata/w.16.xml
new file mode 100644
index 0000000..37fad67
--- /dev/null
+++ b/testdata/w.16.xml
@@ -0,0 +1,9 @@
+
+ Europe
+
+ Germany
+
+ Leipzig
+
+
+
diff --git a/version.go b/version.go
index 5b8741e..ed29d55 100644
--- a/version.go
+++ b/version.go
@@ -22,4 +22,4 @@
package zek
// Version of application.
-const Version = "0.1.16"
+const Version = "0.1.21"