mirror of
https://github.com/mikefarah/yq.git
synced 2024-12-19 20:19:04 +00:00
wip
This commit is contained in:
parent
2e9c91f8a1
commit
585cafc1c0
@ -14,33 +14,14 @@ import (
|
|||||||
type xmlDecoder struct {
|
type xmlDecoder struct {
|
||||||
reader io.Reader
|
reader io.Reader
|
||||||
readAnything bool
|
readAnything bool
|
||||||
attributePrefix string
|
|
||||||
directiveName string
|
|
||||||
procInstPrefix string
|
|
||||||
contentName string
|
|
||||||
strictMode bool
|
|
||||||
keepNamespace bool
|
|
||||||
useRawToken bool
|
|
||||||
finished bool
|
finished bool
|
||||||
skipDirectives bool
|
prefs xmlPreferences
|
||||||
skipProcInst bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewXMLDecoder(attributePrefix string, contentName string, strictMode bool, keepNamespace bool, useRawToken bool, skipDirectives bool, skipProcInst bool) Decoder {
|
func NewXMLDecoder(prefs xmlPreferences) Decoder {
|
||||||
if contentName == "" {
|
|
||||||
contentName = "content"
|
|
||||||
}
|
|
||||||
return &xmlDecoder{
|
return &xmlDecoder{
|
||||||
attributePrefix: attributePrefix,
|
|
||||||
contentName: contentName,
|
|
||||||
finished: false,
|
finished: false,
|
||||||
strictMode: strictMode,
|
prefs: prefs,
|
||||||
keepNamespace: keepNamespace,
|
|
||||||
useRawToken: useRawToken,
|
|
||||||
directiveName: "_directive_",
|
|
||||||
procInstPrefix: "_procInst_",
|
|
||||||
skipDirectives: skipDirectives,
|
|
||||||
skipProcInst: skipProcInst,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +56,7 @@ func (dec *xmlDecoder) createMap(n *xmlNode) (*yaml.Node, error) {
|
|||||||
yamlNode := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"}
|
yamlNode := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"}
|
||||||
|
|
||||||
if len(n.Data) > 0 {
|
if len(n.Data) > 0 {
|
||||||
label := dec.contentName
|
label := dec.prefs.ContentName
|
||||||
labelNode := createScalarNode(label, label)
|
labelNode := createScalarNode(label, label)
|
||||||
labelNode.HeadComment = dec.processComment(n.HeadComment)
|
labelNode.HeadComment = dec.processComment(n.HeadComment)
|
||||||
labelNode.FootComment = dec.processComment(n.FootComment)
|
labelNode.FootComment = dec.processComment(n.FootComment)
|
||||||
@ -211,7 +192,7 @@ type element struct {
|
|||||||
// of the map keys.
|
// of the map keys.
|
||||||
func (dec *xmlDecoder) decodeXML(root *xmlNode) error {
|
func (dec *xmlDecoder) decodeXML(root *xmlNode) error {
|
||||||
xmlDec := xml.NewDecoder(dec.reader)
|
xmlDec := xml.NewDecoder(dec.reader)
|
||||||
xmlDec.Strict = dec.strictMode
|
xmlDec.Strict = dec.prefs.StrictMode
|
||||||
// That will convert the charset if the provided XML is non-UTF-8
|
// That will convert the charset if the provided XML is non-UTF-8
|
||||||
xmlDec.CharsetReader = charset.NewReaderLabel
|
xmlDec.CharsetReader = charset.NewReaderLabel
|
||||||
|
|
||||||
@ -222,7 +203,7 @@ func (dec *xmlDecoder) decodeXML(root *xmlNode) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getToken := func() (xml.Token, error) {
|
getToken := func() (xml.Token, error) {
|
||||||
if dec.useRawToken {
|
if dec.prefs.UseRawToken {
|
||||||
return xmlDec.RawToken()
|
return xmlDec.RawToken()
|
||||||
}
|
}
|
||||||
return xmlDec.Token()
|
return xmlDec.Token()
|
||||||
@ -250,12 +231,12 @@ func (dec *xmlDecoder) decodeXML(root *xmlNode) error {
|
|||||||
|
|
||||||
// Extract attributes as children
|
// Extract attributes as children
|
||||||
for _, a := range se.Attr {
|
for _, a := range se.Attr {
|
||||||
if dec.keepNamespace {
|
if dec.prefs.KeepNamespace {
|
||||||
if a.Name.Space != "" {
|
if a.Name.Space != "" {
|
||||||
a.Name.Local = a.Name.Space + ":" + a.Name.Local
|
a.Name.Local = a.Name.Space + ":" + a.Name.Local
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elem.n.AddChild(dec.attributePrefix+a.Name.Local, &xmlNode{Data: a.Value})
|
elem.n.AddChild(dec.prefs.AttributePrefix+a.Name.Local, &xmlNode{Data: a.Value})
|
||||||
}
|
}
|
||||||
case xml.CharData:
|
case xml.CharData:
|
||||||
// Extract XML data (if any)
|
// Extract XML data (if any)
|
||||||
@ -289,12 +270,12 @@ func (dec *xmlDecoder) decodeXML(root *xmlNode) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case xml.ProcInst:
|
case xml.ProcInst:
|
||||||
if !dec.skipProcInst {
|
if !dec.prefs.SkipProcInst {
|
||||||
elem.n.AddChild(dec.procInstPrefix+se.Target, &xmlNode{Data: string(se.Inst)})
|
elem.n.AddChild(dec.prefs.ProcInstPrefix+se.Target, &xmlNode{Data: string(se.Inst)})
|
||||||
}
|
}
|
||||||
case xml.Directive:
|
case xml.Directive:
|
||||||
if !dec.skipDirectives {
|
if !dec.prefs.SkipDirectives {
|
||||||
elem.n.AddChild(dec.directiveName, &xmlNode{Data: string(se)})
|
elem.n.AddChild(dec.prefs.DirectiveName, &xmlNode{Data: string(se)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ yq -p=xml '.' sample.xml
|
|||||||
```
|
```
|
||||||
will output
|
will output
|
||||||
```yaml
|
```yaml
|
||||||
_procInst_xml: version="1.0" encoding="UTF-8"
|
+p_xml: version="1.0" encoding="UTF-8"
|
||||||
cat:
|
cat:
|
||||||
says: meow
|
says: meow
|
||||||
legs: "4"
|
legs: "4"
|
||||||
@ -55,7 +55,7 @@ yq -p=xml ' (.. | select(tag == "!!str")) |= from_yaml' sample.xml
|
|||||||
```
|
```
|
||||||
will output
|
will output
|
||||||
```yaml
|
```yaml
|
||||||
_procInst_xml: version="1.0" encoding="UTF-8"
|
+p_xml: version="1.0" encoding="UTF-8"
|
||||||
cat:
|
cat:
|
||||||
says: meow
|
says: meow
|
||||||
legs: 4
|
legs: 4
|
||||||
@ -77,7 +77,7 @@ yq -p=xml '.' sample.xml
|
|||||||
```
|
```
|
||||||
will output
|
will output
|
||||||
```yaml
|
```yaml
|
||||||
_procInst_xml: version="1.0" encoding="UTF-8"
|
+p_xml: version="1.0" encoding="UTF-8"
|
||||||
animal:
|
animal:
|
||||||
- cat
|
- cat
|
||||||
- goat
|
- goat
|
||||||
@ -99,9 +99,9 @@ yq -p=xml '.' sample.xml
|
|||||||
```
|
```
|
||||||
will output
|
will output
|
||||||
```yaml
|
```yaml
|
||||||
_procInst_xml: version="1.0" encoding="UTF-8"
|
+p_xml: version="1.0" encoding="UTF-8"
|
||||||
cat:
|
cat:
|
||||||
+legs: "4"
|
+@legs: "4"
|
||||||
legs: "7"
|
legs: "7"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -119,14 +119,14 @@ yq -p=xml '.' sample.xml
|
|||||||
```
|
```
|
||||||
will output
|
will output
|
||||||
```yaml
|
```yaml
|
||||||
_procInst_xml: version="1.0" encoding="UTF-8"
|
+p_xml: version="1.0" encoding="UTF-8"
|
||||||
cat:
|
cat:
|
||||||
+content: meow
|
+content: meow
|
||||||
+legs: "4"
|
+@legs: "4"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parse xml: custom dtd
|
## Parse xml: custom dtd
|
||||||
DTD entities are ignored.
|
DTD entities are processed as directives.
|
||||||
|
|
||||||
Given a sample.xml file of:
|
Given a sample.xml file of:
|
||||||
```xml
|
```xml
|
||||||
@ -142,18 +142,45 @@ Given a sample.xml file of:
|
|||||||
```
|
```
|
||||||
then
|
then
|
||||||
```bash
|
```bash
|
||||||
yq -p=xml '.' sample.xml
|
yq -p=xml -o=xml '.' sample.xml
|
||||||
```
|
```
|
||||||
will output
|
will output
|
||||||
```yaml
|
```xml
|
||||||
_procInst_xml: version="1.0"
|
<?xml version="1.0"?>
|
||||||
_directive_: |-
|
<!DOCTYPE root [
|
||||||
DOCTYPE root [
|
|
||||||
<!ENTITY writer "Blah.">
|
<!ENTITY writer "Blah.">
|
||||||
<!ENTITY copyright "Blah">
|
<!ENTITY copyright "Blah">
|
||||||
]
|
]>
|
||||||
root:
|
<root>
|
||||||
item: '&writer;©right;'
|
<item>&writer;&copyright;</item>
|
||||||
|
</root>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parse xml: skip custom dtd
|
||||||
|
DTDs are directives, skip over directives to skip DTDs.
|
||||||
|
|
||||||
|
Given a sample.xml file of:
|
||||||
|
```xml
|
||||||
|
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE root [
|
||||||
|
<!ENTITY writer "Blah.">
|
||||||
|
<!ENTITY copyright "Blah">
|
||||||
|
]>
|
||||||
|
<root>
|
||||||
|
<item>&writer;©right;</item>
|
||||||
|
</root>
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq -p=xml -o=xml --xml-skip-directives '.' sample.xml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<root>
|
||||||
|
<item>&writer;&copyright;</item>
|
||||||
|
</root>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parse xml: with comments
|
## Parse xml: with comments
|
||||||
@ -225,7 +252,7 @@ will output
|
|||||||
instead of
|
instead of
|
||||||
```xml
|
```xml
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<map xmlns="some-namespace" xsi="some-instance" schemaLocation="some-url"></map>
|
<map xmlns="some-namespace" xmlns:xsi="some-instance" some-instance:schemaLocation="some-url"></map>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parse xml: keep raw attribute namespace
|
## Parse xml: keep raw attribute namespace
|
||||||
@ -244,7 +271,7 @@ yq -p=xml -o=xml --xml-keep-namespace --xml-raw-token '.' sample.xml
|
|||||||
will output
|
will output
|
||||||
```xml
|
```xml
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<map xmlns="some-namespace" xmlns:xsi="some-instance" xsi:schemaLocation="some-url"></map>
|
<map xmlns="some-namespace" xmlns:xsi="some-instance" some-instance:schemaLocation="some-url"></map>
|
||||||
```
|
```
|
||||||
|
|
||||||
instead of
|
instead of
|
||||||
@ -293,7 +320,7 @@ Fields with the matching xml-attribute-prefix are assumed to be attributes.
|
|||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
cat:
|
cat:
|
||||||
+name: tiger
|
+@name: tiger
|
||||||
meows: true
|
meows: true
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -314,7 +341,7 @@ Fields with the matching xml-content-name is assumed to be content.
|
|||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
cat:
|
cat:
|
||||||
+name: tiger
|
+@name: tiger
|
||||||
+content: cool
|
+content: cool
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -359,11 +386,11 @@ Use the special xml names to add/modify proc instructions and directives.
|
|||||||
|
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
_procInst_xml: version="1.0"
|
+p_xml: version="1.0"
|
||||||
_directive_: 'DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" '
|
+directive: 'DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" '
|
||||||
apple:
|
apple:
|
||||||
_procInst_coolioo: version="1.0"
|
+p_coolioo: version="1.0"
|
||||||
_directive_: 'CATYPE meow purr puss '
|
+directive: 'CATYPE meow purr puss '
|
||||||
b: things
|
b: things
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -9,24 +9,19 @@ import (
|
|||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var XMLPreferences = xmlPreferences{AttributePrefix: "+", ContentName: "+content", StrictMode: false, UseRawToken: false}
|
|
||||||
|
|
||||||
type xmlEncoder struct {
|
type xmlEncoder struct {
|
||||||
attributePrefix string
|
|
||||||
contentName string
|
|
||||||
indentString string
|
indentString string
|
||||||
directiveName string
|
|
||||||
procInstPrefix string
|
|
||||||
writer io.Writer
|
writer io.Writer
|
||||||
|
prefs xmlPreferences
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewXMLEncoder(indent int, attributePrefix string, contentName string) Encoder {
|
func NewXMLEncoder(indent int, prefs xmlPreferences) Encoder {
|
||||||
var indentString = ""
|
var indentString = ""
|
||||||
|
|
||||||
for index := 0; index < indent; index++ {
|
for index := 0; index < indent; index++ {
|
||||||
indentString = indentString + " "
|
indentString = indentString + " "
|
||||||
}
|
}
|
||||||
return &xmlEncoder{attributePrefix, contentName, indentString, "_directive_", "_procInst_", nil}
|
return &xmlEncoder{indentString, nil, prefs}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *xmlEncoder) CanHandleAliases() bool {
|
func (e *xmlEncoder) CanHandleAliases() bool {
|
||||||
@ -97,8 +92,8 @@ func (e *xmlEncoder) encodeTopLevelMap(encoder *xml.Encoder, node *yaml.Node) er
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(key.Value, e.procInstPrefix) {
|
if strings.HasPrefix(key.Value, e.prefs.ProcInstPrefix) {
|
||||||
name := strings.Replace(key.Value, e.procInstPrefix, "", 1)
|
name := strings.Replace(key.Value, e.prefs.ProcInstPrefix, "", 1)
|
||||||
procInst := xml.ProcInst{Target: name, Inst: []byte(value.Value)}
|
procInst := xml.ProcInst{Target: name, Inst: []byte(value.Value)}
|
||||||
if err := encoder.EncodeToken(procInst); err != nil {
|
if err := encoder.EncodeToken(procInst); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -106,7 +101,7 @@ func (e *xmlEncoder) encodeTopLevelMap(encoder *xml.Encoder, node *yaml.Node) er
|
|||||||
if _, err := e.writer.Write([]byte("\n")); err != nil {
|
if _, err := e.writer.Write([]byte("\n")); err != nil {
|
||||||
log.Warning("Unable to write newline, skipping: %w", err)
|
log.Warning("Unable to write newline, skipping: %w", err)
|
||||||
}
|
}
|
||||||
} else if key.Value == e.directiveName {
|
} else if key.Value == e.prefs.DirectiveName {
|
||||||
var directive xml.Directive = []byte(value.Value)
|
var directive xml.Directive = []byte(value.Value)
|
||||||
if err := encoder.EncodeToken(directive); err != nil {
|
if err := encoder.EncodeToken(directive); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -205,6 +200,13 @@ func (e *xmlEncoder) encodeArray(encoder *xml.Encoder, node *yaml.Node, start xm
|
|||||||
return e.encodeComment(encoder, footComment(node))
|
return e.encodeComment(encoder, footComment(node))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *xmlEncoder) isAttribute(name string) bool {
|
||||||
|
return strings.HasPrefix(name, e.prefs.AttributePrefix) &&
|
||||||
|
name != e.prefs.ContentName &&
|
||||||
|
name != e.prefs.DirectiveName &&
|
||||||
|
!strings.HasPrefix(name, e.prefs.ProcInstPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
func (e *xmlEncoder) encodeMap(encoder *xml.Encoder, node *yaml.Node, start xml.StartElement) error {
|
func (e *xmlEncoder) encodeMap(encoder *xml.Encoder, node *yaml.Node, start xml.StartElement) error {
|
||||||
log.Debug("its a map")
|
log.Debug("its a map")
|
||||||
|
|
||||||
@ -213,9 +215,9 @@ func (e *xmlEncoder) encodeMap(encoder *xml.Encoder, node *yaml.Node, start xml.
|
|||||||
key := node.Content[i]
|
key := node.Content[i]
|
||||||
value := node.Content[i+1]
|
value := node.Content[i+1]
|
||||||
|
|
||||||
if strings.HasPrefix(key.Value, e.attributePrefix) && key.Value != e.contentName {
|
if e.isAttribute(key.Value) {
|
||||||
if value.Kind == yaml.ScalarNode {
|
if value.Kind == yaml.ScalarNode {
|
||||||
attributeName := strings.Replace(key.Value, e.attributePrefix, "", 1)
|
attributeName := strings.Replace(key.Value, e.prefs.AttributePrefix, "", 1)
|
||||||
start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: attributeName}, Value: value.Value})
|
start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: attributeName}, Value: value.Value})
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("cannot use %v as attribute, only scalars are supported", value.Tag)
|
return fmt.Errorf("cannot use %v as attribute, only scalars are supported", value.Tag)
|
||||||
@ -237,24 +239,18 @@ func (e *xmlEncoder) encodeMap(encoder *xml.Encoder, node *yaml.Node, start xml.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(key.Value, e.procInstPrefix) {
|
if strings.HasPrefix(key.Value, e.prefs.ProcInstPrefix) {
|
||||||
name := strings.Replace(key.Value, e.procInstPrefix, "", 1)
|
name := strings.Replace(key.Value, e.prefs.ProcInstPrefix, "", 1)
|
||||||
procInst := xml.ProcInst{Target: name, Inst: []byte(value.Value)}
|
procInst := xml.ProcInst{Target: name, Inst: []byte(value.Value)}
|
||||||
if err := encoder.EncodeToken(procInst); err != nil {
|
if err := encoder.EncodeToken(procInst); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if key.Value == e.directiveName {
|
} else if key.Value == e.prefs.DirectiveName {
|
||||||
var directive xml.Directive = []byte(value.Value)
|
var directive xml.Directive = []byte(value.Value)
|
||||||
if err := encoder.EncodeToken(directive); err != nil {
|
if err := encoder.EncodeToken(directive); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if !strings.HasPrefix(key.Value, e.attributePrefix) && key.Value != e.contentName {
|
} else if key.Value == e.prefs.ContentName {
|
||||||
start := xml.StartElement{Name: xml.Name{Local: key.Value}}
|
|
||||||
err := e.doEncode(encoder, value, start)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else if key.Value == e.contentName {
|
|
||||||
// directly encode the contents
|
// directly encode the contents
|
||||||
err = e.encodeComment(encoder, headAndLineComment(value))
|
err = e.encodeComment(encoder, headAndLineComment(value))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -269,6 +265,12 @@ func (e *xmlEncoder) encodeMap(encoder *xml.Encoder, node *yaml.Node, start xml.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
} else if !e.isAttribute(key.Value) {
|
||||||
|
start := xml.StartElement{Name: xml.Name{Local: key.Value}}
|
||||||
|
err := e.doEncode(encoder, value, start)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
err = e.encodeComment(encoder, footComment(key))
|
err = e.encodeComment(encoder, footComment(key))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -76,7 +76,7 @@ var participleYqRules = []*participleYqRule{
|
|||||||
{"Base64d", `@base64d`, decodeOp(Base64InputFormat), 0},
|
{"Base64d", `@base64d`, decodeOp(Base64InputFormat), 0},
|
||||||
{"Base64", `@base64`, encodeWithIndent(Base64OutputFormat, 0), 0},
|
{"Base64", `@base64`, encodeWithIndent(Base64OutputFormat, 0), 0},
|
||||||
|
|
||||||
{"LoadXML", `load_?xml|xml_?load`, loadOp(NewXMLDecoder(XMLPreferences.AttributePrefix, XMLPreferences.ContentName, XMLPreferences.StrictMode, XMLPreferences.KeepNamespace, XMLPreferences.UseRawToken), false), 0},
|
{"LoadXML", `load_?xml|xml_?load`, loadOp(NewXMLDecoder(XMLPreferences), false), 0},
|
||||||
|
|
||||||
{"LoadBase64", `load_?base64`, loadOp(NewBase64Decoder(), false), 0},
|
{"LoadBase64", `load_?base64`, loadOp(NewBase64Decoder(), false), 0},
|
||||||
|
|
||||||
|
@ -27,8 +27,28 @@ type xmlPreferences struct {
|
|||||||
StrictMode bool
|
StrictMode bool
|
||||||
KeepNamespace bool
|
KeepNamespace bool
|
||||||
UseRawToken bool
|
UseRawToken bool
|
||||||
|
ProcInstPrefix string
|
||||||
|
DirectiveName string
|
||||||
|
SkipProcInst bool
|
||||||
|
SkipDirectives bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewDefaultXmlPreferences() xmlPreferences {
|
||||||
|
return xmlPreferences{
|
||||||
|
AttributePrefix: "+@",
|
||||||
|
ContentName: "+content",
|
||||||
|
StrictMode: false,
|
||||||
|
KeepNamespace: true,
|
||||||
|
UseRawToken: false,
|
||||||
|
ProcInstPrefix: "+p_",
|
||||||
|
DirectiveName: "+directive",
|
||||||
|
SkipProcInst: false,
|
||||||
|
SkipDirectives: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var XMLPreferences = NewDefaultXmlPreferences()
|
||||||
|
|
||||||
var log = logging.MustGetLogger("yq-lib")
|
var log = logging.MustGetLogger("yq-lib")
|
||||||
|
|
||||||
var PrettyPrintExp = `(... | (select(tag != "!!str"), select(tag == "!!str") | select(test("(?i)^(y|yes|n|no|on|off)$") | not)) ) style=""`
|
var PrettyPrintExp = `(... | (select(tag != "!!str"), select(tag == "!!str") | select(test("(?i)^(y|yes|n|no|on|off)$") | not)) ) style=""`
|
||||||
|
@ -23,7 +23,7 @@ func configureEncoder(format PrinterOutputFormat, indent int) Encoder {
|
|||||||
case YamlOutputFormat:
|
case YamlOutputFormat:
|
||||||
return NewYamlEncoder(indent, false, true, true)
|
return NewYamlEncoder(indent, false, true, true)
|
||||||
case XMLOutputFormat:
|
case XMLOutputFormat:
|
||||||
return NewXMLEncoder(indent, XMLPreferences.AttributePrefix, XMLPreferences.ContentName)
|
return NewXMLEncoder(indent, XMLPreferences)
|
||||||
case Base64OutputFormat:
|
case Base64OutputFormat:
|
||||||
return NewBase64Encoder()
|
return NewBase64Encoder()
|
||||||
}
|
}
|
||||||
@ -104,12 +104,7 @@ func decodeOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
|
|||||||
case YamlInputFormat:
|
case YamlInputFormat:
|
||||||
decoder = NewYamlDecoder()
|
decoder = NewYamlDecoder()
|
||||||
case XMLInputFormat:
|
case XMLInputFormat:
|
||||||
decoder = NewXMLDecoder(
|
decoder = NewXMLDecoder(XMLPreferences)
|
||||||
XMLPreferences.AttributePrefix,
|
|
||||||
XMLPreferences.ContentName,
|
|
||||||
XMLPreferences.StrictMode,
|
|
||||||
XMLPreferences.KeepNamespace,
|
|
||||||
XMLPreferences.UseRawToken)
|
|
||||||
case Base64InputFormat:
|
case Base64InputFormat:
|
||||||
decoder = NewBase64Decoder()
|
decoder = NewBase64Decoder()
|
||||||
case PropertiesInputFormat:
|
case PropertiesInputFormat:
|
||||||
|
@ -58,7 +58,7 @@ cat:
|
|||||||
d:
|
d:
|
||||||
# in d before
|
# in d before
|
||||||
z:
|
z:
|
||||||
+sweet: cool
|
+@sweet: cool
|
||||||
# in d after
|
# in d after
|
||||||
# in y after
|
# in y after
|
||||||
# in_cat_after
|
# in_cat_after
|
||||||
@ -98,11 +98,11 @@ cat:
|
|||||||
d:
|
d:
|
||||||
- # in d before
|
- # in d before
|
||||||
z:
|
z:
|
||||||
+sweet: cool
|
+@sweet: cool
|
||||||
# in d after
|
# in d after
|
||||||
- # in d2 before
|
- # in d2 before
|
||||||
z:
|
z:
|
||||||
+sweet: cool2
|
+@sweet: cool2
|
||||||
# in d2 after
|
# in d2 after
|
||||||
# in y after
|
# in y after
|
||||||
# in_cat_after
|
# in_cat_after
|
||||||
@ -159,18 +159,18 @@ const inputXMLWithNamespacedAttr = `
|
|||||||
</map>
|
</map>
|
||||||
`
|
`
|
||||||
|
|
||||||
const expectedYAMLWithNamespacedAttr = `_procInst_xml: version="1.0"
|
const expectedYAMLWithNamespacedAttr = `+p_xml: version="1.0"
|
||||||
map:
|
map:
|
||||||
+xmlns: some-namespace
|
+@xmlns: some-namespace
|
||||||
+xmlns:xsi: some-instance
|
+@xmlns:xsi: some-instance
|
||||||
+some-instance:schemaLocation: some-url
|
+@some-instance:schemaLocation: some-url
|
||||||
`
|
`
|
||||||
|
|
||||||
const expectedYAMLWithRawNamespacedAttr = `_procInst_xml: version="1.0"
|
const expectedYAMLWithRawNamespacedAttr = `+p_xml: version="1.0"
|
||||||
map:
|
map:
|
||||||
+xmlns: some-namespace
|
+@xmlns: some-namespace
|
||||||
+xmlns:xsi: some-instance
|
+@xmlns:xsi: some-instance
|
||||||
+xsi:schemaLocation: some-url
|
+@xsi:schemaLocation: some-url
|
||||||
`
|
`
|
||||||
|
|
||||||
const xmlWithCustomDtd = `
|
const xmlWithCustomDtd = `
|
||||||
@ -183,18 +183,19 @@ const xmlWithCustomDtd = `
|
|||||||
<item>&writer;©right;</item>
|
<item>&writer;©right;</item>
|
||||||
</root>`
|
</root>`
|
||||||
|
|
||||||
const expectedDtd = `_procInst_xml: version="1.0"
|
const expectedDtd = `<?xml version="1.0"?>
|
||||||
_directive_: |-
|
<!DOCTYPE root [
|
||||||
DOCTYPE root [
|
|
||||||
<!ENTITY writer "Blah.">
|
<!ENTITY writer "Blah.">
|
||||||
<!ENTITY copyright "Blah">
|
<!ENTITY copyright "Blah">
|
||||||
]
|
]>
|
||||||
root:
|
<root>
|
||||||
item: '&writer;©right;'
|
<item>&writer;&copyright;</item>
|
||||||
|
</root>
|
||||||
`
|
`
|
||||||
|
|
||||||
const expectedSkippedDtd = `root:
|
const expectedSkippedDtd = `<root>
|
||||||
item: '&writer;©right;'
|
<item>&writer;©right;</item>
|
||||||
|
</root>
|
||||||
`
|
`
|
||||||
|
|
||||||
const xmlWithProcInstAndDirectives = `<?xml version="1.0"?>
|
const xmlWithProcInstAndDirectives = `<?xml version="1.0"?>
|
||||||
@ -206,11 +207,11 @@ const xmlWithProcInstAndDirectives = `<?xml version="1.0"?>
|
|||||||
</apple>
|
</apple>
|
||||||
`
|
`
|
||||||
|
|
||||||
const yamlWithProcInstAndDirectives = `_procInst_xml: version="1.0"
|
const yamlWithProcInstAndDirectives = `+p_xml: version="1.0"
|
||||||
_directive_: 'DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" '
|
+directive: 'DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" '
|
||||||
apple:
|
apple:
|
||||||
_procInst_coolioo: version="1.0"
|
+p_coolioo: version="1.0"
|
||||||
_directive_: 'CATYPE meow purr puss '
|
+directive: 'CATYPE meow purr puss '
|
||||||
b: things
|
b: things
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -258,13 +259,14 @@ var xmlScenarios = []formatScenario{
|
|||||||
subdescription: "DTD entities are processed as directives.",
|
subdescription: "DTD entities are processed as directives.",
|
||||||
input: xmlWithCustomDtd,
|
input: xmlWithCustomDtd,
|
||||||
expected: expectedDtd,
|
expected: expectedDtd,
|
||||||
|
scenarioType: "roundtrip",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Parse xml: custom dtd",
|
description: "Parse xml: skip custom dtd",
|
||||||
subdescription: "DTD entities are processed as directives.",
|
subdescription: "DTDs are directives, skip over directives to skip DTDs.",
|
||||||
input: xmlWithCustomDtd,
|
input: xmlWithCustomDtd,
|
||||||
expected: expectedSkippedDtd,
|
expected: expectedSkippedDtd,
|
||||||
scenarioType: "c",
|
scenarioType: "roundtrip-skip-directives",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Parse xml: with comments",
|
description: "Parse xml: with comments",
|
||||||
@ -360,20 +362,21 @@ var xmlScenarios = []formatScenario{
|
|||||||
{
|
{
|
||||||
description: "Encode xml: attributes",
|
description: "Encode xml: attributes",
|
||||||
subdescription: "Fields with the matching xml-attribute-prefix are assumed to be attributes.",
|
subdescription: "Fields with the matching xml-attribute-prefix are assumed to be attributes.",
|
||||||
input: "cat:\n +name: tiger\n meows: true\n",
|
input: "cat:\n +@name: tiger\n meows: true\n",
|
||||||
expected: "<cat name=\"tiger\">\n <meows>true</meows>\n</cat>\n",
|
expected: "<cat name=\"tiger\">\n <meows>true</meows>\n</cat>\n",
|
||||||
scenarioType: "encode",
|
scenarioType: "encode",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
description: "double prefix",
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
input: "cat:\n ++name: tiger\n meows: true\n",
|
input: "cat:\n +@+@name: tiger\n meows: true\n",
|
||||||
expected: "<cat +name=\"tiger\">\n <meows>true</meows>\n</cat>\n",
|
expected: "<cat +@name=\"tiger\">\n <meows>true</meows>\n</cat>\n",
|
||||||
scenarioType: "encode",
|
scenarioType: "encode",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Encode xml: attributes with content",
|
description: "Encode xml: attributes with content",
|
||||||
subdescription: "Fields with the matching xml-content-name is assumed to be content.",
|
subdescription: "Fields with the matching xml-content-name is assumed to be content.",
|
||||||
input: "cat:\n +name: tiger\n +content: cool\n",
|
input: "cat:\n +@name: tiger\n +content: cool\n",
|
||||||
expected: "<cat name=\"tiger\">cool</cat>\n",
|
expected: "<cat name=\"tiger\">cool</cat>\n",
|
||||||
scenarioType: "encode",
|
scenarioType: "encode",
|
||||||
},
|
},
|
||||||
@ -410,17 +413,23 @@ var xmlScenarios = []formatScenario{
|
|||||||
func testXMLScenario(t *testing.T, s formatScenario) {
|
func testXMLScenario(t *testing.T, s formatScenario) {
|
||||||
switch s.scenarioType {
|
switch s.scenarioType {
|
||||||
case "", "decode":
|
case "", "decode":
|
||||||
test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder("+", "+content", false, false, false, false, false), NewYamlEncoder(4, false, true, true)), s.description)
|
test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder(XMLPreferences), NewYamlEncoder(4, false, true, true)), s.description)
|
||||||
case "encode":
|
case "encode":
|
||||||
test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewYamlDecoder(), NewXMLEncoder(2, "+", "+content")), s.description)
|
test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewYamlDecoder(), NewXMLEncoder(2, XMLPreferences)), s.description)
|
||||||
case "roundtrip":
|
case "roundtrip":
|
||||||
test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder("+", "+content", false, false, false, false, false), NewXMLEncoder(2, "+", "+content")), s.description)
|
test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder(XMLPreferences), NewXMLEncoder(2, XMLPreferences)), s.description)
|
||||||
case "decode-keep-ns":
|
case "decode-keep-ns":
|
||||||
test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder("+", "+content", false, true, false, false, false), NewYamlEncoder(2, false, true, true)), s.description)
|
prefs := NewDefaultXmlPreferences()
|
||||||
|
prefs.KeepNamespace = true
|
||||||
|
test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder(prefs), NewYamlEncoder(2, false, true, true)), s.description)
|
||||||
case "decode-raw-token":
|
case "decode-raw-token":
|
||||||
test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder("+", "+content", false, true, true, false, false), NewYamlEncoder(2, false, true, true)), s.description)
|
prefs := NewDefaultXmlPreferences()
|
||||||
case "encode-":
|
prefs.UseRawToken = true
|
||||||
test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder("+", "+content", false, true, true, true, true), NewYamlEncoder(2, false, true, true)), s.description)
|
test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder(prefs), NewYamlEncoder(2, false, true, true)), s.description)
|
||||||
|
case "roundtrip-skip-directives":
|
||||||
|
prefs := NewDefaultXmlPreferences()
|
||||||
|
prefs.SkipDirectives = true
|
||||||
|
test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder(prefs), NewXMLEncoder(2, prefs)), s.description)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unhandled scenario type %q", s.scenarioType))
|
panic(fmt.Sprintf("unhandled scenario type %q", s.scenarioType))
|
||||||
}
|
}
|
||||||
@ -443,6 +452,8 @@ func documentXMLScenario(t *testing.T, w *bufio.Writer, i interface{}) {
|
|||||||
documentXMLDecodeKeepNsScenario(w, s)
|
documentXMLDecodeKeepNsScenario(w, s)
|
||||||
case "decode-raw-token":
|
case "decode-raw-token":
|
||||||
documentXMLDecodeKeepNsRawTokenScenario(w, s)
|
documentXMLDecodeKeepNsRawTokenScenario(w, s)
|
||||||
|
case "roundtrip-skip-directives":
|
||||||
|
documentXMLSkipDirectrivesScenario(w, s)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unhandled scenario type %q", s.scenarioType))
|
panic(fmt.Sprintf("unhandled scenario type %q", s.scenarioType))
|
||||||
@ -468,7 +479,7 @@ func documentXMLDecodeScenario(w *bufio.Writer, s formatScenario) {
|
|||||||
writeOrPanic(w, fmt.Sprintf("```bash\nyq -p=xml '%v' sample.xml\n```\n", expression))
|
writeOrPanic(w, fmt.Sprintf("```bash\nyq -p=xml '%v' sample.xml\n```\n", expression))
|
||||||
writeOrPanic(w, "will output\n")
|
writeOrPanic(w, "will output\n")
|
||||||
|
|
||||||
writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder("+", "+content", false, false, false), NewYamlEncoder(2, false, true, true))))
|
writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder(XMLPreferences), NewYamlEncoder(2, false, true, true))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func documentXMLDecodeKeepNsScenario(w *bufio.Writer, s formatScenario) {
|
func documentXMLDecodeKeepNsScenario(w *bufio.Writer, s formatScenario) {
|
||||||
@ -485,11 +496,14 @@ func documentXMLDecodeKeepNsScenario(w *bufio.Writer, s formatScenario) {
|
|||||||
writeOrPanic(w, "then\n")
|
writeOrPanic(w, "then\n")
|
||||||
writeOrPanic(w, "```bash\nyq -p=xml -o=xml --xml-keep-namespace '.' sample.xml\n```\n")
|
writeOrPanic(w, "```bash\nyq -p=xml -o=xml --xml-keep-namespace '.' sample.xml\n```\n")
|
||||||
writeOrPanic(w, "will output\n")
|
writeOrPanic(w, "will output\n")
|
||||||
|
prefs := NewDefaultXmlPreferences()
|
||||||
|
prefs.KeepNamespace = true
|
||||||
|
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder(prefs), NewXMLEncoder(2, prefs))))
|
||||||
|
|
||||||
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder("+", "+content", false, true, false, false, false), NewXMLEncoder(2, "+", "+content"))))
|
prefsWithout := NewDefaultXmlPreferences()
|
||||||
|
prefs.KeepNamespace = false
|
||||||
writeOrPanic(w, "instead of\n")
|
writeOrPanic(w, "instead of\n")
|
||||||
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder("+", "+content", false, false, false, false, false), NewXMLEncoder(2, "+", "+content"))))
|
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder(prefsWithout), NewXMLEncoder(2, prefsWithout))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func documentXMLDecodeKeepNsRawTokenScenario(w *bufio.Writer, s formatScenario) {
|
func documentXMLDecodeKeepNsRawTokenScenario(w *bufio.Writer, s formatScenario) {
|
||||||
@ -507,10 +521,16 @@ func documentXMLDecodeKeepNsRawTokenScenario(w *bufio.Writer, s formatScenario)
|
|||||||
writeOrPanic(w, "```bash\nyq -p=xml -o=xml --xml-keep-namespace --xml-raw-token '.' sample.xml\n```\n")
|
writeOrPanic(w, "```bash\nyq -p=xml -o=xml --xml-keep-namespace --xml-raw-token '.' sample.xml\n```\n")
|
||||||
writeOrPanic(w, "will output\n")
|
writeOrPanic(w, "will output\n")
|
||||||
|
|
||||||
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder("+", "+content", false, true, true, false, false), NewXMLEncoder(2, "+", "+content"))))
|
prefs := NewDefaultXmlPreferences()
|
||||||
|
prefs.KeepNamespace = true
|
||||||
|
|
||||||
|
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder(prefs), NewXMLEncoder(2, prefs))))
|
||||||
|
|
||||||
|
prefsWithout := NewDefaultXmlPreferences()
|
||||||
|
prefsWithout.KeepNamespace = false
|
||||||
|
|
||||||
writeOrPanic(w, "instead of\n")
|
writeOrPanic(w, "instead of\n")
|
||||||
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder("+", "+content", false, false, false, false, false), NewXMLEncoder(2, "+", "+content"))))
|
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder(prefsWithout), NewXMLEncoder(2, prefsWithout))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func documentXMLEncodeScenario(w *bufio.Writer, s formatScenario) {
|
func documentXMLEncodeScenario(w *bufio.Writer, s formatScenario) {
|
||||||
@ -528,7 +548,7 @@ func documentXMLEncodeScenario(w *bufio.Writer, s formatScenario) {
|
|||||||
writeOrPanic(w, "```bash\nyq -o=xml '.' sample.yml\n```\n")
|
writeOrPanic(w, "```bash\nyq -o=xml '.' sample.yml\n```\n")
|
||||||
writeOrPanic(w, "will output\n")
|
writeOrPanic(w, "will output\n")
|
||||||
|
|
||||||
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewYamlDecoder(), NewXMLEncoder(2, "+", "+content"))))
|
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewYamlDecoder(), NewXMLEncoder(2, XMLPreferences))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func documentXMLRoundTripScenario(w *bufio.Writer, s formatScenario) {
|
func documentXMLRoundTripScenario(w *bufio.Writer, s formatScenario) {
|
||||||
@ -546,7 +566,27 @@ func documentXMLRoundTripScenario(w *bufio.Writer, s formatScenario) {
|
|||||||
writeOrPanic(w, "```bash\nyq -p=xml -o=xml '.' sample.xml\n```\n")
|
writeOrPanic(w, "```bash\nyq -p=xml -o=xml '.' sample.xml\n```\n")
|
||||||
writeOrPanic(w, "will output\n")
|
writeOrPanic(w, "will output\n")
|
||||||
|
|
||||||
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder("+", "+content", false, false, false, false, false), NewXMLEncoder(2, "+", "+content"))))
|
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder(XMLPreferences), NewXMLEncoder(2, XMLPreferences))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func documentXMLSkipDirectrivesScenario(w *bufio.Writer, s formatScenario) {
|
||||||
|
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.input))
|
||||||
|
|
||||||
|
writeOrPanic(w, "then\n")
|
||||||
|
writeOrPanic(w, "```bash\nyq -p=xml -o=xml --xml-skip-directives '.' sample.xml\n```\n")
|
||||||
|
writeOrPanic(w, "will output\n")
|
||||||
|
prefs := NewDefaultXmlPreferences()
|
||||||
|
prefs.SkipDirectives = true
|
||||||
|
|
||||||
|
writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder(prefs), NewXMLEncoder(2, prefs))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestXMLScenarios(t *testing.T) {
|
func TestXMLScenarios(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user