Files
ofdgo/ofdgo_xml.go
T
2026-10-12 11:20:30 +08:00

135 lines
4.0 KiB
Go

// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ofdgo
import (
"encoding/xml"
"unicode/utf8"
)
// 旧版与2016版OFD命名空间
const (
ofdNamespaceLegacy = "http://www.ofdspec.org"
ofdNamespace2016 = "http://www.ofdspec.org/2016"
)
// 未声明、未知、旧版及2016版OFD命名空间,不限制已知内容的读取
const (
ofdXMLUnqualified ofdXMLNamespace = iota
ofdXMLUnknown
ofdXMLLegacy
ofdXML2016
)
// ofdXMLNamespace 标识XML命名空间,不代表文档版本或功能支持范围
type ofdXMLNamespace uint8
// ofdXMLChild 判断子节点是否属于可识别的OFD命名空间或父节点所在的未知命名空间
// 入参: parent 父节点名称, child 子节点名称
// 返回: bool 是否属于同一OFD结构
func ofdXMLChild(parent, child xml.Name) bool {
return classifyOFDNamespace(child.Space) != ofdXMLUnknown || child.Space == parent.Space
}
// ofdXMLStart 保留OFD节点的无命名空间属性,不修改解码器返回的属性切片
// 入参: start 起始节点
// 返回: xml.StartElement 供标准结构解码使用的节点
func ofdXMLStart(start xml.StartElement) xml.StartElement {
for i, attr := range start.Attr {
if attr.Name.Space == "" {
continue
}
attrs := make([]xml.Attr, i, len(start.Attr)-1)
copy(attrs, start.Attr[:i])
for _, attr := range start.Attr[i+1:] {
if attr.Name.Space == "" {
attrs = append(attrs, attr)
}
}
start.Attr = attrs
break
}
return start
}
// decodeOFDChildren 按命名空间解析直接子节点,跳过外部扩展而不提升其内部节点
// 入参: d XML解码器, start 容器起始节点, decode 子节点解码函数
// 返回: error 解析错误
func decodeOFDChildren(d *xml.Decoder, start xml.StartElement, decode func(*xml.Decoder, xml.StartElement) error) error {
for {
token, err := d.Token()
if err != nil {
return err
}
switch node := token.(type) {
case xml.StartElement:
if !ofdXMLChild(start.Name, node.Name) {
if err := d.Skip(); err != nil {
return err
}
continue
}
if err := decode(d, ofdXMLStart(node)); err != nil {
return err
}
case xml.EndElement:
if node.Name == start.Name {
return nil
}
}
}
}
// classifyOFDNamespace 识别OFD命名空间,未声明与未知版本分别处理
// 入参: namespace XML命名空间
// 返回: ofdXMLNamespace 命名空间类别
func classifyOFDNamespace(namespace string) ofdXMLNamespace {
switch namespace {
case "":
return ofdXMLUnqualified
case ofdNamespaceLegacy:
return ofdXMLLegacy
case ofdNamespace2016:
return ofdXML2016
default:
return ofdXMLUnknown
}
}
// ofdXMLIDValid 按XML 1.0的NCName规则校验xs:ID,不用于数值型ST_ID
// 入参: id 标识
// 返回: bool 是否有效
func ofdXMLIDValid(id string) bool {
if id == "" || !utf8.ValidString(id) {
return false
}
for i, c := range id {
if c == '_' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' ||
c >= 0xC0 && c <= 0xD6 || c >= 0xD8 && c <= 0xF6 ||
c >= 0xF8 && c <= 0x2FF || c >= 0x370 && c <= 0x37D ||
c >= 0x37F && c <= 0x1FFF || c >= 0x200C && c <= 0x200D ||
c >= 0x2070 && c <= 0x218F || c >= 0x2C00 && c <= 0x2FEF ||
c >= 0x3001 && c <= 0xD7FF || c >= 0xF900 && c <= 0xFDCF ||
c >= 0xFDF0 && c <= 0xFFFD || c >= 0x10000 && c <= 0xEFFFF {
continue
}
if i == 0 || !(c == '-' || c == '.' || c >= '0' && c <= '9' ||
c == 0xB7 || c >= 0x300 && c <= 0x36F || c >= 0x203F && c <= 0x2040) {
return false
}
}
return true
}