Home > Article > Backend Development > Golang xml key name conflict
I need to generate a complex xml file in order to load it into another system. Therefore, tags and tag order cannot be changed as they must follow a precise format to load correctly. I'm currently using the encoding/xml package in golang to try to accomplish this.
The problem I'm having is that I can't have two tags with the same name. I get the following error: main.XMLDict Field "Key1" with label "key" conflicts with field "Key2" with label "key". Below is my simplified code:
import ( "fmt" "encoding/xml" "os" ) type XMLDict struct { XMLName xml.Name `xml:"dict"` Key1 string `xml:"key"` Data1 string `xml:"data"` Key2 string `xml:"key"` StringArray XMLStringArray } type XMLStringArray struct { XMLName xml.Name `xml:"array"̀` XMLString []string `xml:"string"` } func main() { sa := make([]string, 3) sa[0] = "g" sa[1] = "h" sa[2] = "i" arr := XMLStringArray{ XMLString: sa, } master := XMLDict{ Key1: "Color", Data1: "Random data", Key2: "Curve", StringArray: arr, } output, err := xml.MarshalIndent(master, " ", " ") if err != nil { fmt.Printf("error: %v\n", err) } os.Stdout.Write(output) return }
Here is an example of the XML file I need to generate:
<dict> <key>Color</key> <data> BAtzdHJlYW10eXBlZIHoA4QBQISEhAdOU0NvbG9yAISECE5TT2Jq ZWN0AIWEAWMChARmZmZmg7oehT2DZmYmP4NmZiY/AYY= </data> <key>Curve</key> <dict> <key>Interpolation</key> <integer>5</integer> <key>Points</key> <array> <string>{0, 0}</string> <string>{0.05, 0.053871}</string> <string>{0.1, 0.110555}</string> <string>{0.15, 0.166793}</string> <string>{0.2, 0.216919}</string> <string>{0.3, 0.327703}</string> <string>{0.4, 0.440897}</string> <string>{0.5, 0.539322}</string> <string>{0.6, 0.657477}</string> <string>{0.7, 0.763339}</string> <string>{0.75, 0.814082}</string> <string>{0.8, 0.861097}</string> <string>{0.85, 0.904147}</string> <string>{0.9, 0.944079}</string> <string>{0.95, 0.974036}</string> <string>{0.98, 0.990085}</string> <string>{1, 1}</string> </array> </dict> <key>Line Width</key> <real>0.0040000001899898052</real> <key>Points Diameter</key> <real>0.014999999664723873</real> <key>Precision</key> <real>9.9999997473787516e-05</real> </dict>
So, my questions: 1) Is there an easy way to solve this problem, still using the structural approach of building the data and then generating the XML, 2) Is there a better way to build and generate this file than using Stuts or 3) Am I just doing something wrong?
EDIT: Below is the requested input file:
"File created by Curve" LGOROWLENGTH 3 # # Curve4 Run Information # # Run: Run 1 - Calibration # Based On: <none> # # Values in this file are 'Wanted' # # Gray Balance: On # reduced after 100% # until 100% # media white values: Measured # media aim points: -0.17 / 0.96 # NUMBER_OF_FIELDS 6 BEGIN_DATA_FORMAT SampleID SAMPLE_NAME CMYK_C CMYK_M CMYK_Y CMYK_K END_DATA_FORMAT NUMBER_OF_SETS 17 BEGIN_DATA A0 "0.00" 0.0000 0.0000 0.0000 0.0000 A1 "5.00" 5.3871 5.1408 5.2761 5.5775 A2 "10.00" 11.0555 10.7418 10.6026 11.0702 A3 "15.00" 16.6793 16.3894 15.8763 16.7064 A4 "20.00" 21.6919 21.7968 21.0234 22.3956 A5 "30.00" 32.7703 32.7458 31.1841 33.6738 A6 "40.00" 44.0897 42.4801 40.0887 44.2966 A7 "50.00" 53.9322 54.2364 49.1680 54.8968 A8 "60.00" 65.7477 64.9423 59.1719 66.4696 A9 "70.00" 76.3339 75.4398 69.4943 77.1661 A10 "75.00" 81.4082 80.5957 75.1423 82.0637 A11 "80.00" 86.1097 84.9296 81.1298 86.7399 A12 "85.00" 90.4147 88.8039 86.7945 90.8142 A13 "90.00" 94.4079 92.6783 92.1110 94.2426 A14 "95.00" 97.4036 96.2097 96.6019 97.2088 A15 "98.00" 99.0085 98.5775 98.7747 98.9000 A16 "100.00" 100.0000 100.0000 100.0000 100.0000 END_DATA
I think there are many solutions to achieve this using XML custom marshaler; this is my attempt.
First of all, in your root 009c9cf71eed486d4d9be08f8b55d0a7
it always seems to be a "list" of items like this:
<key>a-string</key> <data|integer|real|array|dict>a-value</data|integer|real|array|dict> <key>another-string</key> <data|integer|real|array|dict>another-value</data|integer|real|array|dict>
To represent each of these, we can define a KeyValue
structure, where Value
is just an interface to represent anything it can hold (data
, integer
,...)
type KeyValue struct { Key string Value Value } type Value interface { getXMLName() string // this will return data, integer, array, etc, depending on the implementation }
We can then implement Value
using the different types provided in the example:
type ValueData struct { Data string `xml:",chardata"` } func (v ValueData) getXMLName() string { return "data" } type ValueInteger struct { Integer int `xml:",chardata"` } func (v ValueInteger) getXMLName() string { return "integer" } type ValueStringArray struct { Array []string `xml:"string"` } func (v ValueStringArray) getXMLName() string { return "array" } type ValueReal struct { Real float64 `xml:",chardata"` } func (v ValueReal) getXMLName() string { return "real" }
Within the XML tag, chardata
is used to avoid including extra levels in our XML document; for ValueStringArray
, we use string
because your The project is named this way.
Finally, to allow recursion (dict
internal dict
), we can also define this structure:
type ValueDict struct { XMLName xml.Name `xml:"dict"` KeysValues []KeyValue } func (v ValueDict) getXMLName() string { return "dict" }
After completing all operations, we can implement a custom marshaler:
func (kv KeyValue) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { if err := e.EncodeElement(kv.Key, xml.StartElement{Name: xml.Name{Local: "key"}}); err != nil { return err } if err := e.EncodeElement(kv.Value, xml.StartElement{Name: xml.Name{Local: kv.Value.getXMLName()}}); err != nil { return err } return nil }
This will allow the generation of projects like this:
<key>Line Width</key> <real>0.004000000189989805</real>The name of the
tag (here real
) is based on what is returned by the getXMLName()
method.
Finally, you can define Go structs and marshals like this:
master := ValueDict{ KeysValues: []KeyValue{ { Key: "Color", Value: ValueData{ Data: "BAtzdHJlYW10eXBlZIHoA4QBQISEhAdOU0NvbG9yAISECE5TT2JqZWN0AIWEAWMChARmZmZmg7oehT2DZmYmP4NmZiY/AYY=", }, }, { Key: "Curve", Value: ValueDict{ KeysValues: []KeyValue{ { Key: "Interpolation", Value: ValueInteger{ Integer: 5, }, }, { Key: "Points", Value: ValueStringArray{ Array: []string{ "{0, 0}", "{0.05, 0.053871}", "{0.1, 0.110555}", "{0.15, 0.166793}", "{0.2, 0.216919}", "{0.3, 0.327703}", "{0.4, 0.440897}", "{0.5, 0.539322}", "{0.6, 0.657477}", "{0.7, 0.763339}", "{0.75, 0.814082}", "{0.8, 0.861097}", "{0.85, 0.904147}", "{0.9, 0.944079}", "{0.95, 0.974036}", "{0.98, 0.990085}", "{1, 1}", }, }, }, }, }, }, { Key: "Line Width", Value: ValueReal{ Real: 0.0040000001899898052, }, }, { Key: "Points Diameter", Value: ValueReal{ Real: 0.014999999664723873, }, }, { Key: "Precision", Value: ValueReal{ Real: 9.9999997473787516e-05, }, }, }, } output, err := xml.MarshalIndent(master, " ", " ") if err != nil { fmt.Printf("error: %v\n", err) } os.Stdout.Write(output)
This will print exactly what is in the sample XML provided in your question.
You can test the complete code in the Go Playground: https://go.dev/play/p /k8cEIywx3UB.
The main advantages of this solution are:
ValueBool
implementation Value
) The above is the detailed content of Golang xml key name conflict. For more information, please follow other related articles on the PHP Chinese website!