From df32baedf1e5426c2aed0c37eedf9078fbb9041a Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Tue, 21 Dec 2021 15:02:07 +1100 Subject: [PATCH] Added XML decoder (#1044) --- LICENSE | 2 + cmd/constant.go | 4 + cmd/evaluate_all_command.go | 11 +- cmd/evalute_sequence_command.go | 11 +- cmd/root.go | 5 + cmd/utils.go | 12 + examples/mike.xml | 4 + examples/mike2.xml | 11 + go.mod | 2 + go.sum | 2 + pkg/yqlib/all_at_once_evaluator.go | 6 +- pkg/yqlib/decode_xml_test.go | 110 ++++++++ pkg/yqlib/decoder_xml.go | 249 ++++++++++++++++++ pkg/yqlib/decoder_yaml.go | 28 ++ pkg/yqlib/doc/{ => operators}/add.md | 0 .../alternative-default-value.md | 0 .../anchor-and-alias-operators.md | 0 .../doc/{ => operators}/assign-update.md | 0 .../doc/{ => operators}/boolean-operators.md | 0 .../doc/{ => operators}/collect-into-array.md | 0 .../doc/{ => operators}/comment-operators.md | 0 pkg/yqlib/doc/{ => operators}/contains.md | 0 .../create-collect-into-object.md | 0 pkg/yqlib/doc/{ => operators}/delete.md | 0 .../doc/{ => operators}/document-index.md | 0 .../doc/{ => operators}/encode-decode.md | 16 ++ pkg/yqlib/doc/{ => operators}/entries.md | 0 .../{ => operators}/env-variable-operators.md | 0 pkg/yqlib/doc/{ => operators}/equals.md | 0 .../doc/{ => operators}/file-operators.md | 0 pkg/yqlib/doc/{ => operators}/flatten.md | 0 pkg/yqlib/doc/{ => operators}/group-by.md | 0 pkg/yqlib/doc/{ => operators}/has.md | 0 pkg/yqlib/doc/{ => operators}/headers/Main.md | 0 pkg/yqlib/doc/{ => operators}/headers/add.md | 0 .../headers/alternative-default-value.md | 0 .../headers/anchor-and-alias-operators.md | 0 .../{ => operators}/headers/assign-update.md | 0 .../headers/boolean-operators.md | 0 .../headers/collect-into-array.md | 0 .../headers/comment-operators.md | 0 .../doc/{ => operators}/headers/contains.md | 0 .../headers/create-collect-into-object.md | 0 .../doc/{ => operators}/headers/delete.md | 0 .../{ => operators}/headers/document-index.md | 0 .../{ => operators}/headers/encode-decode.md | 1 + .../doc/{ => operators}/headers/entries.md | 0 .../headers/env-variable-operators.md | 0 .../doc/{ => operators}/headers/equals.md | 0 .../{ => operators}/headers/file-operators.md | 0 .../doc/{ => operators}/headers/flatten.md | 0 .../doc/{ => operators}/headers/group-by.md | 0 pkg/yqlib/doc/{ => operators}/headers/has.md | 0 pkg/yqlib/doc/{ => operators}/headers/keys.md | 0 .../doc/{ => operators}/headers/length.md | 0 pkg/yqlib/doc/{ => operators}/headers/load.md | 0 pkg/yqlib/doc/{ => operators}/headers/map.md | 0 .../{ => operators}/headers/multiply-merge.md | 0 .../doc/{ => operators}/headers/parent.md | 0 pkg/yqlib/doc/{ => operators}/headers/path.md | 0 pkg/yqlib/doc/{ => operators}/headers/pipe.md | 0 .../headers/recursive-descent-glob.md | 0 .../doc/{ => operators}/headers/reduce.md | 0 .../doc/{ => operators}/headers/select.md | 0 .../doc/{ => operators}/headers/sort-keys.md | 0 pkg/yqlib/doc/{ => operators}/headers/sort.md | 0 .../headers/split-into-documents.md | 0 .../headers/string-operators.md | 0 .../doc/{ => operators}/headers/style.md | 0 .../doc/{ => operators}/headers/subtract.md | 0 pkg/yqlib/doc/{ => operators}/headers/tag.md | 0 .../{ => operators}/headers/traverse-read.md | 0 .../doc/{ => operators}/headers/union.md | 0 .../doc/{ => operators}/headers/unique.md | 0 .../headers/variable-operators.md | 0 pkg/yqlib/doc/{ => operators}/headers/with.md | 0 pkg/yqlib/doc/{ => operators}/keys.md | 0 pkg/yqlib/doc/{ => operators}/length.md | 0 pkg/yqlib/doc/{ => operators}/load.md | 0 pkg/yqlib/doc/{ => operators}/map.md | 0 .../doc/{ => operators}/multiply-merge.md | 0 pkg/yqlib/doc/{ => operators}/parent.md | 0 pkg/yqlib/doc/{ => operators}/path.md | 0 pkg/yqlib/doc/{ => operators}/pipe.md | 0 .../{ => operators}/recursive-descent-glob.md | 0 pkg/yqlib/doc/{ => operators}/reduce.md | 0 pkg/yqlib/doc/{ => operators}/select.md | 0 pkg/yqlib/doc/{ => operators}/sort-keys.md | 0 pkg/yqlib/doc/{ => operators}/sort.md | 0 .../{ => operators}/split-into-documents.md | 0 .../doc/{ => operators}/string-operators.md | 0 pkg/yqlib/doc/{ => operators}/style.md | 0 pkg/yqlib/doc/{ => operators}/subtract.md | 0 pkg/yqlib/doc/{ => operators}/tag.md | 0 .../doc/{ => operators}/traverse-read.md | 0 pkg/yqlib/doc/{ => operators}/union.md | 0 pkg/yqlib/doc/{ => operators}/unique.md | 0 .../doc/{ => operators}/variable-operators.md | 0 pkg/yqlib/doc/{ => operators}/with.md | 0 pkg/yqlib/doc/usage/headers/xml.md | 12 + pkg/yqlib/doc/usage/xml.md | 88 +++++++ pkg/yqlib/encoder_csv_test.go | 2 +- pkg/yqlib/encoder_properties_test.go | 2 +- pkg/yqlib/encoder_test.go | 2 +- pkg/yqlib/expression_tokeniser.go | 33 ++- pkg/yqlib/operator_add_test.go | 2 +- pkg/yqlib/operator_alternative_test.go | 2 +- pkg/yqlib/operator_anchors_aliases_test.go | 2 +- pkg/yqlib/operator_assign_test.go | 2 +- pkg/yqlib/operator_booleans_test.go | 2 +- pkg/yqlib/operator_collect_object_test.go | 2 +- pkg/yqlib/operator_collect_test.go | 2 +- pkg/yqlib/operator_comments_test.go | 2 +- pkg/yqlib/operator_contains_test.go | 2 +- pkg/yqlib/operator_delete_test.go | 2 +- pkg/yqlib/operator_document_index_test.go | 2 +- pkg/yqlib/operator_encoder_decoder.go | 18 +- pkg/yqlib/operator_encoder_decoder_test.go | 10 +- pkg/yqlib/operator_entries_test.go | 2 +- pkg/yqlib/operator_env_test.go | 2 +- pkg/yqlib/operator_equals_test.go | 2 +- pkg/yqlib/operator_file_test.go | 2 +- pkg/yqlib/operator_flatten_test.go | 2 +- pkg/yqlib/operator_group_by_test.go | 2 +- pkg/yqlib/operator_has_test.go | 2 +- pkg/yqlib/operator_keys_test.go | 2 +- pkg/yqlib/operator_length_test.go | 2 +- pkg/yqlib/operator_load.go | 7 +- pkg/yqlib/operator_load_test.go | 2 +- pkg/yqlib/operator_map_test.go | 2 +- pkg/yqlib/operator_multiply_test.go | 2 +- pkg/yqlib/operator_parent_test.go | 2 +- pkg/yqlib/operator_path_test.go | 2 +- pkg/yqlib/operator_pipe_test.go | 2 +- pkg/yqlib/operator_recursive_descent_test.go | 2 +- pkg/yqlib/operator_reduce_test.go | 2 +- pkg/yqlib/operator_select_test.go | 2 +- pkg/yqlib/operator_sort_keys_test.go | 2 +- pkg/yqlib/operator_sort_test.go | 2 +- pkg/yqlib/operator_split_document_test.go | 2 +- pkg/yqlib/operator_strings_test.go | 2 +- pkg/yqlib/operator_style_test.go | 2 +- pkg/yqlib/operator_subtract_test.go | 2 +- pkg/yqlib/operator_tag_test.go | 2 +- pkg/yqlib/operator_traverse_path_test.go | 2 +- pkg/yqlib/operator_union_test.go | 2 +- pkg/yqlib/operator_unique_test.go | 2 +- pkg/yqlib/operator_variables_test.go | 2 +- pkg/yqlib/operator_with_test.go | 2 +- pkg/yqlib/operators_test.go | 71 +++-- pkg/yqlib/printer_test.go | 18 +- pkg/yqlib/stream_evaluator.go | 12 +- pkg/yqlib/utils.go | 4 +- scripts/copy-docs.sh | 3 +- 154 files changed, 725 insertions(+), 113 deletions(-) create mode 100644 examples/mike.xml create mode 100644 examples/mike2.xml create mode 100644 pkg/yqlib/decode_xml_test.go create mode 100644 pkg/yqlib/decoder_xml.go create mode 100644 pkg/yqlib/decoder_yaml.go rename pkg/yqlib/doc/{ => operators}/add.md (100%) rename pkg/yqlib/doc/{ => operators}/alternative-default-value.md (100%) rename pkg/yqlib/doc/{ => operators}/anchor-and-alias-operators.md (100%) rename pkg/yqlib/doc/{ => operators}/assign-update.md (100%) rename pkg/yqlib/doc/{ => operators}/boolean-operators.md (100%) rename pkg/yqlib/doc/{ => operators}/collect-into-array.md (100%) rename pkg/yqlib/doc/{ => operators}/comment-operators.md (100%) rename pkg/yqlib/doc/{ => operators}/contains.md (100%) rename pkg/yqlib/doc/{ => operators}/create-collect-into-object.md (100%) rename pkg/yqlib/doc/{ => operators}/delete.md (100%) rename pkg/yqlib/doc/{ => operators}/document-index.md (100%) rename pkg/yqlib/doc/{ => operators}/encode-decode.md (95%) rename pkg/yqlib/doc/{ => operators}/entries.md (100%) rename pkg/yqlib/doc/{ => operators}/env-variable-operators.md (100%) rename pkg/yqlib/doc/{ => operators}/equals.md (100%) rename pkg/yqlib/doc/{ => operators}/file-operators.md (100%) rename pkg/yqlib/doc/{ => operators}/flatten.md (100%) rename pkg/yqlib/doc/{ => operators}/group-by.md (100%) rename pkg/yqlib/doc/{ => operators}/has.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/Main.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/add.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/alternative-default-value.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/anchor-and-alias-operators.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/assign-update.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/boolean-operators.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/collect-into-array.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/comment-operators.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/contains.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/create-collect-into-object.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/delete.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/document-index.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/encode-decode.md (97%) rename pkg/yqlib/doc/{ => operators}/headers/entries.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/env-variable-operators.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/equals.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/file-operators.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/flatten.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/group-by.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/has.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/keys.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/length.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/load.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/map.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/multiply-merge.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/parent.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/path.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/pipe.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/recursive-descent-glob.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/reduce.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/select.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/sort-keys.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/sort.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/split-into-documents.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/string-operators.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/style.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/subtract.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/tag.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/traverse-read.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/union.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/unique.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/variable-operators.md (100%) rename pkg/yqlib/doc/{ => operators}/headers/with.md (100%) rename pkg/yqlib/doc/{ => operators}/keys.md (100%) rename pkg/yqlib/doc/{ => operators}/length.md (100%) rename pkg/yqlib/doc/{ => operators}/load.md (100%) rename pkg/yqlib/doc/{ => operators}/map.md (100%) rename pkg/yqlib/doc/{ => operators}/multiply-merge.md (100%) rename pkg/yqlib/doc/{ => operators}/parent.md (100%) rename pkg/yqlib/doc/{ => operators}/path.md (100%) rename pkg/yqlib/doc/{ => operators}/pipe.md (100%) rename pkg/yqlib/doc/{ => operators}/recursive-descent-glob.md (100%) rename pkg/yqlib/doc/{ => operators}/reduce.md (100%) rename pkg/yqlib/doc/{ => operators}/select.md (100%) rename pkg/yqlib/doc/{ => operators}/sort-keys.md (100%) rename pkg/yqlib/doc/{ => operators}/sort.md (100%) rename pkg/yqlib/doc/{ => operators}/split-into-documents.md (100%) rename pkg/yqlib/doc/{ => operators}/string-operators.md (100%) rename pkg/yqlib/doc/{ => operators}/style.md (100%) rename pkg/yqlib/doc/{ => operators}/subtract.md (100%) rename pkg/yqlib/doc/{ => operators}/tag.md (100%) rename pkg/yqlib/doc/{ => operators}/traverse-read.md (100%) rename pkg/yqlib/doc/{ => operators}/union.md (100%) rename pkg/yqlib/doc/{ => operators}/unique.md (100%) rename pkg/yqlib/doc/{ => operators}/variable-operators.md (100%) rename pkg/yqlib/doc/{ => operators}/with.md (100%) create mode 100644 pkg/yqlib/doc/usage/headers/xml.md create mode 100644 pkg/yqlib/doc/usage/xml.md diff --git a/LICENSE b/LICENSE index e2f1fc03..dfbcc195 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,5 @@ +The MIT License (MIT) + Copyright (c) 2017 Mike Farah Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/cmd/constant.go b/cmd/constant.go index 0b921410..5cca3c5c 100644 --- a/cmd/constant.go +++ b/cmd/constant.go @@ -6,6 +6,10 @@ var unwrapScalar = true var writeInplace = false var outputToJSON = false var outputFormat = "yaml" +var inputFormat = "yaml" + +var xmlAttributePrefix = "+" +var xmlContentName = "+content" var exitStatus = false var forceColor = false diff --git a/cmd/evaluate_all_command.go b/cmd/evaluate_all_command.go index af3c6ec7..c52e2526 100644 --- a/cmd/evaluate_all_command.go +++ b/cmd/evaluate_all_command.go @@ -75,6 +75,11 @@ func evaluateAll(cmd *cobra.Command, args []string) error { return err } + decoder, err := configureDecoder() + if err != nil { + return err + } + printerWriter := configurePrinterWriter(format, out) printer := yqlib.NewPrinter(printerWriter, format, unwrapScalar, colorsEnabled, indent, !noDocSeparators) @@ -99,7 +104,7 @@ func evaluateAll(cmd *cobra.Command, args []string) error { switch len(args) { case 0: if pipingStdIn { - err = allAtOnceEvaluator.EvaluateFiles(processExpression(""), []string{"-"}, printer, leadingContentPreProcessing) + err = allAtOnceEvaluator.EvaluateFiles(processExpression(""), []string{"-"}, printer, leadingContentPreProcessing, decoder) } else { cmd.Println(cmd.UsageString()) return nil @@ -108,10 +113,10 @@ func evaluateAll(cmd *cobra.Command, args []string) error { if nullInput { err = yqlib.NewStreamEvaluator().EvaluateNew(processExpression(args[0]), printer, "") } else { - err = allAtOnceEvaluator.EvaluateFiles(processExpression(""), []string{args[0]}, printer, leadingContentPreProcessing) + err = allAtOnceEvaluator.EvaluateFiles(processExpression(""), []string{args[0]}, printer, leadingContentPreProcessing, decoder) } default: - err = allAtOnceEvaluator.EvaluateFiles(processExpression(args[0]), args[1:], printer, leadingContentPreProcessing) + err = allAtOnceEvaluator.EvaluateFiles(processExpression(args[0]), args[1:], printer, leadingContentPreProcessing, decoder) } completedSuccessfully = err == nil diff --git a/cmd/evalute_sequence_command.go b/cmd/evalute_sequence_command.go index e875c4bd..2a371ed4 100644 --- a/cmd/evalute_sequence_command.go +++ b/cmd/evalute_sequence_command.go @@ -92,6 +92,11 @@ func evaluateSequence(cmd *cobra.Command, args []string) error { printer := yqlib.NewPrinter(printerWriter, format, unwrapScalar, colorsEnabled, indent, !noDocSeparators) + decoder, err := configureDecoder() + if err != nil { + return err + } + streamEvaluator := yqlib.NewStreamEvaluator() if frontMatter != "" { @@ -113,7 +118,7 @@ func evaluateSequence(cmd *cobra.Command, args []string) error { switch len(args) { case 0: if pipingStdIn { - err = streamEvaluator.EvaluateFiles(processExpression(""), []string{"-"}, printer, leadingContentPreProcessing) + err = streamEvaluator.EvaluateFiles(processExpression(""), []string{"-"}, printer, leadingContentPreProcessing, decoder) } else { cmd.Println(cmd.UsageString()) return nil @@ -122,10 +127,10 @@ func evaluateSequence(cmd *cobra.Command, args []string) error { if nullInput { err = streamEvaluator.EvaluateNew(processExpression(args[0]), printer, "") } else { - err = streamEvaluator.EvaluateFiles(processExpression(""), []string{args[0]}, printer, leadingContentPreProcessing) + err = streamEvaluator.EvaluateFiles(processExpression(""), []string{args[0]}, printer, leadingContentPreProcessing, decoder) } default: - err = streamEvaluator.EvaluateFiles(processExpression(args[0]), args[1:], printer, leadingContentPreProcessing) + err = streamEvaluator.EvaluateFiles(processExpression(args[0]), args[1:], printer, leadingContentPreProcessing, decoder) } completedSuccessfully = err == nil diff --git a/cmd/root.go b/cmd/root.go index 11471d27..28e22648 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -49,6 +49,11 @@ See https://mikefarah.gitbook.io/yq/ for detailed documentation and examples.`, } rootCmd.PersistentFlags().StringVarP(&outputFormat, "output-format", "o", "yaml", "[yaml|y|json|j|props|p] output format type.") + rootCmd.PersistentFlags().StringVarP(&inputFormat, "input-format", "p", "yaml", "[yaml|y|xml|x] input format type.") + + rootCmd.PersistentFlags().StringVar(&xmlAttributePrefix, "xml-attribute-prefix", "+", "prefix for xml attributes") + rootCmd.PersistentFlags().StringVar(&xmlContentName, "xml-content-name", "+content", "name for xml content (if no attribute name is present).") + rootCmd.PersistentFlags().BoolVarP(&nullInput, "null-input", "n", false, "Don't read input, simply evaluate the expression given. Useful for creating yaml docs from scratch.") rootCmd.PersistentFlags().BoolVarP(&noDocSeparators, "no-doc", "N", false, "Don't print document separators (---)") diff --git a/cmd/utils.go b/cmd/utils.go index 483ea426..7ab518a4 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -45,6 +45,18 @@ func initCommand(cmd *cobra.Command, args []string) (firstFileIndex int, err err return firstFileIndex, nil } +func configureDecoder() (yqlib.Decoder, error) { + yqlibInputFormat, err := yqlib.InputFormatFromString(inputFormat) + if err != nil { + return nil, err + } + switch yqlibInputFormat { + case yqlib.XmlInputFormat: + return yqlib.NewXmlDecoder(xmlAttributePrefix, xmlContentName), nil + } + return yqlib.NewYamlDecoder(), nil +} + func configurePrinterWriter(format yqlib.PrinterOutputFormat, out io.Writer) yqlib.PrinterWriter { var printerWriter yqlib.PrinterWriter diff --git a/examples/mike.xml b/examples/mike.xml new file mode 100644 index 00000000..e64b598b --- /dev/null +++ b/examples/mike.xml @@ -0,0 +1,4 @@ + + 3f + meow:as + true \ No newline at end of file diff --git a/examples/mike2.xml b/examples/mike2.xml new file mode 100644 index 00000000..0ad80955 --- /dev/null +++ b/examples/mike2.xml @@ -0,0 +1,11 @@ + + + + + + + cool + + ba2234r + bar2234233 + \ No newline at end of file diff --git a/go.mod b/go.mod index ac72186a..be0a0302 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/magiconair/properties v1.8.5 github.com/spf13/cobra v1.3.0 github.com/timtadh/lexmachine v0.2.2 + golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) @@ -19,6 +20,7 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/timtadh/data-structures v0.5.3 // indirect golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect + golang.org/x/text v0.3.7 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect ) diff --git a/go.sum b/go.sum index 1cdc7256..aad032da 100644 --- a/go.sum +++ b/go.sum @@ -449,6 +449,7 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -553,6 +554,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/pkg/yqlib/all_at_once_evaluator.go b/pkg/yqlib/all_at_once_evaluator.go index d23c56c6..371bc48f 100644 --- a/pkg/yqlib/all_at_once_evaluator.go +++ b/pkg/yqlib/all_at_once_evaluator.go @@ -8,7 +8,7 @@ import ( // A yaml expression evaluator that runs the expression once against all files/nodes in memory. type Evaluator interface { - EvaluateFiles(expression string, filenames []string, printer Printer, leadingContentPreProcessing bool) error + EvaluateFiles(expression string, filenames []string, printer Printer, leadingContentPreProcessing bool, decoder Decoder) error // EvaluateNodes takes an expression and one or more yaml nodes, returning a list of matching candidate nodes EvaluateNodes(expression string, nodes ...*yaml.Node) (*list.List, error) @@ -46,7 +46,7 @@ func (e *allAtOnceEvaluator) EvaluateCandidateNodes(expression string, inputCand return context.MatchingNodes, nil } -func (e *allAtOnceEvaluator) EvaluateFiles(expression string, filenames []string, printer Printer, leadingContentPreProcessing bool) error { +func (e *allAtOnceEvaluator) EvaluateFiles(expression string, filenames []string, printer Printer, leadingContentPreProcessing bool, decoder Decoder) error { fileIndex := 0 firstFileLeadingContent := "" @@ -61,7 +61,7 @@ func (e *allAtOnceEvaluator) EvaluateFiles(expression string, filenames []string firstFileLeadingContent = leadingContent } - fileDocuments, err := readDocuments(reader, filename, fileIndex) + fileDocuments, err := readDocuments(reader, filename, fileIndex, decoder) if err != nil { return err } diff --git a/pkg/yqlib/decode_xml_test.go b/pkg/yqlib/decode_xml_test.go new file mode 100644 index 00000000..e0d72d21 --- /dev/null +++ b/pkg/yqlib/decode_xml_test.go @@ -0,0 +1,110 @@ +package yqlib + +import ( + "bufio" + "bytes" + "fmt" + "strings" + "testing" + + "github.com/mikefarah/yq/v4/test" + yaml "gopkg.in/yaml.v3" +) + +func decodeXml(t *testing.T, xml string) *CandidateNode { + decoder := NewXmlDecoder("+", "+content") + + decoder.Init(strings.NewReader(xml)) + + node := &yaml.Node{} + err := decoder.Decode(node) + if err != nil { + t.Error(err, "fail to decode", xml) + } + return &CandidateNode{Node: node} +} + +type xmlScenario struct { + inputXml string + expected string + description string + subdescription string + skipDoc bool +} + +var xmlScenarios = []xmlScenario{ + { + description: "Parse xml: simple", + inputXml: "\nmeow", + expected: "D0, P[], (doc)::cat: meow\n", + }, + { + description: "Parse xml: array", + subdescription: "Consecutive nodes with identical xml names are assumed to be arrays.", + inputXml: "\n1\n2", + expected: "D0, P[], (doc)::animal:\n - \"1\"\n - \"2\"\n", + }, + { + description: "Parse xml: attributes", + subdescription: "Attributes are converted to fields, with the attribute prefix.", + inputXml: "\n\n 7\n", + expected: "D0, P[], (doc)::cat:\n +legs: \"4\"\n legs: \"7\"\n", + }, + { + description: "Parse xml: attributes with content", + subdescription: "Content is added as a field, using the content name", + inputXml: "\nmeow", + expected: "D0, P[], (doc)::cat:\n +content: meow\n +legs: \"4\"\n", + }, +} + +func testXmlScenario(t *testing.T, s *xmlScenario) { + var actual = resultToString(t, decodeXml(t, s.inputXml)) + test.AssertResult(t, s.expected, actual) +} + +func documentXmlScenario(t *testing.T, w *bufio.Writer, i interface{}) { + s := i.(xmlScenario) + + if s.skipDoc { + return + } + writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) + + if s.subdescription != "" { + writeOrPanic(w, s.subdescription) + writeOrPanic(w, "\n\n") + } + + writeOrPanic(w, "Given a sample.xml file of:\n") + writeOrPanic(w, fmt.Sprintf("```xml\n%v\n```\n", s.inputXml)) + + writeOrPanic(w, "then\n") + writeOrPanic(w, "```bash\nyq e sample.xml\n```\n") + writeOrPanic(w, "will output\n") + + var output bytes.Buffer + printer := NewPrinterWithSingleWriter(bufio.NewWriter(&output), YamlOutputFormat, true, false, 2, true) + + node := decodeXml(t, s.inputXml) + + err := printer.PrintResults(node.AsList()) + if err != nil { + t.Error(err) + return + } + + writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", output.String())) + +} + +func TestXmlScenarios(t *testing.T) { + for _, tt := range xmlScenarios { + testXmlScenario(t, &tt) + } + genericScenarios := make([]interface{}, len(xmlScenarios)) + for i, s := range xmlScenarios { + genericScenarios[i] = s + } + documentScenarios(t, "usage", "xml", genericScenarios, documentXmlScenario) +} diff --git a/pkg/yqlib/decoder_xml.go b/pkg/yqlib/decoder_xml.go new file mode 100644 index 00000000..ada214ca --- /dev/null +++ b/pkg/yqlib/decoder_xml.go @@ -0,0 +1,249 @@ +package yqlib + +import ( + "encoding/xml" + "fmt" + "io" + "unicode" + + "golang.org/x/net/html/charset" + yaml "gopkg.in/yaml.v3" +) + +type InputFormat uint + +const ( + YamlInputFormat = 1 << iota + XmlInputFormat +) + +func InputFormatFromString(format string) (InputFormat, error) { + switch format { + case "yaml", "y": + return YamlInputFormat, nil + case "xml", "x": + return XmlInputFormat, nil + default: + return 0, fmt.Errorf("unknown format '%v' please use [yaml|xml]", format) + } +} + +type xmlDecoder struct { + reader io.Reader + attributePrefix string + contentPrefix string + finished bool +} + +func NewXmlDecoder(attributePrefix string, contentPrefix string) Decoder { + if contentPrefix == "" { + contentPrefix = "content" + } + return &xmlDecoder{attributePrefix: attributePrefix, contentPrefix: contentPrefix, finished: false} +} + +func (dec *xmlDecoder) Init(reader io.Reader) { + dec.reader = reader + dec.finished = false +} + +func (dec *xmlDecoder) createSequence(nodes []*xmlNode) (*yaml.Node, error) { + yamlNode := &yaml.Node{Kind: yaml.SequenceNode} + for _, child := range nodes { + yamlChild, err := dec.convertToYamlNode(child) + if err != nil { + return nil, err + } + yamlNode.Content = append(yamlNode.Content, yamlChild) + } + + return yamlNode, nil +} + +func (dec *xmlDecoder) createMap(n *xmlNode) (*yaml.Node, error) { + yamlNode := &yaml.Node{Kind: yaml.MappingNode, HeadComment: n.Comment} + + if len(n.Data) > 0 { + label := dec.contentPrefix + yamlNode.Content = append(yamlNode.Content, createScalarNode(label, label), createScalarNode(n.Data, n.Data)) + } + + for _, keyValuePair := range n.Children { + label := keyValuePair.K + children := keyValuePair.V + labelNode := createScalarNode(label, label) + var valueNode *yaml.Node + var err error + log.Debug("len of children in %v is %v", label, len(children)) + if len(children) > 1 { + valueNode, err = dec.createSequence(children) + if err != nil { + return nil, err + } + } else { + valueNode, err = dec.convertToYamlNode(children[0]) + if err != nil { + return nil, err + } + } + yamlNode.Content = append(yamlNode.Content, labelNode, valueNode) + } + + return yamlNode, nil +} + +func (dec *xmlDecoder) convertToYamlNode(n *xmlNode) (*yaml.Node, error) { + if len(n.Children) > 0 { + return dec.createMap(n) + } + scalar := createScalarNode(n.Data, n.Data) + scalar.HeadComment = n.Comment + return scalar, nil +} + +func (dec *xmlDecoder) Decode(rootYamlNode *yaml.Node) error { + if dec.finished { + return io.EOF + } + root := &xmlNode{} + // cant use xj - it doesn't keep map order. + err := dec.decodeXml(root) + + if err != nil { + return err + } + firstNode, err := dec.convertToYamlNode(root) + + if err != nil { + return err + } + rootYamlNode.Kind = yaml.DocumentNode + rootYamlNode.Content = []*yaml.Node{firstNode} + dec.finished = true + return nil +} + +type xmlNode struct { + Children []*xmlChildrenKv + Comment string + Data string +} + +type xmlChildrenKv struct { + K string + V []*xmlNode +} + +// AddChild appends a node to the list of children +func (n *xmlNode) AddChild(s string, c *xmlNode) { + + if n.Children == nil { + n.Children = make([]*xmlChildrenKv, 0) + } + log.Debug("looking for %s", s) + // see if we can find an existing entry to add to + for _, childEntry := range n.Children { + if childEntry.K == s { + log.Debug("found it, appending an entry%s", s) + childEntry.V = append(childEntry.V, c) + log.Debug("yay len of children in %v is %v", s, len(childEntry.V)) + return + } + } + log.Debug("not there, making a new one %s", s) + n.Children = append(n.Children, &xmlChildrenKv{K: s, V: []*xmlNode{c}}) +} + +type element struct { + parent *element + n *xmlNode + label string +} + +// this code is heavily based on https://github.com/basgys/goxml2json +// main changes are to decode into a structure that preserves the original order +// of the map keys. +func (dec *xmlDecoder) decodeXml(root *xmlNode) error { + xmlDec := xml.NewDecoder(dec.reader) + + // That will convert the charset if the provided XML is non-UTF-8 + xmlDec.CharsetReader = charset.NewReaderLabel + + // Create first element from the root node + elem := &element{ + parent: nil, + n: root, + } + + for { + t, _ := xmlDec.Token() + if t == nil { + break + } + + switch se := t.(type) { + case xml.StartElement: + // Build new a new current element and link it to its parent + elem = &element{ + parent: elem, + n: &xmlNode{}, + label: se.Name.Local, + } + + // Extract attributes as children + for _, a := range se.Attr { + elem.n.AddChild(dec.attributePrefix+a.Name.Local, &xmlNode{Data: a.Value}) + } + case xml.CharData: + // Extract XML data (if any) + elem.n.Data = trimNonGraphic(string(se)) + case xml.EndElement: + // And add it to its parent list + if elem.parent != nil { + elem.parent.n.AddChild(elem.label, elem.n) + } + + // Then change the current element to its parent + elem = elem.parent + case xml.Comment: + elem.n.Comment = trimNonGraphic(string(xml.CharData(se))) + } + } + + return nil +} + +// trimNonGraphic returns a slice of the string s, with all leading and trailing +// non graphic characters and spaces removed. +// +// Graphic characters include letters, marks, numbers, punctuation, symbols, +// and spaces, from categories L, M, N, P, S, Zs. +// Spacing characters are set by category Z and property Pattern_White_Space. +func trimNonGraphic(s string) string { + if s == "" { + return s + } + + var first *int + var last int + for i, r := range []rune(s) { + if !unicode.IsGraphic(r) || unicode.IsSpace(r) { + continue + } + + if first == nil { + f := i // copy i + first = &f + last = i + } else { + last = i + } + } + + // If first is nil, it means there are no graphic characters + if first == nil { + return "" + } + + return string([]rune(s)[*first : last+1]) +} diff --git a/pkg/yqlib/decoder_yaml.go b/pkg/yqlib/decoder_yaml.go new file mode 100644 index 00000000..58f1c520 --- /dev/null +++ b/pkg/yqlib/decoder_yaml.go @@ -0,0 +1,28 @@ +package yqlib + +import ( + "io" + + yaml "gopkg.in/yaml.v3" +) + +type Decoder interface { + Init(reader io.Reader) + Decode(node *yaml.Node) error +} + +type yamlDecoder struct { + decoder yaml.Decoder +} + +func NewYamlDecoder() Decoder { + return &yamlDecoder{} +} + +func (dec *yamlDecoder) Init(reader io.Reader) { + dec.decoder = *yaml.NewDecoder(reader) +} + +func (dec *yamlDecoder) Decode(rootYamlNode *yaml.Node) error { + return dec.decoder.Decode(rootYamlNode) +} diff --git a/pkg/yqlib/doc/add.md b/pkg/yqlib/doc/operators/add.md similarity index 100% rename from pkg/yqlib/doc/add.md rename to pkg/yqlib/doc/operators/add.md diff --git a/pkg/yqlib/doc/alternative-default-value.md b/pkg/yqlib/doc/operators/alternative-default-value.md similarity index 100% rename from pkg/yqlib/doc/alternative-default-value.md rename to pkg/yqlib/doc/operators/alternative-default-value.md diff --git a/pkg/yqlib/doc/anchor-and-alias-operators.md b/pkg/yqlib/doc/operators/anchor-and-alias-operators.md similarity index 100% rename from pkg/yqlib/doc/anchor-and-alias-operators.md rename to pkg/yqlib/doc/operators/anchor-and-alias-operators.md diff --git a/pkg/yqlib/doc/assign-update.md b/pkg/yqlib/doc/operators/assign-update.md similarity index 100% rename from pkg/yqlib/doc/assign-update.md rename to pkg/yqlib/doc/operators/assign-update.md diff --git a/pkg/yqlib/doc/boolean-operators.md b/pkg/yqlib/doc/operators/boolean-operators.md similarity index 100% rename from pkg/yqlib/doc/boolean-operators.md rename to pkg/yqlib/doc/operators/boolean-operators.md diff --git a/pkg/yqlib/doc/collect-into-array.md b/pkg/yqlib/doc/operators/collect-into-array.md similarity index 100% rename from pkg/yqlib/doc/collect-into-array.md rename to pkg/yqlib/doc/operators/collect-into-array.md diff --git a/pkg/yqlib/doc/comment-operators.md b/pkg/yqlib/doc/operators/comment-operators.md similarity index 100% rename from pkg/yqlib/doc/comment-operators.md rename to pkg/yqlib/doc/operators/comment-operators.md diff --git a/pkg/yqlib/doc/contains.md b/pkg/yqlib/doc/operators/contains.md similarity index 100% rename from pkg/yqlib/doc/contains.md rename to pkg/yqlib/doc/operators/contains.md diff --git a/pkg/yqlib/doc/create-collect-into-object.md b/pkg/yqlib/doc/operators/create-collect-into-object.md similarity index 100% rename from pkg/yqlib/doc/create-collect-into-object.md rename to pkg/yqlib/doc/operators/create-collect-into-object.md diff --git a/pkg/yqlib/doc/delete.md b/pkg/yqlib/doc/operators/delete.md similarity index 100% rename from pkg/yqlib/doc/delete.md rename to pkg/yqlib/doc/operators/delete.md diff --git a/pkg/yqlib/doc/document-index.md b/pkg/yqlib/doc/operators/document-index.md similarity index 100% rename from pkg/yqlib/doc/document-index.md rename to pkg/yqlib/doc/operators/document-index.md diff --git a/pkg/yqlib/doc/encode-decode.md b/pkg/yqlib/doc/operators/encode-decode.md similarity index 95% rename from pkg/yqlib/doc/encode-decode.md rename to pkg/yqlib/doc/operators/encode-decode.md index d4416608..d77aa917 100644 --- a/pkg/yqlib/doc/encode-decode.md +++ b/pkg/yqlib/doc/operators/encode-decode.md @@ -271,3 +271,19 @@ cat thing1,thing2 true 3.40 dog thing3 false 12 ``` +## Decode a xml encoded string +Given a sample.yml file of: +```yaml +a: bar +``` +then +```bash +yq eval '.b = (.a | from_xml)' sample.yml +``` +will output +```yaml +a: bar +b: + foo: bar +``` + diff --git a/pkg/yqlib/doc/entries.md b/pkg/yqlib/doc/operators/entries.md similarity index 100% rename from pkg/yqlib/doc/entries.md rename to pkg/yqlib/doc/operators/entries.md diff --git a/pkg/yqlib/doc/env-variable-operators.md b/pkg/yqlib/doc/operators/env-variable-operators.md similarity index 100% rename from pkg/yqlib/doc/env-variable-operators.md rename to pkg/yqlib/doc/operators/env-variable-operators.md diff --git a/pkg/yqlib/doc/equals.md b/pkg/yqlib/doc/operators/equals.md similarity index 100% rename from pkg/yqlib/doc/equals.md rename to pkg/yqlib/doc/operators/equals.md diff --git a/pkg/yqlib/doc/file-operators.md b/pkg/yqlib/doc/operators/file-operators.md similarity index 100% rename from pkg/yqlib/doc/file-operators.md rename to pkg/yqlib/doc/operators/file-operators.md diff --git a/pkg/yqlib/doc/flatten.md b/pkg/yqlib/doc/operators/flatten.md similarity index 100% rename from pkg/yqlib/doc/flatten.md rename to pkg/yqlib/doc/operators/flatten.md diff --git a/pkg/yqlib/doc/group-by.md b/pkg/yqlib/doc/operators/group-by.md similarity index 100% rename from pkg/yqlib/doc/group-by.md rename to pkg/yqlib/doc/operators/group-by.md diff --git a/pkg/yqlib/doc/has.md b/pkg/yqlib/doc/operators/has.md similarity index 100% rename from pkg/yqlib/doc/has.md rename to pkg/yqlib/doc/operators/has.md diff --git a/pkg/yqlib/doc/headers/Main.md b/pkg/yqlib/doc/operators/headers/Main.md similarity index 100% rename from pkg/yqlib/doc/headers/Main.md rename to pkg/yqlib/doc/operators/headers/Main.md diff --git a/pkg/yqlib/doc/headers/add.md b/pkg/yqlib/doc/operators/headers/add.md similarity index 100% rename from pkg/yqlib/doc/headers/add.md rename to pkg/yqlib/doc/operators/headers/add.md diff --git a/pkg/yqlib/doc/headers/alternative-default-value.md b/pkg/yqlib/doc/operators/headers/alternative-default-value.md similarity index 100% rename from pkg/yqlib/doc/headers/alternative-default-value.md rename to pkg/yqlib/doc/operators/headers/alternative-default-value.md diff --git a/pkg/yqlib/doc/headers/anchor-and-alias-operators.md b/pkg/yqlib/doc/operators/headers/anchor-and-alias-operators.md similarity index 100% rename from pkg/yqlib/doc/headers/anchor-and-alias-operators.md rename to pkg/yqlib/doc/operators/headers/anchor-and-alias-operators.md diff --git a/pkg/yqlib/doc/headers/assign-update.md b/pkg/yqlib/doc/operators/headers/assign-update.md similarity index 100% rename from pkg/yqlib/doc/headers/assign-update.md rename to pkg/yqlib/doc/operators/headers/assign-update.md diff --git a/pkg/yqlib/doc/headers/boolean-operators.md b/pkg/yqlib/doc/operators/headers/boolean-operators.md similarity index 100% rename from pkg/yqlib/doc/headers/boolean-operators.md rename to pkg/yqlib/doc/operators/headers/boolean-operators.md diff --git a/pkg/yqlib/doc/headers/collect-into-array.md b/pkg/yqlib/doc/operators/headers/collect-into-array.md similarity index 100% rename from pkg/yqlib/doc/headers/collect-into-array.md rename to pkg/yqlib/doc/operators/headers/collect-into-array.md diff --git a/pkg/yqlib/doc/headers/comment-operators.md b/pkg/yqlib/doc/operators/headers/comment-operators.md similarity index 100% rename from pkg/yqlib/doc/headers/comment-operators.md rename to pkg/yqlib/doc/operators/headers/comment-operators.md diff --git a/pkg/yqlib/doc/headers/contains.md b/pkg/yqlib/doc/operators/headers/contains.md similarity index 100% rename from pkg/yqlib/doc/headers/contains.md rename to pkg/yqlib/doc/operators/headers/contains.md diff --git a/pkg/yqlib/doc/headers/create-collect-into-object.md b/pkg/yqlib/doc/operators/headers/create-collect-into-object.md similarity index 100% rename from pkg/yqlib/doc/headers/create-collect-into-object.md rename to pkg/yqlib/doc/operators/headers/create-collect-into-object.md diff --git a/pkg/yqlib/doc/headers/delete.md b/pkg/yqlib/doc/operators/headers/delete.md similarity index 100% rename from pkg/yqlib/doc/headers/delete.md rename to pkg/yqlib/doc/operators/headers/delete.md diff --git a/pkg/yqlib/doc/headers/document-index.md b/pkg/yqlib/doc/operators/headers/document-index.md similarity index 100% rename from pkg/yqlib/doc/headers/document-index.md rename to pkg/yqlib/doc/operators/headers/document-index.md diff --git a/pkg/yqlib/doc/headers/encode-decode.md b/pkg/yqlib/doc/operators/headers/encode-decode.md similarity index 97% rename from pkg/yqlib/doc/headers/encode-decode.md rename to pkg/yqlib/doc/operators/headers/encode-decode.md index 92ff9f5a..847240b3 100644 --- a/pkg/yqlib/doc/headers/encode-decode.md +++ b/pkg/yqlib/doc/operators/headers/encode-decode.md @@ -14,6 +14,7 @@ These operators are useful to process yaml documents that have stringified embed | Properties | | to_props/@props | | CSV | | to_csv/@csv | | TSV | | to_tsv/@tsv | +| XML | from_xml | | CSV and TSV format both accept either a single array or scalars (representing a single row), or an array of array of scalars (representing multiple rows). diff --git a/pkg/yqlib/doc/headers/entries.md b/pkg/yqlib/doc/operators/headers/entries.md similarity index 100% rename from pkg/yqlib/doc/headers/entries.md rename to pkg/yqlib/doc/operators/headers/entries.md diff --git a/pkg/yqlib/doc/headers/env-variable-operators.md b/pkg/yqlib/doc/operators/headers/env-variable-operators.md similarity index 100% rename from pkg/yqlib/doc/headers/env-variable-operators.md rename to pkg/yqlib/doc/operators/headers/env-variable-operators.md diff --git a/pkg/yqlib/doc/headers/equals.md b/pkg/yqlib/doc/operators/headers/equals.md similarity index 100% rename from pkg/yqlib/doc/headers/equals.md rename to pkg/yqlib/doc/operators/headers/equals.md diff --git a/pkg/yqlib/doc/headers/file-operators.md b/pkg/yqlib/doc/operators/headers/file-operators.md similarity index 100% rename from pkg/yqlib/doc/headers/file-operators.md rename to pkg/yqlib/doc/operators/headers/file-operators.md diff --git a/pkg/yqlib/doc/headers/flatten.md b/pkg/yqlib/doc/operators/headers/flatten.md similarity index 100% rename from pkg/yqlib/doc/headers/flatten.md rename to pkg/yqlib/doc/operators/headers/flatten.md diff --git a/pkg/yqlib/doc/headers/group-by.md b/pkg/yqlib/doc/operators/headers/group-by.md similarity index 100% rename from pkg/yqlib/doc/headers/group-by.md rename to pkg/yqlib/doc/operators/headers/group-by.md diff --git a/pkg/yqlib/doc/headers/has.md b/pkg/yqlib/doc/operators/headers/has.md similarity index 100% rename from pkg/yqlib/doc/headers/has.md rename to pkg/yqlib/doc/operators/headers/has.md diff --git a/pkg/yqlib/doc/headers/keys.md b/pkg/yqlib/doc/operators/headers/keys.md similarity index 100% rename from pkg/yqlib/doc/headers/keys.md rename to pkg/yqlib/doc/operators/headers/keys.md diff --git a/pkg/yqlib/doc/headers/length.md b/pkg/yqlib/doc/operators/headers/length.md similarity index 100% rename from pkg/yqlib/doc/headers/length.md rename to pkg/yqlib/doc/operators/headers/length.md diff --git a/pkg/yqlib/doc/headers/load.md b/pkg/yqlib/doc/operators/headers/load.md similarity index 100% rename from pkg/yqlib/doc/headers/load.md rename to pkg/yqlib/doc/operators/headers/load.md diff --git a/pkg/yqlib/doc/headers/map.md b/pkg/yqlib/doc/operators/headers/map.md similarity index 100% rename from pkg/yqlib/doc/headers/map.md rename to pkg/yqlib/doc/operators/headers/map.md diff --git a/pkg/yqlib/doc/headers/multiply-merge.md b/pkg/yqlib/doc/operators/headers/multiply-merge.md similarity index 100% rename from pkg/yqlib/doc/headers/multiply-merge.md rename to pkg/yqlib/doc/operators/headers/multiply-merge.md diff --git a/pkg/yqlib/doc/headers/parent.md b/pkg/yqlib/doc/operators/headers/parent.md similarity index 100% rename from pkg/yqlib/doc/headers/parent.md rename to pkg/yqlib/doc/operators/headers/parent.md diff --git a/pkg/yqlib/doc/headers/path.md b/pkg/yqlib/doc/operators/headers/path.md similarity index 100% rename from pkg/yqlib/doc/headers/path.md rename to pkg/yqlib/doc/operators/headers/path.md diff --git a/pkg/yqlib/doc/headers/pipe.md b/pkg/yqlib/doc/operators/headers/pipe.md similarity index 100% rename from pkg/yqlib/doc/headers/pipe.md rename to pkg/yqlib/doc/operators/headers/pipe.md diff --git a/pkg/yqlib/doc/headers/recursive-descent-glob.md b/pkg/yqlib/doc/operators/headers/recursive-descent-glob.md similarity index 100% rename from pkg/yqlib/doc/headers/recursive-descent-glob.md rename to pkg/yqlib/doc/operators/headers/recursive-descent-glob.md diff --git a/pkg/yqlib/doc/headers/reduce.md b/pkg/yqlib/doc/operators/headers/reduce.md similarity index 100% rename from pkg/yqlib/doc/headers/reduce.md rename to pkg/yqlib/doc/operators/headers/reduce.md diff --git a/pkg/yqlib/doc/headers/select.md b/pkg/yqlib/doc/operators/headers/select.md similarity index 100% rename from pkg/yqlib/doc/headers/select.md rename to pkg/yqlib/doc/operators/headers/select.md diff --git a/pkg/yqlib/doc/headers/sort-keys.md b/pkg/yqlib/doc/operators/headers/sort-keys.md similarity index 100% rename from pkg/yqlib/doc/headers/sort-keys.md rename to pkg/yqlib/doc/operators/headers/sort-keys.md diff --git a/pkg/yqlib/doc/headers/sort.md b/pkg/yqlib/doc/operators/headers/sort.md similarity index 100% rename from pkg/yqlib/doc/headers/sort.md rename to pkg/yqlib/doc/operators/headers/sort.md diff --git a/pkg/yqlib/doc/headers/split-into-documents.md b/pkg/yqlib/doc/operators/headers/split-into-documents.md similarity index 100% rename from pkg/yqlib/doc/headers/split-into-documents.md rename to pkg/yqlib/doc/operators/headers/split-into-documents.md diff --git a/pkg/yqlib/doc/headers/string-operators.md b/pkg/yqlib/doc/operators/headers/string-operators.md similarity index 100% rename from pkg/yqlib/doc/headers/string-operators.md rename to pkg/yqlib/doc/operators/headers/string-operators.md diff --git a/pkg/yqlib/doc/headers/style.md b/pkg/yqlib/doc/operators/headers/style.md similarity index 100% rename from pkg/yqlib/doc/headers/style.md rename to pkg/yqlib/doc/operators/headers/style.md diff --git a/pkg/yqlib/doc/headers/subtract.md b/pkg/yqlib/doc/operators/headers/subtract.md similarity index 100% rename from pkg/yqlib/doc/headers/subtract.md rename to pkg/yqlib/doc/operators/headers/subtract.md diff --git a/pkg/yqlib/doc/headers/tag.md b/pkg/yqlib/doc/operators/headers/tag.md similarity index 100% rename from pkg/yqlib/doc/headers/tag.md rename to pkg/yqlib/doc/operators/headers/tag.md diff --git a/pkg/yqlib/doc/headers/traverse-read.md b/pkg/yqlib/doc/operators/headers/traverse-read.md similarity index 100% rename from pkg/yqlib/doc/headers/traverse-read.md rename to pkg/yqlib/doc/operators/headers/traverse-read.md diff --git a/pkg/yqlib/doc/headers/union.md b/pkg/yqlib/doc/operators/headers/union.md similarity index 100% rename from pkg/yqlib/doc/headers/union.md rename to pkg/yqlib/doc/operators/headers/union.md diff --git a/pkg/yqlib/doc/headers/unique.md b/pkg/yqlib/doc/operators/headers/unique.md similarity index 100% rename from pkg/yqlib/doc/headers/unique.md rename to pkg/yqlib/doc/operators/headers/unique.md diff --git a/pkg/yqlib/doc/headers/variable-operators.md b/pkg/yqlib/doc/operators/headers/variable-operators.md similarity index 100% rename from pkg/yqlib/doc/headers/variable-operators.md rename to pkg/yqlib/doc/operators/headers/variable-operators.md diff --git a/pkg/yqlib/doc/headers/with.md b/pkg/yqlib/doc/operators/headers/with.md similarity index 100% rename from pkg/yqlib/doc/headers/with.md rename to pkg/yqlib/doc/operators/headers/with.md diff --git a/pkg/yqlib/doc/keys.md b/pkg/yqlib/doc/operators/keys.md similarity index 100% rename from pkg/yqlib/doc/keys.md rename to pkg/yqlib/doc/operators/keys.md diff --git a/pkg/yqlib/doc/length.md b/pkg/yqlib/doc/operators/length.md similarity index 100% rename from pkg/yqlib/doc/length.md rename to pkg/yqlib/doc/operators/length.md diff --git a/pkg/yqlib/doc/load.md b/pkg/yqlib/doc/operators/load.md similarity index 100% rename from pkg/yqlib/doc/load.md rename to pkg/yqlib/doc/operators/load.md diff --git a/pkg/yqlib/doc/map.md b/pkg/yqlib/doc/operators/map.md similarity index 100% rename from pkg/yqlib/doc/map.md rename to pkg/yqlib/doc/operators/map.md diff --git a/pkg/yqlib/doc/multiply-merge.md b/pkg/yqlib/doc/operators/multiply-merge.md similarity index 100% rename from pkg/yqlib/doc/multiply-merge.md rename to pkg/yqlib/doc/operators/multiply-merge.md diff --git a/pkg/yqlib/doc/parent.md b/pkg/yqlib/doc/operators/parent.md similarity index 100% rename from pkg/yqlib/doc/parent.md rename to pkg/yqlib/doc/operators/parent.md diff --git a/pkg/yqlib/doc/path.md b/pkg/yqlib/doc/operators/path.md similarity index 100% rename from pkg/yqlib/doc/path.md rename to pkg/yqlib/doc/operators/path.md diff --git a/pkg/yqlib/doc/pipe.md b/pkg/yqlib/doc/operators/pipe.md similarity index 100% rename from pkg/yqlib/doc/pipe.md rename to pkg/yqlib/doc/operators/pipe.md diff --git a/pkg/yqlib/doc/recursive-descent-glob.md b/pkg/yqlib/doc/operators/recursive-descent-glob.md similarity index 100% rename from pkg/yqlib/doc/recursive-descent-glob.md rename to pkg/yqlib/doc/operators/recursive-descent-glob.md diff --git a/pkg/yqlib/doc/reduce.md b/pkg/yqlib/doc/operators/reduce.md similarity index 100% rename from pkg/yqlib/doc/reduce.md rename to pkg/yqlib/doc/operators/reduce.md diff --git a/pkg/yqlib/doc/select.md b/pkg/yqlib/doc/operators/select.md similarity index 100% rename from pkg/yqlib/doc/select.md rename to pkg/yqlib/doc/operators/select.md diff --git a/pkg/yqlib/doc/sort-keys.md b/pkg/yqlib/doc/operators/sort-keys.md similarity index 100% rename from pkg/yqlib/doc/sort-keys.md rename to pkg/yqlib/doc/operators/sort-keys.md diff --git a/pkg/yqlib/doc/sort.md b/pkg/yqlib/doc/operators/sort.md similarity index 100% rename from pkg/yqlib/doc/sort.md rename to pkg/yqlib/doc/operators/sort.md diff --git a/pkg/yqlib/doc/split-into-documents.md b/pkg/yqlib/doc/operators/split-into-documents.md similarity index 100% rename from pkg/yqlib/doc/split-into-documents.md rename to pkg/yqlib/doc/operators/split-into-documents.md diff --git a/pkg/yqlib/doc/string-operators.md b/pkg/yqlib/doc/operators/string-operators.md similarity index 100% rename from pkg/yqlib/doc/string-operators.md rename to pkg/yqlib/doc/operators/string-operators.md diff --git a/pkg/yqlib/doc/style.md b/pkg/yqlib/doc/operators/style.md similarity index 100% rename from pkg/yqlib/doc/style.md rename to pkg/yqlib/doc/operators/style.md diff --git a/pkg/yqlib/doc/subtract.md b/pkg/yqlib/doc/operators/subtract.md similarity index 100% rename from pkg/yqlib/doc/subtract.md rename to pkg/yqlib/doc/operators/subtract.md diff --git a/pkg/yqlib/doc/tag.md b/pkg/yqlib/doc/operators/tag.md similarity index 100% rename from pkg/yqlib/doc/tag.md rename to pkg/yqlib/doc/operators/tag.md diff --git a/pkg/yqlib/doc/traverse-read.md b/pkg/yqlib/doc/operators/traverse-read.md similarity index 100% rename from pkg/yqlib/doc/traverse-read.md rename to pkg/yqlib/doc/operators/traverse-read.md diff --git a/pkg/yqlib/doc/union.md b/pkg/yqlib/doc/operators/union.md similarity index 100% rename from pkg/yqlib/doc/union.md rename to pkg/yqlib/doc/operators/union.md diff --git a/pkg/yqlib/doc/unique.md b/pkg/yqlib/doc/operators/unique.md similarity index 100% rename from pkg/yqlib/doc/unique.md rename to pkg/yqlib/doc/operators/unique.md diff --git a/pkg/yqlib/doc/variable-operators.md b/pkg/yqlib/doc/operators/variable-operators.md similarity index 100% rename from pkg/yqlib/doc/variable-operators.md rename to pkg/yqlib/doc/operators/variable-operators.md diff --git a/pkg/yqlib/doc/with.md b/pkg/yqlib/doc/operators/with.md similarity index 100% rename from pkg/yqlib/doc/with.md rename to pkg/yqlib/doc/operators/with.md diff --git a/pkg/yqlib/doc/usage/headers/xml.md b/pkg/yqlib/doc/usage/headers/xml.md new file mode 100644 index 00000000..9258afd0 --- /dev/null +++ b/pkg/yqlib/doc/usage/headers/xml.md @@ -0,0 +1,12 @@ +# XML + +At the moment, `yq` only supports decoding `xml` (into one of the other supported output formats). + +As yaml does not have the concept of attributes, these are converted to regular fields with a prefix to prevent clobbering. Consecutive xml nodes with the same name are assumed to be arrays. + +All values in XML are assumed to be strings - but you can use `from_yaml` to parse them into their correct types: + + +``` +yq e -p=xml '.myNumberField |= from_yaml' my.xml +``` diff --git a/pkg/yqlib/doc/usage/xml.md b/pkg/yqlib/doc/usage/xml.md new file mode 100644 index 00000000..e084149e --- /dev/null +++ b/pkg/yqlib/doc/usage/xml.md @@ -0,0 +1,88 @@ +# XML + +At the moment, `yq` only supports decoding `xml` (into one of the other supported output formats). + +As yaml does not have the concept of attributes, these are converted to regular fields with a prefix to prevent clobbering. Consecutive xml nodes with the same name are assumed to be arrays. + +All values in XML are assumed to be strings - but you can use `from_yaml` to parse them into their correct types: + + +``` +yq e -p=xml '.myNumberField |= from_yaml' my.xml +``` + +## Parse xml: simple +Given a sample.xml file of: +```xml + +meow +``` +then +```bash +yq e sample.xml +``` +will output +```yaml +cat: meow +``` + +## Parse xml: array +Consecutive nodes with identical xml names are assumed to be arrays. + +Given a sample.xml file of: +```xml + +1 +2 +``` +then +```bash +yq e sample.xml +``` +will output +```yaml +animal: + - "1" + - "2" +``` + +## Parse xml: attributes +Attributes are converted to fields, with the attribute prefix. + +Given a sample.xml file of: +```xml + + + 7 + +``` +then +```bash +yq e sample.xml +``` +will output +```yaml +cat: + +legs: "4" + legs: "7" +``` + +## Parse xml: attributes with content +Content is added as a field, using the content name + +Given a sample.xml file of: +```xml + +meow +``` +then +```bash +yq e sample.xml +``` +will output +```yaml +cat: + +content: meow + +legs: "4" +``` + diff --git a/pkg/yqlib/encoder_csv_test.go b/pkg/yqlib/encoder_csv_test.go index 1db2859e..816cbfe4 100644 --- a/pkg/yqlib/encoder_csv_test.go +++ b/pkg/yqlib/encoder_csv_test.go @@ -14,7 +14,7 @@ func yamlToCsv(sampleYaml string, separator rune) string { writer := bufio.NewWriter(&output) var jsonEncoder = NewCsvEncoder(writer, separator) - inputs, err := readDocuments(strings.NewReader(sampleYaml), "sample.yml", 0) + inputs, err := readDocuments(strings.NewReader(sampleYaml), "sample.yml", 0, NewYamlDecoder()) if err != nil { panic(err) } diff --git a/pkg/yqlib/encoder_properties_test.go b/pkg/yqlib/encoder_properties_test.go index df973734..f82af12a 100644 --- a/pkg/yqlib/encoder_properties_test.go +++ b/pkg/yqlib/encoder_properties_test.go @@ -14,7 +14,7 @@ func yamlToProps(sampleYaml string) string { writer := bufio.NewWriter(&output) var propsEncoder = NewPropertiesEncoder(writer) - inputs, err := readDocuments(strings.NewReader(sampleYaml), "sample.yml", 0) + inputs, err := readDocuments(strings.NewReader(sampleYaml), "sample.yml", 0, NewYamlDecoder()) if err != nil { panic(err) } diff --git a/pkg/yqlib/encoder_test.go b/pkg/yqlib/encoder_test.go index 283e89ee..576cfb95 100644 --- a/pkg/yqlib/encoder_test.go +++ b/pkg/yqlib/encoder_test.go @@ -14,7 +14,7 @@ func yamlToJson(sampleYaml string, indent int) string { writer := bufio.NewWriter(&output) var jsonEncoder = NewJsonEncoder(writer, indent) - inputs, err := readDocuments(strings.NewReader(sampleYaml), "sample.yml", 0) + inputs, err := readDocuments(strings.NewReader(sampleYaml), "sample.yml", 0, NewYamlDecoder()) if err != nil { panic(err) } diff --git a/pkg/yqlib/expression_tokeniser.go b/pkg/yqlib/expression_tokeniser.go index 0fb05642..d812039c 100644 --- a/pkg/yqlib/expression_tokeniser.go +++ b/pkg/yqlib/expression_tokeniser.go @@ -326,36 +326,47 @@ func initLexer() (*lex.Lexer, error) { lexer.Add([]byte(`to_json\([0-9]+\)`), encodeWithIndent(JsonOutputFormat)) lexer.Add([]byte(`toyaml`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: YamlOutputFormat, indent: 2})) + lexer.Add([]byte(`to_yaml`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: YamlOutputFormat, indent: 2})) // 0 indent doesn't work with yaml. lexer.Add([]byte(`@yaml`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: YamlOutputFormat, indent: 2})) lexer.Add([]byte(`tojson`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: JsonOutputFormat, indent: 2})) - lexer.Add([]byte(`toprops`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: PropsOutputFormat, indent: 2})) - lexer.Add([]byte(`@props`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: PropsOutputFormat, indent: 2})) - - lexer.Add([]byte(`to_yaml`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: YamlOutputFormat, indent: 2})) lexer.Add([]byte(`to_json`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: JsonOutputFormat, indent: 2})) lexer.Add([]byte(`@json`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: JsonOutputFormat, indent: 0})) + lexer.Add([]byte(`toprops`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: PropsOutputFormat, indent: 2})) + lexer.Add([]byte(`to_props`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: PropsOutputFormat, indent: 2})) + lexer.Add([]byte(`@props`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: PropsOutputFormat, indent: 2})) + + lexer.Add([]byte(`tocsv`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: CsvOutputFormat})) lexer.Add([]byte(`to_csv`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: CsvOutputFormat})) lexer.Add([]byte(`@csv`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: CsvOutputFormat})) + lexer.Add([]byte(`totsv`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: TsvOutputFormat})) lexer.Add([]byte(`to_tsv`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: TsvOutputFormat})) lexer.Add([]byte(`@tsv`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: TsvOutputFormat})) - lexer.Add([]byte(`to_props`), opTokenWithPrefs(encodeOpType, nil, encoderPreferences{format: PropsOutputFormat, indent: 2})) + lexer.Add([]byte(`fromyaml`), opTokenWithPrefs(decodeOpType, nil, decoderPreferences{format: YamlInputFormat})) + lexer.Add([]byte(`fromjson`), opTokenWithPrefs(decodeOpType, nil, decoderPreferences{format: YamlInputFormat})) + lexer.Add([]byte(`fromxml`), opTokenWithPrefs(decodeOpType, nil, decoderPreferences{format: XmlInputFormat})) - lexer.Add([]byte(`fromyaml`), opToken(decodeOpType)) - lexer.Add([]byte(`fromjson`), opToken(decodeOpType)) - - lexer.Add([]byte(`from_yaml`), opToken(decodeOpType)) - lexer.Add([]byte(`from_json`), opToken(decodeOpType)) + lexer.Add([]byte(`from_yaml`), opTokenWithPrefs(decodeOpType, nil, decoderPreferences{format: YamlInputFormat})) + lexer.Add([]byte(`from_json`), opTokenWithPrefs(decodeOpType, nil, decoderPreferences{format: YamlInputFormat})) + lexer.Add([]byte(`from_xml`), opTokenWithPrefs(decodeOpType, nil, decoderPreferences{format: XmlInputFormat})) lexer.Add([]byte(`sortKeys`), opToken(sortKeysOpType)) lexer.Add([]byte(`sort_keys`), opToken(sortKeysOpType)) - lexer.Add([]byte(`load`), opTokenWithPrefs(loadOpType, nil, loadPrefs{loadAsString: false})) + lexer.Add([]byte(`load`), opTokenWithPrefs(loadOpType, nil, loadPrefs{loadAsString: false, decoder: NewYamlDecoder()})) + + lexer.Add([]byte(`xmlload`), opTokenWithPrefs(loadOpType, nil, loadPrefs{loadAsString: false, decoder: NewXmlDecoder("+", "+content")})) + lexer.Add([]byte(`load_xml`), opTokenWithPrefs(loadOpType, nil, loadPrefs{loadAsString: false, decoder: NewXmlDecoder("+", "+content")})) + lexer.Add([]byte(`loadxml`), opTokenWithPrefs(loadOpType, nil, loadPrefs{loadAsString: false, decoder: NewXmlDecoder("+", "+content")})) + lexer.Add([]byte(`strload`), opTokenWithPrefs(loadOpType, nil, loadPrefs{loadAsString: true})) + lexer.Add([]byte(`load_str`), opTokenWithPrefs(loadOpType, nil, loadPrefs{loadAsString: true})) + lexer.Add([]byte(`loadstr`), opTokenWithPrefs(loadOpType, nil, loadPrefs{loadAsString: true})) + lexer.Add([]byte(`select`), opToken(selectOpType)) lexer.Add([]byte(`has`), opToken(hasOpType)) lexer.Add([]byte(`unique`), opToken(uniqueOpType)) diff --git a/pkg/yqlib/operator_add_test.go b/pkg/yqlib/operator_add_test.go index 445977e8..b303866a 100644 --- a/pkg/yqlib/operator_add_test.go +++ b/pkg/yqlib/operator_add_test.go @@ -150,5 +150,5 @@ func TestAddOperatorScenarios(t *testing.T) { for _, tt := range addOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "add", addOperatorScenarios) + documentOperatorScenarios(t, "add", addOperatorScenarios) } diff --git a/pkg/yqlib/operator_alternative_test.go b/pkg/yqlib/operator_alternative_test.go index 0331213d..e23c8dbb 100644 --- a/pkg/yqlib/operator_alternative_test.go +++ b/pkg/yqlib/operator_alternative_test.go @@ -91,5 +91,5 @@ func TestAlternativeOperatorScenarios(t *testing.T) { for _, tt := range alternativeOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "alternative-default-value", alternativeOperatorScenarios) + documentOperatorScenarios(t, "alternative-default-value", alternativeOperatorScenarios) } diff --git a/pkg/yqlib/operator_anchors_aliases_test.go b/pkg/yqlib/operator_anchors_aliases_test.go index 30642dcd..9222c177 100644 --- a/pkg/yqlib/operator_anchors_aliases_test.go +++ b/pkg/yqlib/operator_anchors_aliases_test.go @@ -233,5 +233,5 @@ func TestAnchorAliasOperatorScenarios(t *testing.T) { for _, tt := range anchorOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "anchor-and-alias-operators", anchorOperatorScenarios) + documentOperatorScenarios(t, "anchor-and-alias-operators", anchorOperatorScenarios) } diff --git a/pkg/yqlib/operator_assign_test.go b/pkg/yqlib/operator_assign_test.go index 9ecca5d4..b5f6813c 100644 --- a/pkg/yqlib/operator_assign_test.go +++ b/pkg/yqlib/operator_assign_test.go @@ -178,5 +178,5 @@ func TestAssignOperatorScenarios(t *testing.T) { for _, tt := range assignOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "assign-update", assignOperatorScenarios) + documentOperatorScenarios(t, "assign-update", assignOperatorScenarios) } diff --git a/pkg/yqlib/operator_booleans_test.go b/pkg/yqlib/operator_booleans_test.go index ac6de3eb..e9b72f99 100644 --- a/pkg/yqlib/operator_booleans_test.go +++ b/pkg/yqlib/operator_booleans_test.go @@ -229,5 +229,5 @@ func TestBooleanOperatorScenarios(t *testing.T) { for _, tt := range booleanOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "boolean-operators", booleanOperatorScenarios) + documentOperatorScenarios(t, "boolean-operators", booleanOperatorScenarios) } diff --git a/pkg/yqlib/operator_collect_object_test.go b/pkg/yqlib/operator_collect_object_test.go index edbe63d2..4db923f7 100644 --- a/pkg/yqlib/operator_collect_object_test.go +++ b/pkg/yqlib/operator_collect_object_test.go @@ -160,5 +160,5 @@ func TestCollectObjectOperatorScenarios(t *testing.T) { for _, tt := range collectObjectOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "create-collect-into-object", collectObjectOperatorScenarios) + documentOperatorScenarios(t, "create-collect-into-object", collectObjectOperatorScenarios) } diff --git a/pkg/yqlib/operator_collect_test.go b/pkg/yqlib/operator_collect_test.go index 0e23e340..fce899fb 100644 --- a/pkg/yqlib/operator_collect_test.go +++ b/pkg/yqlib/operator_collect_test.go @@ -108,5 +108,5 @@ func TestCollectOperatorScenarios(t *testing.T) { for _, tt := range collectOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "collect-into-array", collectOperatorScenarios) + documentOperatorScenarios(t, "collect-into-array", collectOperatorScenarios) } diff --git a/pkg/yqlib/operator_comments_test.go b/pkg/yqlib/operator_comments_test.go index cc3ce4fe..223eeb6c 100644 --- a/pkg/yqlib/operator_comments_test.go +++ b/pkg/yqlib/operator_comments_test.go @@ -136,5 +136,5 @@ func TestCommentOperatorScenarios(t *testing.T) { for _, tt := range commentOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "comment-operators", commentOperatorScenarios) + documentOperatorScenarios(t, "comment-operators", commentOperatorScenarios) } diff --git a/pkg/yqlib/operator_contains_test.go b/pkg/yqlib/operator_contains_test.go index e1d3dd01..5cef9d7c 100644 --- a/pkg/yqlib/operator_contains_test.go +++ b/pkg/yqlib/operator_contains_test.go @@ -78,5 +78,5 @@ func TestContainsOperatorScenarios(t *testing.T) { for _, tt := range containsOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "contains", containsOperatorScenarios) + documentOperatorScenarios(t, "contains", containsOperatorScenarios) } diff --git a/pkg/yqlib/operator_delete_test.go b/pkg/yqlib/operator_delete_test.go index 02efcb75..76d95224 100644 --- a/pkg/yqlib/operator_delete_test.go +++ b/pkg/yqlib/operator_delete_test.go @@ -171,5 +171,5 @@ func TestDeleteOperatorScenarios(t *testing.T) { for _, tt := range deleteOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "delete", deleteOperatorScenarios) + documentOperatorScenarios(t, "delete", deleteOperatorScenarios) } diff --git a/pkg/yqlib/operator_document_index_test.go b/pkg/yqlib/operator_document_index_test.go index 1e35cd19..23195505 100644 --- a/pkg/yqlib/operator_document_index_test.go +++ b/pkg/yqlib/operator_document_index_test.go @@ -54,5 +54,5 @@ func TestDocumentIndexScenarios(t *testing.T) { for _, tt := range documentIndexScenarios { testScenario(t, &tt) } - documentScenarios(t, "document-index", documentIndexScenarios) + documentOperatorScenarios(t, "document-index", documentIndexScenarios) } diff --git a/pkg/yqlib/operator_encoder_decoder.go b/pkg/yqlib/operator_encoder_decoder.go index d4d61b3f..a702d0f1 100644 --- a/pkg/yqlib/operator_encoder_decoder.go +++ b/pkg/yqlib/operator_encoder_decoder.go @@ -68,9 +68,23 @@ func encodeOperator(d *dataTreeNavigator, context Context, expressionNode *Expre return context.ChildContext(results), nil } +type decoderPreferences struct { + format InputFormat +} + /* takes a string and decodes it back into an object */ func decodeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { + preferences := expressionNode.Operation.Preferences.(decoderPreferences) + + var decoder Decoder + switch preferences.format { + case YamlInputFormat: + decoder = NewYamlDecoder() + case XmlInputFormat: + decoder = NewXmlDecoder("+a", "+content") + } + var results = list.New() for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) @@ -79,7 +93,9 @@ func decodeOperator(d *dataTreeNavigator, context Context, expressionNode *Expre var dataBucket yaml.Node log.Debugf("got: [%v]", candidate.Node.Value) - decoder := yaml.NewDecoder(strings.NewReader(unwrapDoc(candidate.Node).Value)) + + decoder.Init(strings.NewReader(unwrapDoc(candidate.Node).Value)) + errorReading := decoder.Decode(&dataBucket) if errorReading != nil { return Context{}, errorReading diff --git a/pkg/yqlib/operator_encoder_decoder_test.go b/pkg/yqlib/operator_encoder_decoder_test.go index c7597533..1c394356 100644 --- a/pkg/yqlib/operator_encoder_decoder_test.go +++ b/pkg/yqlib/operator_encoder_decoder_test.go @@ -168,11 +168,19 @@ var encoderDecoderOperatorScenarios = []expressionScenario{ "D0, P[], (doc)::a: \"foo:\\n a: frog\"\n", }, }, + { + description: "Decode a xml encoded string", + document: `a: "bar"`, + expression: `.b = (.a | from_xml)`, + expected: []string{ + "D0, P[], (doc)::a: \"bar\"\nb:\n foo: bar\n", + }, + }, } func TestEncoderDecoderOperatorScenarios(t *testing.T) { for _, tt := range encoderDecoderOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "encode-decode", encoderDecoderOperatorScenarios) + documentOperatorScenarios(t, "encode-decode", encoderDecoderOperatorScenarios) } diff --git a/pkg/yqlib/operator_entries_test.go b/pkg/yqlib/operator_entries_test.go index 4bee227d..1dd120c3 100644 --- a/pkg/yqlib/operator_entries_test.go +++ b/pkg/yqlib/operator_entries_test.go @@ -76,5 +76,5 @@ func TestEntriesOperatorScenarios(t *testing.T) { for _, tt := range entriesOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "entries", entriesOperatorScenarios) + documentOperatorScenarios(t, "entries", entriesOperatorScenarios) } diff --git a/pkg/yqlib/operator_env_test.go b/pkg/yqlib/operator_env_test.go index 1cc1ba67..eac19734 100644 --- a/pkg/yqlib/operator_env_test.go +++ b/pkg/yqlib/operator_env_test.go @@ -68,5 +68,5 @@ func TestEnvOperatorScenarios(t *testing.T) { for _, tt := range envOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "env-variable-operators", envOperatorScenarios) + documentOperatorScenarios(t, "env-variable-operators", envOperatorScenarios) } diff --git a/pkg/yqlib/operator_equals_test.go b/pkg/yqlib/operator_equals_test.go index 69cd6e09..517dae36 100644 --- a/pkg/yqlib/operator_equals_test.go +++ b/pkg/yqlib/operator_equals_test.go @@ -186,5 +186,5 @@ func TestEqualOperatorScenarios(t *testing.T) { for _, tt := range equalsOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "equals", equalsOperatorScenarios) + documentOperatorScenarios(t, "equals", equalsOperatorScenarios) } diff --git a/pkg/yqlib/operator_file_test.go b/pkg/yqlib/operator_file_test.go index 88d1422f..875d4187 100644 --- a/pkg/yqlib/operator_file_test.go +++ b/pkg/yqlib/operator_file_test.go @@ -53,5 +53,5 @@ func TestFileOperatorsScenarios(t *testing.T) { for _, tt := range fileOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "file-operators", fileOperatorScenarios) + documentOperatorScenarios(t, "file-operators", fileOperatorScenarios) } diff --git a/pkg/yqlib/operator_flatten_test.go b/pkg/yqlib/operator_flatten_test.go index 4e826471..add71e1f 100644 --- a/pkg/yqlib/operator_flatten_test.go +++ b/pkg/yqlib/operator_flatten_test.go @@ -44,5 +44,5 @@ func TestFlattenOperatorScenarios(t *testing.T) { for _, tt := range flattenOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "flatten", flattenOperatorScenarios) + documentOperatorScenarios(t, "flatten", flattenOperatorScenarios) } diff --git a/pkg/yqlib/operator_group_by_test.go b/pkg/yqlib/operator_group_by_test.go index 60462206..2c7803da 100644 --- a/pkg/yqlib/operator_group_by_test.go +++ b/pkg/yqlib/operator_group_by_test.go @@ -27,5 +27,5 @@ func TestGroupByOperatorScenarios(t *testing.T) { for _, tt := range groupByOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "group-by", groupByOperatorScenarios) + documentOperatorScenarios(t, "group-by", groupByOperatorScenarios) } diff --git a/pkg/yqlib/operator_has_test.go b/pkg/yqlib/operator_has_test.go index fbd31d5b..3a631330 100644 --- a/pkg/yqlib/operator_has_test.go +++ b/pkg/yqlib/operator_has_test.go @@ -77,5 +77,5 @@ func TestHasOperatorScenarios(t *testing.T) { for _, tt := range hasOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "has", hasOperatorScenarios) + documentOperatorScenarios(t, "has", hasOperatorScenarios) } diff --git a/pkg/yqlib/operator_keys_test.go b/pkg/yqlib/operator_keys_test.go index bd5087fb..b42306f7 100644 --- a/pkg/yqlib/operator_keys_test.go +++ b/pkg/yqlib/operator_keys_test.go @@ -81,5 +81,5 @@ func TestKeysOperatorScenarios(t *testing.T) { for _, tt := range keysOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "keys", keysOperatorScenarios) + documentOperatorScenarios(t, "keys", keysOperatorScenarios) } diff --git a/pkg/yqlib/operator_length_test.go b/pkg/yqlib/operator_length_test.go index d08c1326..a67e627d 100644 --- a/pkg/yqlib/operator_length_test.go +++ b/pkg/yqlib/operator_length_test.go @@ -62,5 +62,5 @@ func TestLengthOperatorScenarios(t *testing.T) { for _, tt := range lengthOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "length", lengthOperatorScenarios) + documentOperatorScenarios(t, "length", lengthOperatorScenarios) } diff --git a/pkg/yqlib/operator_load.go b/pkg/yqlib/operator_load.go index 4741b09a..426b8f25 100644 --- a/pkg/yqlib/operator_load.go +++ b/pkg/yqlib/operator_load.go @@ -12,6 +12,7 @@ import ( type loadPrefs struct { loadAsString bool + decoder Decoder } func loadString(filename string) (*CandidateNode, error) { @@ -26,7 +27,7 @@ func loadString(filename string) (*CandidateNode, error) { return &CandidateNode{Node: &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: string(filebytes)}}, nil } -func loadYaml(filename string) (*CandidateNode, error) { +func loadYaml(filename string, decoder Decoder) (*CandidateNode, error) { file, err := os.Open(filename) // #nosec if err != nil { @@ -34,7 +35,7 @@ func loadYaml(filename string) (*CandidateNode, error) { } reader := bufio.NewReader(file) - documents, err := readDocuments(reader, filename, 0) + documents, err := readDocuments(reader, filename, 0, decoder) if err != nil { return nil, err } @@ -83,7 +84,7 @@ func loadYamlOperator(d *dataTreeNavigator, context Context, expressionNode *Exp if loadPrefs.loadAsString { contentsCandidate, err = loadString(filename) } else { - contentsCandidate, err = loadYaml(filename) + contentsCandidate, err = loadYaml(filename, loadPrefs.decoder) } if err != nil { return Context{}, fmt.Errorf("Failed to load %v: %w", filename, err) diff --git a/pkg/yqlib/operator_load_test.go b/pkg/yqlib/operator_load_test.go index 4fe690f1..5cfae4ad 100644 --- a/pkg/yqlib/operator_load_test.go +++ b/pkg/yqlib/operator_load_test.go @@ -46,5 +46,5 @@ func TestLoadScenarios(t *testing.T) { for _, tt := range loadScenarios { testScenario(t, &tt) } - documentScenarios(t, "load", loadScenarios) + documentOperatorScenarios(t, "load", loadScenarios) } diff --git a/pkg/yqlib/operator_map_test.go b/pkg/yqlib/operator_map_test.go index 1dd5db57..715df336 100644 --- a/pkg/yqlib/operator_map_test.go +++ b/pkg/yqlib/operator_map_test.go @@ -47,5 +47,5 @@ func TestMapOperatorScenarios(t *testing.T) { for _, tt := range mapOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "map", mapOperatorScenarios) + documentOperatorScenarios(t, "map", mapOperatorScenarios) } diff --git a/pkg/yqlib/operator_multiply_test.go b/pkg/yqlib/operator_multiply_test.go index 59a08849..ab136f13 100644 --- a/pkg/yqlib/operator_multiply_test.go +++ b/pkg/yqlib/operator_multiply_test.go @@ -441,5 +441,5 @@ func TestMultiplyOperatorScenarios(t *testing.T) { for _, tt := range multiplyOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "multiply-merge", multiplyOperatorScenarios) + documentOperatorScenarios(t, "multiply-merge", multiplyOperatorScenarios) } diff --git a/pkg/yqlib/operator_parent_test.go b/pkg/yqlib/operator_parent_test.go index f4cbf236..35afd67e 100644 --- a/pkg/yqlib/operator_parent_test.go +++ b/pkg/yqlib/operator_parent_test.go @@ -33,5 +33,5 @@ func TestParentOperatorScenarios(t *testing.T) { for _, tt := range parentOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "parent", parentOperatorScenarios) + documentOperatorScenarios(t, "parent", parentOperatorScenarios) } diff --git a/pkg/yqlib/operator_path_test.go b/pkg/yqlib/operator_path_test.go index a93830a2..faf06ce9 100644 --- a/pkg/yqlib/operator_path_test.go +++ b/pkg/yqlib/operator_path_test.go @@ -69,5 +69,5 @@ func TestPathOperatorsScenarios(t *testing.T) { for _, tt := range pathOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "path", pathOperatorScenarios) + documentOperatorScenarios(t, "path", pathOperatorScenarios) } diff --git a/pkg/yqlib/operator_pipe_test.go b/pkg/yqlib/operator_pipe_test.go index fc36cde9..9e979603 100644 --- a/pkg/yqlib/operator_pipe_test.go +++ b/pkg/yqlib/operator_pipe_test.go @@ -27,5 +27,5 @@ func TestPipeOperatorScenarios(t *testing.T) { for _, tt := range pipeOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "pipe", pipeOperatorScenarios) + documentOperatorScenarios(t, "pipe", pipeOperatorScenarios) } diff --git a/pkg/yqlib/operator_recursive_descent_test.go b/pkg/yqlib/operator_recursive_descent_test.go index 8d4ddc7e..45e63cfd 100644 --- a/pkg/yqlib/operator_recursive_descent_test.go +++ b/pkg/yqlib/operator_recursive_descent_test.go @@ -233,5 +233,5 @@ func TestRecursiveDescentOperatorScenarios(t *testing.T) { for _, tt := range recursiveDescentOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "recursive-descent-glob", recursiveDescentOperatorScenarios) + documentOperatorScenarios(t, "recursive-descent-glob", recursiveDescentOperatorScenarios) } diff --git a/pkg/yqlib/operator_reduce_test.go b/pkg/yqlib/operator_reduce_test.go index dde2c33f..80b45a56 100644 --- a/pkg/yqlib/operator_reduce_test.go +++ b/pkg/yqlib/operator_reduce_test.go @@ -36,5 +36,5 @@ func TestReduceOperatorScenarios(t *testing.T) { for _, tt := range reduceOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "reduce", reduceOperatorScenarios) + documentOperatorScenarios(t, "reduce", reduceOperatorScenarios) } diff --git a/pkg/yqlib/operator_select_test.go b/pkg/yqlib/operator_select_test.go index 4a1e8723..43c16a9b 100644 --- a/pkg/yqlib/operator_select_test.go +++ b/pkg/yqlib/operator_select_test.go @@ -99,5 +99,5 @@ func TestSelectOperatorScenarios(t *testing.T) { for _, tt := range selectOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "select", selectOperatorScenarios) + documentOperatorScenarios(t, "select", selectOperatorScenarios) } diff --git a/pkg/yqlib/operator_sort_keys_test.go b/pkg/yqlib/operator_sort_keys_test.go index 462771e6..8d421556 100644 --- a/pkg/yqlib/operator_sort_keys_test.go +++ b/pkg/yqlib/operator_sort_keys_test.go @@ -36,5 +36,5 @@ func TestSortKeysOperatorScenarios(t *testing.T) { for _, tt := range sortKeysOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "sort-keys", sortKeysOperatorScenarios) + documentOperatorScenarios(t, "sort-keys", sortKeysOperatorScenarios) } diff --git a/pkg/yqlib/operator_sort_test.go b/pkg/yqlib/operator_sort_test.go index 6828d73e..7c4128d6 100644 --- a/pkg/yqlib/operator_sort_test.go +++ b/pkg/yqlib/operator_sort_test.go @@ -76,5 +76,5 @@ func TestSortByOperatorScenarios(t *testing.T) { for _, tt := range sortByOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "sort", sortByOperatorScenarios) + documentOperatorScenarios(t, "sort", sortByOperatorScenarios) } diff --git a/pkg/yqlib/operator_split_document_test.go b/pkg/yqlib/operator_split_document_test.go index 813f3173..ca490512 100644 --- a/pkg/yqlib/operator_split_document_test.go +++ b/pkg/yqlib/operator_split_document_test.go @@ -28,5 +28,5 @@ func TestSplitDocOperatorScenarios(t *testing.T) { for _, tt := range splitDocOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "split-into-documents", splitDocOperatorScenarios) + documentOperatorScenarios(t, "split-into-documents", splitDocOperatorScenarios) } diff --git a/pkg/yqlib/operator_strings_test.go b/pkg/yqlib/operator_strings_test.go index c94a2fc8..c4e26214 100644 --- a/pkg/yqlib/operator_strings_test.go +++ b/pkg/yqlib/operator_strings_test.go @@ -170,5 +170,5 @@ func TestStringsOperatorScenarios(t *testing.T) { for _, tt := range stringsOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "string-operators", stringsOperatorScenarios) + documentOperatorScenarios(t, "string-operators", stringsOperatorScenarios) } diff --git a/pkg/yqlib/operator_style_test.go b/pkg/yqlib/operator_style_test.go index 5f2490fd..ce263a7b 100644 --- a/pkg/yqlib/operator_style_test.go +++ b/pkg/yqlib/operator_style_test.go @@ -152,5 +152,5 @@ func TestStyleOperatorScenarios(t *testing.T) { for _, tt := range styleOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "style", styleOperatorScenarios) + documentOperatorScenarios(t, "style", styleOperatorScenarios) } diff --git a/pkg/yqlib/operator_subtract_test.go b/pkg/yqlib/operator_subtract_test.go index 7e209d5e..e43e0bfe 100644 --- a/pkg/yqlib/operator_subtract_test.go +++ b/pkg/yqlib/operator_subtract_test.go @@ -99,5 +99,5 @@ func TestSubtractOperatorScenarios(t *testing.T) { for _, tt := range subtractOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "subtract", subtractOperatorScenarios) + documentOperatorScenarios(t, "subtract", subtractOperatorScenarios) } diff --git a/pkg/yqlib/operator_tag_test.go b/pkg/yqlib/operator_tag_test.go index 08b22741..380f6028 100644 --- a/pkg/yqlib/operator_tag_test.go +++ b/pkg/yqlib/operator_tag_test.go @@ -64,5 +64,5 @@ func TestTagOperatorScenarios(t *testing.T) { for _, tt := range tagOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "tag", tagOperatorScenarios) + documentOperatorScenarios(t, "tag", tagOperatorScenarios) } diff --git a/pkg/yqlib/operator_traverse_path_test.go b/pkg/yqlib/operator_traverse_path_test.go index b556ba28..94400ba2 100644 --- a/pkg/yqlib/operator_traverse_path_test.go +++ b/pkg/yqlib/operator_traverse_path_test.go @@ -519,5 +519,5 @@ func TestTraversePathOperatorScenarios(t *testing.T) { for _, tt := range traversePathOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "traverse-read", traversePathOperatorScenarios) + documentOperatorScenarios(t, "traverse-read", traversePathOperatorScenarios) } diff --git a/pkg/yqlib/operator_union_test.go b/pkg/yqlib/operator_union_test.go index a5f12de7..1409fd7f 100644 --- a/pkg/yqlib/operator_union_test.go +++ b/pkg/yqlib/operator_union_test.go @@ -44,5 +44,5 @@ func TestUnionOperatorScenarios(t *testing.T) { for _, tt := range unionOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "union", unionOperatorScenarios) + documentOperatorScenarios(t, "union", unionOperatorScenarios) } diff --git a/pkg/yqlib/operator_unique_test.go b/pkg/yqlib/operator_unique_test.go index 347f4d71..4a3ce79b 100644 --- a/pkg/yqlib/operator_unique_test.go +++ b/pkg/yqlib/operator_unique_test.go @@ -61,5 +61,5 @@ func TestUniqueOperatorScenarios(t *testing.T) { for _, tt := range uniqueOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "unique", uniqueOperatorScenarios) + documentOperatorScenarios(t, "unique", uniqueOperatorScenarios) } diff --git a/pkg/yqlib/operator_variables_test.go b/pkg/yqlib/operator_variables_test.go index a6e8d854..e40f8dd8 100644 --- a/pkg/yqlib/operator_variables_test.go +++ b/pkg/yqlib/operator_variables_test.go @@ -66,5 +66,5 @@ func TestVariableOperatorScenarios(t *testing.T) { for _, tt := range variableOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "variable-operators", variableOperatorScenarios) + documentOperatorScenarios(t, "variable-operators", variableOperatorScenarios) } diff --git a/pkg/yqlib/operator_with_test.go b/pkg/yqlib/operator_with_test.go index 11ea08c1..aad4d15c 100644 --- a/pkg/yqlib/operator_with_test.go +++ b/pkg/yqlib/operator_with_test.go @@ -34,5 +34,5 @@ func TestWithOperatorScenarios(t *testing.T) { for _, tt := range withOperatorScenarios { testScenario(t, &tt) } - documentScenarios(t, "with", withOperatorScenarios) + documentOperatorScenarios(t, "with", withOperatorScenarios) } diff --git a/pkg/yqlib/operators_test.go b/pkg/yqlib/operators_test.go index b99d3865..c99c63a7 100644 --- a/pkg/yqlib/operators_test.go +++ b/pkg/yqlib/operators_test.go @@ -32,7 +32,7 @@ func readDocumentWithLeadingContent(content string, fakefilename string, fakeFil return nil, err } - inputs, err := readDocuments(reader, fakefilename, fakeFileIndex) + inputs, err := readDocuments(reader, fakefilename, fakeFileIndex, NewYamlDecoder()) if err != nil { return nil, err } @@ -90,27 +90,32 @@ func testScenario(t *testing.T, s *expressionScenario) { test.AssertResultComplexWithContext(t, s.expected, resultsToString(t, context.MatchingNodes), fmt.Sprintf("desc: %v\nexp: %v\ndoc: %v", s.description, s.expression, s.document)) } +func resultToString(t *testing.T, n *CandidateNode) string { + var valueBuffer bytes.Buffer + printer := NewPrinterWithSingleWriter(bufio.NewWriter(&valueBuffer), YamlOutputFormat, true, false, 4, true) + + err := printer.PrintResults(n.AsList()) + if err != nil { + t.Error(err) + return "" + } + + tag := n.Node.Tag + if n.Node.Kind == yaml.DocumentNode { + tag = "doc" + } else if n.Node.Kind == yaml.AliasNode { + tag = "alias" + } + return fmt.Sprintf(`D%v, P%v, (%v)::%v`, n.Document, n.Path, tag, valueBuffer.String()) +} + func resultsToString(t *testing.T, results *list.List) []string { var pretty = make([]string, 0) for el := results.Front(); el != nil; el = el.Next() { n := el.Value.(*CandidateNode) - var valueBuffer bytes.Buffer - printer := NewPrinterWithSingleWriter(bufio.NewWriter(&valueBuffer), YamlOutputFormat, true, false, 4, true) - err := printer.PrintResults(n.AsList()) - if err != nil { - t.Error(err) - return nil - } - - tag := n.Node.Tag - if n.Node.Kind == yaml.DocumentNode { - tag = "doc" - } else if n.Node.Kind == yaml.AliasNode { - tag = "alias" - } - output := fmt.Sprintf(`D%v, P%v, (%v)::%v`, n.Document, n.Path, tag, valueBuffer.String()) + output := resultToString(t, n) pretty = append(pretty, output) } return pretty @@ -123,8 +128,8 @@ func writeOrPanic(w *bufio.Writer, text string) { } } -func copyFromHeader(title string, out *os.File) error { - source := fmt.Sprintf("doc/headers/%v.md", title) +func copyFromHeader(folder string, title string, out *os.File) error { + source := fmt.Sprintf("doc/%v/headers/%v.md", folder, title) _, err := os.Stat(source) if os.IsNotExist(err) { return nil @@ -147,15 +152,17 @@ func formatYaml(yaml string, filename string) string { panic(err) } streamEvaluator := NewStreamEvaluator() - _, err = streamEvaluator.Evaluate(filename, strings.NewReader(yaml), node, printer, "") + _, err = streamEvaluator.Evaluate(filename, strings.NewReader(yaml), node, printer, "", NewYamlDecoder()) if err != nil { panic(err) } return output.String() } -func documentScenarios(t *testing.T, title string, scenarios []expressionScenario) { - f, err := os.Create(fmt.Sprintf("doc/%v.md", title)) +type documentScenarioFunc func(t *testing.T, writer *bufio.Writer, scenario interface{}) + +func documentScenarios(t *testing.T, folder string, title string, scenarios []interface{}, documentScenario documentScenarioFunc) { + f, err := os.Create(fmt.Sprintf("doc/%v/%v.md", folder, title)) if err != nil { t.Error(err) @@ -163,7 +170,7 @@ func documentScenarios(t *testing.T, title string, scenarios []expressionScenari } defer f.Close() - err = copyFromHeader(title, f) + err = copyFromHeader(folder, title, f) if err != nil { t.Error(err) return @@ -173,14 +180,26 @@ func documentScenarios(t *testing.T, title string, scenarios []expressionScenari writeOrPanic(w, "\n") for _, s := range scenarios { - if !s.skipDoc { - documentScenario(t, w, s) - } + documentScenario(t, w, s) } w.Flush() } -func documentScenario(t *testing.T, w *bufio.Writer, s expressionScenario) { +func documentOperatorScenarios(t *testing.T, title string, scenarios []expressionScenario) { + genericScenarios := make([]interface{}, len(scenarios)) + for i, s := range scenarios { + genericScenarios[i] = s + } + + documentScenarios(t, "operators", title, genericScenarios, documentOperatorScenario) +} + +func documentOperatorScenario(t *testing.T, w *bufio.Writer, i interface{}) { + s := i.(expressionScenario) + + if s.skipDoc { + return + } writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) if s.subdescription != "" { diff --git a/pkg/yqlib/printer_test.go b/pkg/yqlib/printer_test.go index 8c74af87..d3172906 100644 --- a/pkg/yqlib/printer_test.go +++ b/pkg/yqlib/printer_test.go @@ -38,7 +38,7 @@ func TestPrinterMultipleDocsInSequence(t *testing.T) { var writer = bufio.NewWriter(&output) printer := NewPrinterWithSingleWriter(writer, YamlOutputFormat, true, false, 2, true) - inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0) + inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder()) if err != nil { panic(err) } @@ -76,7 +76,7 @@ func TestPrinterMultipleDocsInSequenceWithLeadingContent(t *testing.T) { var writer = bufio.NewWriter(&output) printer := NewPrinterWithSingleWriter(writer, YamlOutputFormat, true, false, 2, true) - inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0) + inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder()) if err != nil { panic(err) } @@ -118,7 +118,7 @@ func TestPrinterMultipleFilesInSequence(t *testing.T) { var writer = bufio.NewWriter(&output) printer := NewPrinterWithSingleWriter(writer, YamlOutputFormat, true, false, 2, true) - inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0) + inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder()) if err != nil { panic(err) } @@ -165,7 +165,7 @@ func TestPrinterMultipleFilesInSequenceWithLeadingContent(t *testing.T) { var writer = bufio.NewWriter(&output) printer := NewPrinterWithSingleWriter(writer, YamlOutputFormat, true, false, 2, true) - inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0) + inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder()) if err != nil { panic(err) } @@ -215,7 +215,7 @@ func TestPrinterMultipleDocsInSinglePrint(t *testing.T) { var writer = bufio.NewWriter(&output) printer := NewPrinterWithSingleWriter(writer, YamlOutputFormat, true, false, 2, true) - inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0) + inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder()) if err != nil { panic(err) } @@ -234,7 +234,7 @@ func TestPrinterMultipleDocsInSinglePrintWithLeadingDoc(t *testing.T) { var writer = bufio.NewWriter(&output) printer := NewPrinterWithSingleWriter(writer, YamlOutputFormat, true, false, 2, true) - inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0) + inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder()) if err != nil { panic(err) } @@ -263,7 +263,7 @@ func TestPrinterMultipleDocsInSinglePrintWithLeadingDocTrailing(t *testing.T) { var writer = bufio.NewWriter(&output) printer := NewPrinterWithSingleWriter(writer, YamlOutputFormat, true, false, 2, true) - inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0) + inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder()) if err != nil { panic(err) } @@ -294,7 +294,7 @@ func TestPrinterScalarWithLeadingCont(t *testing.T) { panic(err) } streamEvaluator := NewStreamEvaluator() - _, err = streamEvaluator.Evaluate("sample", strings.NewReader(multiDocSample), node, printer, "# blah\n") + _, err = streamEvaluator.Evaluate("sample", strings.NewReader(multiDocSample), node, printer, "# blah\n", NewYamlDecoder()) if err != nil { panic(err) } @@ -316,7 +316,7 @@ func TestPrinterMultipleDocsJson(t *testing.T) { // when outputing JSON. printer := NewPrinterWithSingleWriter(writer, JsonOutputFormat, true, false, 0, true) - inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0) + inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder()) if err != nil { panic(err) } diff --git a/pkg/yqlib/stream_evaluator.go b/pkg/yqlib/stream_evaluator.go index ddb777fb..96826f30 100644 --- a/pkg/yqlib/stream_evaluator.go +++ b/pkg/yqlib/stream_evaluator.go @@ -14,8 +14,8 @@ import ( // Uses less memory than loading all documents and running the expression once, but this cannot process // cross document expressions. type StreamEvaluator interface { - Evaluate(filename string, reader io.Reader, node *ExpressionNode, printer Printer, leadingContent string) (uint, error) - EvaluateFiles(expression string, filenames []string, printer Printer, leadingContentPreProcessing bool) error + Evaluate(filename string, reader io.Reader, node *ExpressionNode, printer Printer, leadingContent string, decoder Decoder) (uint, error) + EvaluateFiles(expression string, filenames []string, printer Printer, leadingContentPreProcessing bool, decoder Decoder) error EvaluateNew(expression string, printer Printer, leadingContent string) error } @@ -51,7 +51,7 @@ func (s *streamEvaluator) EvaluateNew(expression string, printer Printer, leadin return printer.PrintResults(result.MatchingNodes) } -func (s *streamEvaluator) EvaluateFiles(expression string, filenames []string, printer Printer, leadingContentPreProcessing bool) error { +func (s *streamEvaluator) EvaluateFiles(expression string, filenames []string, printer Printer, leadingContentPreProcessing bool, decoder Decoder) error { var totalProcessDocs uint node, err := s.treeCreator.ParseExpression(expression) if err != nil { @@ -70,7 +70,7 @@ func (s *streamEvaluator) EvaluateFiles(expression string, filenames []string, p if err != nil { return err } - processedDocs, err := s.Evaluate(filename, reader, node, printer, leadingContent) + processedDocs, err := s.Evaluate(filename, reader, node, printer, leadingContent, decoder) if err != nil { return err } @@ -89,10 +89,10 @@ func (s *streamEvaluator) EvaluateFiles(expression string, filenames []string, p return nil } -func (s *streamEvaluator) Evaluate(filename string, reader io.Reader, node *ExpressionNode, printer Printer, leadingContent string) (uint, error) { +func (s *streamEvaluator) Evaluate(filename string, reader io.Reader, node *ExpressionNode, printer Printer, leadingContent string, decoder Decoder) (uint, error) { var currentIndex uint - decoder := yaml.NewDecoder(reader) + decoder.Init(reader) for { var dataBucket yaml.Node errorReading := decoder.Decode(&dataBucket) diff --git a/pkg/yqlib/utils.go b/pkg/yqlib/utils.go index f8291a40..ecfa6a99 100644 --- a/pkg/yqlib/utils.go +++ b/pkg/yqlib/utils.go @@ -107,8 +107,8 @@ func processReadStream(reader *bufio.Reader) (io.Reader, string, error) { } } -func readDocuments(reader io.Reader, filename string, fileIndex int) (*list.List, error) { - decoder := yaml.NewDecoder(reader) +func readDocuments(reader io.Reader, filename string, fileIndex int, decoder Decoder) (*list.List, error) { + decoder.Init(reader) inputList := list.New() var currentIndex uint diff --git a/scripts/copy-docs.sh b/scripts/copy-docs.sh index e3f39341..5c27efaa 100755 --- a/scripts/copy-docs.sh +++ b/scripts/copy-docs.sh @@ -1,4 +1,5 @@ #!/bin/bash cp how-it-works.md ../yq-gitbook/. -cp pkg/yqlib/doc/*.md ../yq-gitbook/operators/. \ No newline at end of file +cp pkg/yqlib/doc/operators/*.md ../yq-gitbook/operators/. +cp pkg/yqlib/doc/usage/*.md ../yq-gitbook/usage/. \ No newline at end of file