-
-
Notifications
You must be signed in to change notification settings - Fork 118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add xml support as xq (from python yq) #215
Open
momiji
wants to merge
26
commits into
itchyny:main
Choose a base branch
from
momiji:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
8417b4e
add xml support using mxj
efed1c6
fix compact mode for xml output
2b4ad32
updates from mxj
da965e1
add paths to force-list
31bbd2a
update mxj version
205ad7a
fix missing tags in xml compact mode
81145e7
replace mxj wth my own xqml which is much faster
b359294
fix issue with elems/attrs
347b8f6
add xml attributes/html options
3a58c73
change too long options names
d6ed42c
update xqml version
ae0a59e
fix missing changes in methods
3184d9d
update github.com/momiji/xqml version to v0.0.6
88834bd
update github.com/momiji/xqml version to v0.0.7
140499b
update xqml to 0.0.8 with new Encoder/Decoder methods
a278590
update xqml to 0.0.9
043e9ab
add -V to get version
8ea86d6
fix: handle xml error in parser
a4ff272
feat: auto-detect format JSON/YAML/XML
9c11250
fix: add missing "---" yaml detection
407db29
fix: add missing "- " yaml detection
912da02
Merge branch 'itchyny:main' into main
momiji 77e0319
Merge branch 'itchyny:main' into main
momiji d258e7a
Merge remote-tracking branch 'upstream/main'
a389322
Merge branch 'itchyny:main' into main
1facbbc
fix: restore -v and remove -V
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
package cli | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
) | ||
|
||
type DetectedFormat int | ||
|
||
func (d DetectedFormat) String() string { | ||
switch d { | ||
case JsonFormat: | ||
return "json" | ||
case YamlFormat: | ||
return "yaml" | ||
case XmlFormat: | ||
return "xml" | ||
} | ||
return "" | ||
} | ||
|
||
const ( | ||
JsonFormat DetectedFormat = iota | ||
YamlFormat | ||
XmlFormat | ||
) | ||
|
||
func detectInputType(r io.Reader, bufSize int) (io.Reader, DetectedFormat) { | ||
readers := make([]io.Reader, 0) | ||
var buf []byte | ||
index := 0 | ||
length := 0 | ||
var err error | ||
result := func(t DetectedFormat) (io.Reader, DetectedFormat) { | ||
readers = append(readers, r) | ||
return io.MultiReader(readers...), t | ||
} | ||
readByte := func() (byte, error) { | ||
if index == length { | ||
if err != nil { | ||
return 0, err | ||
} | ||
buf = make([]byte, bufSize) | ||
length, err = r.Read(buf) | ||
if length == 0 && err != nil { | ||
return 0, err | ||
} | ||
readers = append(readers, bytes.NewReader(buf[0:length])) | ||
index = 0 | ||
} | ||
i := index | ||
index = index + 1 | ||
return buf[i], nil | ||
} | ||
|
||
// state machine | ||
state := "loop" | ||
var b, c byte | ||
loop: | ||
for { | ||
switch state { | ||
// main loop | ||
case "loop": | ||
for { | ||
b, err = readByte() | ||
if err != nil { | ||
return result(JsonFormat) | ||
} | ||
switch b { | ||
case ' ', '\t', '\r', '\n': | ||
case '{', '[', '/': | ||
return result(JsonFormat) | ||
case '#': | ||
return result(YamlFormat) | ||
case '<': | ||
return result(XmlFormat) | ||
case '-': | ||
// yaml if "- " or "---" | ||
c, err = readByte() | ||
if err != nil { | ||
return result(JsonFormat) | ||
} | ||
if c == ' ' { | ||
return result(YamlFormat) | ||
} | ||
if c != '-' { | ||
return result(JsonFormat) | ||
} | ||
c, err = readByte() | ||
if err != nil || c != '-' { | ||
return result(JsonFormat) | ||
} | ||
return result(YamlFormat) | ||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.': | ||
return result(JsonFormat) | ||
case '"': | ||
// string can be either a json/yaml text | ||
state = "string" | ||
c = b | ||
continue loop | ||
case 't': | ||
// json if true | ||
for _, c = range []byte("rue") { | ||
b, err = readByte() | ||
if err != nil || b != c { | ||
return result(YamlFormat) | ||
} | ||
} | ||
state = "after" | ||
continue loop | ||
case 'f': | ||
// json if false | ||
for _, c = range []byte("alse") { | ||
b, err = readByte() | ||
if err != nil || b != c { | ||
return result(YamlFormat) | ||
} | ||
} | ||
state = "after" | ||
continue loop | ||
case 'n': | ||
// json if false | ||
for _, c = range []byte("ull") { | ||
b, err = readByte() | ||
if err != nil || b != c { | ||
return result(YamlFormat) | ||
} | ||
} | ||
state = "after" | ||
continue loop | ||
default: | ||
// neither a number or string with " | ||
return result(YamlFormat) | ||
} | ||
} | ||
// string, started by " | ||
case "string": | ||
escape := false | ||
for { | ||
b, err = readByte() | ||
if err != nil { | ||
return result(JsonFormat) | ||
} | ||
if escape { | ||
continue | ||
} | ||
switch b { | ||
case ' ', '\t': | ||
case '\r', '\n': | ||
// new line not allowed in yaml tags | ||
result(JsonFormat) | ||
case '\\': | ||
// escape next character | ||
escape = true | ||
case c: | ||
// close string, look for next char to identify if it is yaml tag | ||
state = "after" | ||
continue loop | ||
} | ||
} | ||
// close string, look for next char to identify if it is yaml tag | ||
case "after": | ||
for { | ||
b, err = readByte() | ||
if err != nil { | ||
return result(JsonFormat) | ||
} | ||
switch b { | ||
case ' ', '\t': | ||
case '\r', '\n': | ||
// new line not allowed in yaml tags | ||
return result(JsonFormat) | ||
case ':': | ||
// it is a yaml tag | ||
return result(YamlFormat) | ||
default: | ||
// it is not a yaml tag | ||
return result(JsonFormat) | ||
} | ||
} | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package cli | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
func TestDetectInputType(t *testing.T) { | ||
for _, s := range []string{"", "\t", "\r", "\n", " ", " \t", " \r", " \n", " \t ", " \r ", " \n "} { | ||
testDetectInputType(t, s+"", JsonFormat) | ||
testDetectInputType(t, s+"{", JsonFormat) | ||
testDetectInputType(t, s+"#", YamlFormat) | ||
testDetectInputType(t, s+"<", XmlFormat) | ||
testDetectInputType(t, s+"a", YamlFormat) | ||
testDetectInputType(t, s+"a:", YamlFormat) | ||
testDetectInputType(t, s+"a: 1", YamlFormat) | ||
testDetectInputType(t, s+"true", JsonFormat) | ||
testDetectInputType(t, s+"true true", JsonFormat) | ||
testDetectInputType(t, s+"true:", YamlFormat) | ||
testDetectInputType(t, s+"null", JsonFormat) | ||
testDetectInputType(t, s+"null null", JsonFormat) | ||
testDetectInputType(t, s+"null:", YamlFormat) | ||
testDetectInputType(t, s+"false", JsonFormat) | ||
testDetectInputType(t, s+"false false", JsonFormat) | ||
testDetectInputType(t, s+"false:", YamlFormat) | ||
testDetectInputType(t, s+"1", JsonFormat) | ||
testDetectInputType(t, s+"-1", JsonFormat) | ||
testDetectInputType(t, s+"-1e3", JsonFormat) | ||
testDetectInputType(t, s+"- ", YamlFormat) | ||
testDetectInputType(t, s+"--", JsonFormat) | ||
testDetectInputType(t, s+"---", YamlFormat) | ||
testDetectInputType(t, s+`"hello"`, JsonFormat) | ||
testDetectInputType(t, s+`"hello":1`, YamlFormat) | ||
testDetectInputType(t, s+`"hello": 1`, YamlFormat) | ||
testDetectInputType(t, s+`'hello'`, YamlFormat) | ||
testDetectInputType(t, s+`'hello':1`, YamlFormat) | ||
testDetectInputType(t, s+`'hello': 1`, YamlFormat) | ||
} | ||
} | ||
|
||
func testDetectInputType(t *testing.T, s string, format DetectedFormat) { | ||
r, f := detectInputType(strings.NewReader(s), 1) | ||
if f != format { | ||
t.Fatalf("failed: invalid format '%s' expected '%s' for string '%s'", f, format, s) | ||
} | ||
buf := new(bytes.Buffer) | ||
_, err := io.Copy(buf, r) | ||
if err != nil { | ||
t.Fatalf("failed: copy error for string '%s'", s) | ||
} | ||
if buf.String() != s { | ||
t.Fatalf("failed: invalid reader content '%s'' for string '%s'", buf.String(), s) | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Note: I'm not the author of gojq, just an interested third party.)
I think it makes sense to add even more formats in the future (toml, msgpack, bson, etc), but this list of boolean flags will grow unmanageable.
What do you think about
--input-format=xml
and--output-format=xml
instead of these format-specific parameters?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, looks to be a very good idea :)