in testing/xml/xmlToStruct.go [209:339]
func sortFlattenedNodes(nodes []*XMLNode) ([]*XMLNode, error) {
var sortedNodes []*XMLNode
// concreteNodeMap stores concrete value associated with a list of nodes
// This is possible in case multiple members of a flatList has same values.
concreteNodeMap := make(map[string][]*XMLNode, 0)
// flatListNodeMap stores flat list or wrapped list members associated with a list of nodes
// This will have only flattened list with members that are Nodes and not concrete values.
flatListNodeMap := make(map[string][]*XMLNode, 0)
// flatMapNodeMap stores flat map or map entry members associated with a list of nodes
// This will have only flattened map concrete value members. It is possible to limit this
// to concrete value as map key is expected to be concrete.
flatMapNodeMap := make(map[string][]*XMLNode, 0)
// nodes with concrete value are prioritized and appended based on sorting order
sortedNodesWithConcreteValue := []string{}
// list with nested nodes are second in priority and appended based on sorting order
sortedNodesWithListValue := []string{}
// map are last in priority and appended based on sorting order
sortedNodesWithMapValue := []string{}
for _, node := range nodes {
// node has no children element, then we consider it as having concrete value
if len(node.Children) == 0 {
sortedNodesWithConcreteValue = append(sortedNodesWithConcreteValue, node.Text)
if v, ok := concreteNodeMap[node.Text]; ok {
concreteNodeMap[node.Text] = append(v, node)
} else {
concreteNodeMap[node.Text] = []*XMLNode{node}
}
}
// if node has a single child, then it is a flattened list node
if len(node.Children) == 1 {
for _, nestedNodes := range node.Children {
nestedNodeName := nestedNodes[0].Name.Local
// append to sorted node name for list value
sortedNodesWithListValue = append(sortedNodesWithListValue, nestedNodeName)
if v, ok := flatListNodeMap[nestedNodeName]; ok {
flatListNodeMap[nestedNodeName] = append(v, nestedNodes[0])
} else {
flatListNodeMap[nestedNodeName] = []*XMLNode{nestedNodes[0]}
}
}
}
// if node has two children, then it is a flattened map node
if len(node.Children) == 2 {
nestedPair := []*XMLNode{}
for _, k := range node.Children {
nestedPair = append(nestedPair, k[0])
}
comparableValues := []string{nestedPair[0].Name.Local, nestedPair[1].Name.Local}
sort.Strings(comparableValues)
comparableValue := comparableValues[0]
for _, nestedNode := range nestedPair {
if comparableValue == nestedNode.Name.Local && len(nestedNode.Children) != 0 {
// if value was selected and is nested node, skip it and use key instead
comparableValue = comparableValues[1]
continue
}
// now we are certain there is no nested node
if comparableValue == nestedNode.Name.Local {
// get chardata for comparison
comparableValue = nestedNode.Text
sortedNodesWithMapValue = append(sortedNodesWithMapValue, comparableValue)
if v, ok := flatMapNodeMap[comparableValue]; ok {
flatMapNodeMap[comparableValue] = append(v, node)
} else {
flatMapNodeMap[comparableValue] = []*XMLNode{node}
}
break
}
}
}
// we don't support multiple same name nodes in an xml doc except for in flattened maps, list.
if len(node.Children) > 2 {
return nodes, fmt.Errorf("malformed xml: multiple nodes with same key name exist, " +
"but are not associated with flattened maps (2 children) or list (0 or 1 child)")
}
}
// sort concrete value node name list and append corresponding nodes
// to sortedNodes
sort.Strings(sortedNodesWithConcreteValue)
for _, name := range sortedNodesWithConcreteValue {
for _, node := range concreteNodeMap[name] {
sortedNodes = append(sortedNodes, node)
}
}
// sort nested nodes with a list and append corresponding nodes
// to sortedNodes
sort.Strings(sortedNodesWithListValue)
for _, name := range sortedNodesWithListValue {
// if two nested nodes have same name, then sort them separately.
if len(flatListNodeMap[name]) > 1 {
// return nodes, fmt.Errorf("flat list node name are %s %v", flatListNodeMap[name][0].Name.Local, len(flatListNodeMap[name]))
nestedFlattenedList, err := sortFlattenedNodes(flatListNodeMap[name])
if err != nil {
return nodes, err
}
// append the identical but sorted nodes
for _, nestedNode := range nestedFlattenedList {
sortedNodes = append(sortedNodes, nestedNode)
}
} else {
// append the sorted nodes
sortedNodes = append(sortedNodes, flatListNodeMap[name][0])
}
}
// sorted nodes with a map and append corresponding nodes to sortedNodes
sort.Strings(sortedNodesWithMapValue)
for _, name := range sortedNodesWithMapValue {
sortedNodes = append(sortedNodes, flatMapNodeMap[name][0])
}
return sortedNodes, nil
}