package xml
import (
)
type typeInfo struct {
xmlname *fieldInfo
fields []fieldInfo
}
type fieldInfo struct {
idx []int
name string
xmlns string
flags fieldFlags
parents []string
}
type fieldFlags int
const (
fElement fieldFlags = 1 << iota
fAttr
fCDATA
fCharData
fInnerXML
fComment
fAny
fOmitEmpty
fMode = fElement | fAttr | fCDATA | fCharData | fInnerXML | fComment | fAny
xmlName = "XMLName"
)
var tinfoMap sync.Map
var nameType = reflect.TypeOf(Name{})
func ( reflect.Type) (*typeInfo, error) {
if , := tinfoMap.Load(); {
return .(*typeInfo), nil
}
:= &typeInfo{}
if .Kind() == reflect.Struct && != nameType {
:= .NumField()
for := 0; < ; ++ {
:= .Field()
if (!.IsExported() && !.Anonymous) || .Tag.Get("xml") == "-" {
continue
}
if .Anonymous {
:= .Type
if .Kind() == reflect.Pointer {
= .Elem()
}
if .Kind() == reflect.Struct {
, := ()
if != nil {
return nil,
}
if .xmlname == nil {
.xmlname = .xmlname
}
for , := range .fields {
.idx = append([]int{}, .idx...)
if := addFieldInfo(, , &); != nil {
return nil,
}
}
continue
}
}
, := structFieldInfo(, &)
if != nil {
return nil,
}
if .Name == xmlName {
.xmlname =
continue
}
if := addFieldInfo(, , ); != nil {
return nil,
}
}
}
, := tinfoMap.LoadOrStore(, )
return .(*typeInfo), nil
}
func ( reflect.Type, *reflect.StructField) (*fieldInfo, error) {
:= &fieldInfo{idx: .Index}
:= .Tag.Get("xml")
if , , := strings.Cut(, " "); {
.xmlns, = ,
}
:= strings.Split(, ",")
if len() == 1 {
.flags = fElement
} else {
= [0]
for , := range [1:] {
switch {
case "attr":
.flags |= fAttr
case "cdata":
.flags |= fCDATA
case "chardata":
.flags |= fCharData
case "innerxml":
.flags |= fInnerXML
case "comment":
.flags |= fComment
case "any":
.flags |= fAny
case "omitempty":
.flags |= fOmitEmpty
}
}
:= true
switch := .flags & fMode; {
case 0:
.flags |= fElement
case fAttr, fCDATA, fCharData, fInnerXML, fComment, fAny, fAny | fAttr:
if .Name == xmlName || != "" && != fAttr {
= false
}
default:
= false
}
if .flags&fMode == fAny {
.flags |= fElement
}
if .flags&fOmitEmpty != 0 && .flags&(fElement|fAttr) == 0 {
= false
}
if ! {
return nil, fmt.Errorf("xml: invalid tag in field %s of type %s: %q",
.Name, , .Tag.Get("xml"))
}
}
if .xmlns != "" && == "" {
return nil, fmt.Errorf("xml: namespace without name in field %s of type %s: %q",
.Name, , .Tag.Get("xml"))
}
if .Name == xmlName {
.name =
return , nil
}
if == "" {
if := lookupXMLName(.Type); != nil {
.xmlns, .name = .xmlns, .name
} else {
.name = .Name
}
return , nil
}
:= strings.Split(, ">")
if [0] == "" {
[0] = .Name
}
if [len()-1] == "" {
return nil, fmt.Errorf("xml: trailing '>' in field %s of type %s", .Name, )
}
.name = [len()-1]
if len() > 1 {
if (.flags & fElement) == 0 {
return nil, fmt.Errorf("xml: %s chain not valid with %s flag", , strings.Join([1:], ","))
}
.parents = [:len()-1]
}
if .flags&fElement != 0 {
:= .Type
:= lookupXMLName()
if != nil && .name != .name {
return nil, fmt.Errorf("xml: name %q in tag of %s.%s conflicts with name %q in %s.XMLName",
.name, , .Name, .name, )
}
}
return , nil
}
func ( reflect.Type) ( *fieldInfo) {
for .Kind() == reflect.Pointer {
= .Elem()
}
if .Kind() != reflect.Struct {
return nil
}
for , := 0, .NumField(); < ; ++ {
:= .Field()
if .Name != xmlName {
continue
}
, := structFieldInfo(, &)
if == nil && .name != "" {
return
}
break
}
return nil
}
func (, int) int {
if <= {
return
}
return
}
func ( reflect.Type, *typeInfo, *fieldInfo) error {
var []int
:
for := range .fields {
:= &.fields[]
if .flags&fMode != .flags&fMode {
continue
}
if .xmlns != "" && .xmlns != "" && .xmlns != .xmlns {
continue
}
:= min(len(.parents), len(.parents))
for := 0; < ; ++ {
if .parents[] != .parents[] {
continue
}
}
if len(.parents) > len(.parents) {
if .parents[len(.parents)] == .name {
= append(, )
}
} else if len(.parents) < len(.parents) {
if .parents[len(.parents)] == .name {
= append(, )
}
} else {
if .name == .name {
= append(, )
}
}
}
if == nil {
.fields = append(.fields, *)
return nil
}
for , := range {
if len(.fields[].idx) < len(.idx) {
return nil
}
}
for , := range {
:= &.fields[]
if len(.idx) == len(.idx) {
:= .FieldByIndex(.idx)
:= .FieldByIndex(.idx)
return &TagPathError{, .Name, .Tag.Get("xml"), .Name, .Tag.Get("xml")}
}
}
for := len() - 1; >= 0; -- {
:= []
copy(.fields[:], .fields[+1:])
.fields = .fields[:len(.fields)-1]
}
.fields = append(.fields, *)
return nil
}
type TagPathError struct {
Struct reflect.Type
Field1, Tag1 string
Field2, Tag2 string
}
func ( *TagPathError) () string {
return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", .Struct, .Field1, .Tag1, .Field2, .Tag2)
}
const (
initNilPointers = true
dontInitNilPointers = false
)
func ( *fieldInfo) ( reflect.Value, bool) reflect.Value {
for , := range .idx {
if > 0 {
:= .Type()
if .Kind() == reflect.Pointer && .Elem().Kind() == reflect.Struct {
if .IsNil() {
if ! {
return reflect.Value{}
}
.Set(reflect.New(.Type().Elem()))
}
= .Elem()
}
}
= .Field()
}
return
}