Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Binaries
json2xml
json2xml-go
/json2xml
/json2xml-go
*.exe
*.exe~
*.dll
Expand Down
289 changes: 145 additions & 144 deletions cmd/json2xml-go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,15 @@ import (
json2xml "github.com/vinitkumar/json2xml-go"
)

const version = "1.0.0"
func main() {
os.Exit(run(os.Args[1:], os.Stdin, os.Stdout, os.Stderr))
}

var (
// Input options
// cliOptions holds all parsed CLI flags.
type cliOptions struct {
inputURL string
inputString string

// Output options
outputFile string

// Conversion options
outputFile string
wrapper string
root bool
pretty bool
Expand All @@ -68,52 +66,150 @@ var (
xpathFormat bool
cdata bool
listHeaders bool

// Other options
showVersion bool
showHelp bool
)
args []string
}

func init() {
// Input options
flag.StringVar(&inputURL, "u", "", "Read JSON from URL")
flag.StringVar(&inputURL, "url", "", "Read JSON from URL")
flag.StringVar(&inputString, "s", "", "Read JSON from string")
flag.StringVar(&inputString, "string", "", "Read JSON from string")

// Output options
flag.StringVar(&outputFile, "o", "", "Output file (default: stdout)")
flag.StringVar(&outputFile, "output", "", "Output file (default: stdout)")

// Conversion options
flag.StringVar(&wrapper, "w", "all", "Wrapper element name")
flag.StringVar(&wrapper, "wrapper", "all", "Wrapper element name")
flag.BoolVar(&root, "r", true, "Include root element")
flag.BoolVar(&root, "root", true, "Include root element")
flag.BoolVar(&pretty, "p", true, "Pretty print output")
flag.BoolVar(&pretty, "pretty", true, "Pretty print output")
flag.BoolVar(&attrType, "t", true, "Include type attributes")
flag.BoolVar(&attrType, "type", true, "Include type attributes")
flag.BoolVar(&itemWrap, "i", true, "Wrap list items in <item> elements")
flag.BoolVar(&itemWrap, "item-wrap", true, "Wrap list items in <item> elements")
flag.BoolVar(&xpathFormat, "x", false, "Use XPath 3.1 json-to-xml format")
flag.BoolVar(&xpathFormat, "xpath", false, "Use XPath 3.1 json-to-xml format")
flag.BoolVar(&cdata, "c", false, "Wrap string values in CDATA sections")
flag.BoolVar(&cdata, "cdata", false, "Wrap string values in CDATA sections")
flag.BoolVar(&listHeaders, "l", false, "Repeat headers for each list item")
flag.BoolVar(&listHeaders, "list-headers", false, "Repeat headers for each list item")

// Other options
flag.BoolVar(&showVersion, "v", false, "Show version information")
flag.BoolVar(&showVersion, "version", false, "Show version information")
flag.BoolVar(&showHelp, "h", false, "Show help message")
flag.BoolVar(&showHelp, "help", false, "Show help message")

flag.Usage = usage
func parseFlags(arguments []string) (cliOptions, error) {
var opts cliOptions
fs := flag.NewFlagSet("json2xml-go", flag.ContinueOnError)

fs.StringVar(&opts.inputURL, "u", "", "Read JSON from URL")
fs.StringVar(&opts.inputURL, "url", "", "Read JSON from URL")
fs.StringVar(&opts.inputString, "s", "", "Read JSON from string")
fs.StringVar(&opts.inputString, "string", "", "Read JSON from string")
fs.StringVar(&opts.outputFile, "o", "", "Output file (default: stdout)")
fs.StringVar(&opts.outputFile, "output", "", "Output file (default: stdout)")
fs.StringVar(&opts.wrapper, "w", "all", "Wrapper element name")
fs.StringVar(&opts.wrapper, "wrapper", "all", "Wrapper element name")
fs.BoolVar(&opts.root, "r", true, "Include root element")
fs.BoolVar(&opts.root, "root", true, "Include root element")
fs.BoolVar(&opts.pretty, "p", true, "Pretty print output")
fs.BoolVar(&opts.pretty, "pretty", true, "Pretty print output")
fs.BoolVar(&opts.attrType, "t", true, "Include type attributes")
fs.BoolVar(&opts.attrType, "type", true, "Include type attributes")
fs.BoolVar(&opts.itemWrap, "i", true, "Wrap list items in <item> elements")
fs.BoolVar(&opts.itemWrap, "item-wrap", true, "Wrap list items in <item> elements")
fs.BoolVar(&opts.xpathFormat, "x", false, "Use XPath 3.1 json-to-xml format")
fs.BoolVar(&opts.xpathFormat, "xpath", false, "Use XPath 3.1 json-to-xml format")
fs.BoolVar(&opts.cdata, "c", false, "Wrap string values in CDATA sections")
fs.BoolVar(&opts.cdata, "cdata", false, "Wrap string values in CDATA sections")
fs.BoolVar(&opts.listHeaders, "l", false, "Repeat headers for each list item")
fs.BoolVar(&opts.listHeaders, "list-headers", false, "Repeat headers for each list item")
fs.BoolVar(&opts.showVersion, "v", false, "Show version information")
fs.BoolVar(&opts.showVersion, "version", false, "Show version information")
fs.BoolVar(&opts.showHelp, "h", false, "Show help message")
fs.BoolVar(&opts.showHelp, "help", false, "Show help message")

fs.Usage = func() { printUsage(os.Stderr) }

if err := fs.Parse(arguments); err != nil {
return opts, err
}
opts.args = fs.Args()
return opts, nil
}

func usage() {
fmt.Fprintf(os.Stderr, `json2xml-go - Convert JSON to XML
func run(arguments []string, stdin io.Reader, stdout, stderr io.Writer) int {
opts, err := parseFlags(arguments)
if err != nil {
fmt.Fprintf(stderr, "Error: %v\n", err)
return 1
}

if opts.showHelp {
printUsage(stderr)
return 0
}

if opts.showVersion {
fmt.Fprintf(stdout, "json2xml-go version %s\n", json2xml.Version)
fmt.Fprintf(stdout, "Author: %s <%s>\n", json2xml.Author, json2xml.Email)
return 0
}

data, err := readInput(opts, stdin)
if err != nil {
fmt.Fprintf(stderr, "Error reading input: %v\n", err)
return 1
}

converter := json2xml.New(data).
WithWrapper(opts.wrapper).
WithRoot(opts.root).
WithPretty(opts.pretty).
WithAttrType(opts.attrType).
WithItemWrap(opts.itemWrap).
WithXPathFormat(opts.xpathFormat)

xmlOutput, err := converter.ToXMLString()
if err != nil {
fmt.Fprintf(stderr, "Error converting to XML: %v\n", err)
return 1
}

if err := writeOutput(xmlOutput, opts.outputFile, stdout); err != nil {
fmt.Fprintf(stderr, "Error writing output: %v\n", err)
return 1
}

return 0
}

func readInput(opts cliOptions, stdin io.Reader) (any, error) {
if opts.inputURL != "" {
return json2xml.ReadFromURL(opts.inputURL, nil)
}

if opts.inputString != "" {
return json2xml.ReadFromString(opts.inputString)
}

if len(opts.args) > 0 {
filename := opts.args[0]
if filename == "-" {
return readFromReader(stdin)
}
return json2xml.ReadFromJSON(filename)
}

// Check if stdin has data (only when stdin is *os.File)
if f, ok := stdin.(*os.File); ok {
stat, _ := f.Stat()
if (stat.Mode() & os.ModeCharDevice) == 0 {
return readFromReader(stdin)
}
}

return nil, fmt.Errorf("no input provided. Use -h for help")
}

func readFromReader(r io.Reader) (any, error) {
data, err := io.ReadAll(r)
if err != nil {
return nil, fmt.Errorf("failed to read from stdin: %w", err)
}

jsonStr := strings.TrimSpace(string(data))
if jsonStr == "" {
return nil, fmt.Errorf("empty input")
}

return json2xml.ReadFromString(jsonStr)
}

func writeOutput(output, outputFile string, stdout io.Writer) error {
if outputFile != "" {
return os.WriteFile(outputFile, []byte(output), 0644)
}

fmt.Fprintln(stdout, output)
return nil
}

func printUsage(w io.Writer) {
fmt.Fprintf(w, `json2xml-go - Convert JSON to XML

Usage:
json2xml-go [flags] [input-file]
Expand Down Expand Up @@ -167,98 +263,3 @@ Examples:

`)
}

func main() {
flag.Parse()

if showHelp {
usage()
os.Exit(0)
}

if showVersion {
fmt.Printf("json2xml-go version %s\n", version)
fmt.Printf("Author: %s <%s>\n", json2xml.Author, json2xml.Email)
os.Exit(0)
}

// Read input data
data, err := readInput()
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading input: %v\n", err)
os.Exit(1)
}

// Convert to XML
converter := json2xml.New(data).
WithWrapper(wrapper).
WithRoot(root).
WithPretty(pretty).
WithAttrType(attrType).
WithItemWrap(itemWrap).
WithXPathFormat(xpathFormat)

xmlOutput, err := converter.ToXMLString()
if err != nil {
fmt.Fprintf(os.Stderr, "Error converting to XML: %v\n", err)
os.Exit(1)
}

// Write output
if err := writeOutput(xmlOutput); err != nil {
fmt.Fprintf(os.Stderr, "Error writing output: %v\n", err)
os.Exit(1)
}
}

func readInput() (any, error) {
// Priority: URL > String > File > Stdin
if inputURL != "" {
return json2xml.ReadFromURL(inputURL, nil)
}

if inputString != "" {
return json2xml.ReadFromString(inputString)
}

args := flag.Args()
if len(args) > 0 {
filename := args[0]
if filename == "-" {
// Read from stdin
return readFromStdin()
}
return json2xml.ReadFromJSON(filename)
}

// Check if there's data on stdin
stat, _ := os.Stdin.Stat()
if (stat.Mode() & os.ModeCharDevice) == 0 {
return readFromStdin()
}

return nil, fmt.Errorf("no input provided. Use -h for help")
}

func readFromStdin() (any, error) {
data, err := io.ReadAll(os.Stdin)
if err != nil {
return nil, fmt.Errorf("failed to read from stdin: %w", err)
}

jsonStr := strings.TrimSpace(string(data))
if jsonStr == "" {
return nil, fmt.Errorf("empty input")
}

return json2xml.ReadFromString(jsonStr)
}

func writeOutput(output string) error {
if outputFile != "" {
return os.WriteFile(outputFile, []byte(output), 0644)
}

fmt.Println(output)
return nil
}
Loading
Loading