7dc695752b
- Deleted the `github.com/google/uuid` dependency from `go.mod` to streamline the project and reduce unnecessary dependencies. - Updated `go.sum` to reflect the removal of the UUID package. - Enhanced the overall module management by ensuring only necessary dependencies are included.
159 lines
4.0 KiB
Go
159 lines
4.0 KiB
Go
package xmlparser
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// ValidationError represents a validation error with details
|
|
type ValidationError struct {
|
|
Field string `json:"field"`
|
|
Message string `json:"message"`
|
|
Value string `json:"value,omitempty"`
|
|
}
|
|
|
|
func (ve ValidationError) Error() string {
|
|
if ve.Value != "" {
|
|
return fmt.Sprintf("validation error in field '%s': %s (value: %s)", ve.Field, ve.Message, ve.Value)
|
|
}
|
|
return fmt.Sprintf("validation error in field '%s': %s", ve.Field, ve.Message)
|
|
}
|
|
|
|
// ValidationErrors represents multiple validation errors
|
|
type ValidationErrors []ValidationError
|
|
|
|
func (ve ValidationErrors) Error() string {
|
|
if len(ve) == 0 {
|
|
return "no validation errors"
|
|
}
|
|
|
|
var messages []string
|
|
for _, err := range ve {
|
|
messages = append(messages, err.Error())
|
|
}
|
|
return fmt.Sprintf("validation failed with %d error(s): %s", len(ve), strings.Join(messages, "; "))
|
|
}
|
|
|
|
// IsEmpty returns true if there are no validation errors
|
|
func (ve ValidationErrors) IsEmpty() bool {
|
|
return len(ve) == 0
|
|
}
|
|
|
|
// Add adds a validation error
|
|
func (ve *ValidationErrors) Add(field, message, value string) {
|
|
*ve = append(*ve, ValidationError{
|
|
Field: field,
|
|
Message: message,
|
|
Value: value,
|
|
})
|
|
}
|
|
|
|
// XMLParseError represents an XML parsing error with context
|
|
type XMLParseError struct {
|
|
Line int `json:"line,omitempty"`
|
|
Column int `json:"column,omitempty"`
|
|
Element string `json:"element,omitempty"`
|
|
Message string `json:"message"`
|
|
Original error `json:"-"`
|
|
}
|
|
|
|
func (xpe XMLParseError) Error() string {
|
|
var parts []string
|
|
|
|
if xpe.Element != "" {
|
|
parts = append(parts, fmt.Sprintf("element '%s'", xpe.Element))
|
|
}
|
|
|
|
if xpe.Line > 0 {
|
|
if xpe.Column > 0 {
|
|
parts = append(parts, fmt.Sprintf("line %d, column %d", xpe.Line, xpe.Column))
|
|
} else {
|
|
parts = append(parts, fmt.Sprintf("line %d", xpe.Line))
|
|
}
|
|
}
|
|
|
|
context := ""
|
|
if len(parts) > 0 {
|
|
context = " (" + strings.Join(parts, ", ") + ")"
|
|
}
|
|
|
|
return fmt.Sprintf("XML parse error%s: %s", context, xpe.Message)
|
|
}
|
|
|
|
// Unwrap returns the original error
|
|
func (xpe XMLParseError) Unwrap() error {
|
|
return xpe.Original
|
|
}
|
|
|
|
// SanitizeXMLString removes invalid XML characters and cleans the string
|
|
func SanitizeXMLString(s string) string {
|
|
// Remove control characters except tab, newline, and carriage return
|
|
var result strings.Builder
|
|
for _, r := range s {
|
|
if (r >= 0x20 && r <= 0xD7FF) ||
|
|
(r >= 0xE000 && r <= 0xFFFD) ||
|
|
(r >= 0x10000 && r <= 0x10FFFF) ||
|
|
r == 0x09 || r == 0x0A || r == 0x0D {
|
|
result.WriteRune(r)
|
|
}
|
|
}
|
|
return result.String()
|
|
}
|
|
|
|
// ValidateXMLName checks if a string is a valid XML name
|
|
func ValidateXMLName(name string) bool {
|
|
if name == "" {
|
|
return false
|
|
}
|
|
|
|
// XML names must start with a letter or underscore
|
|
first := []rune(name)[0]
|
|
if !((first >= 'A' && first <= 'Z') ||
|
|
(first >= 'a' && first <= 'z') ||
|
|
first == '_') {
|
|
return false
|
|
}
|
|
|
|
// Subsequent characters can be letters, digits, hyphens, periods, or underscores
|
|
for _, r := range []rune(name)[1:] {
|
|
if !((r >= 'A' && r <= 'Z') ||
|
|
(r >= 'a' && r <= 'z') ||
|
|
(r >= '0' && r <= '9') ||
|
|
r == '-' || r == '.' || r == '_') {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// IsValidNamespace checks if a string is a valid XML namespace URI
|
|
func IsValidNamespace(namespace string) bool {
|
|
if namespace == "" {
|
|
return true // Empty namespace is valid
|
|
}
|
|
|
|
// Basic URI validation - should start with http:// or https:// or urn:
|
|
return strings.HasPrefix(namespace, "http://") ||
|
|
strings.HasPrefix(namespace, "https://") ||
|
|
strings.HasPrefix(namespace, "urn:")
|
|
}
|
|
|
|
// NormalizeWhitespace normalizes whitespace in XML text content
|
|
func NormalizeWhitespace(s string) string {
|
|
// Replace all whitespace sequences with single spaces
|
|
fields := strings.Fields(s)
|
|
return strings.Join(fields, " ")
|
|
}
|
|
|
|
// TruncateString truncates a string to maxLength and adds "..." if needed
|
|
func TruncateString(s string, maxLength int) string {
|
|
if len(s) <= maxLength {
|
|
return s
|
|
}
|
|
if maxLength <= 3 {
|
|
return s[:maxLength]
|
|
}
|
|
return s[:maxLength-3] + "..."
|
|
}
|