Remove github.com/google/uuid dependency and update module files
- 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.
This commit is contained in:
@@ -0,0 +1,307 @@
|
||||
# Generic XML Parser Package
|
||||
|
||||
This package is a generic XML parser implemented using Go Generics that can parse any XML structure into Go structs.
|
||||
|
||||
## Features
|
||||
|
||||
- **Generic**: Support for any struct type that implements the `Parseable` interface
|
||||
- **Flexible**: Configurable parsing options for different use cases
|
||||
- **Validation**: Automatic validation of XML and parsed structures
|
||||
- **Metadata Extraction**: Extract XML metadata and parsing information
|
||||
- **Advanced Error Handling**: Detailed error and warning reporting
|
||||
- **TED XML Support**: Pre-built models for TED XML formats
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
go mod tidy
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Parse XML from URL
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"tm/pkg/xmlparser"
|
||||
"tm/pkg/xmlparser/ted"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create parser for Contract Notice
|
||||
parser := xmlparser.NewParser[ted.ContractNotice](nil)
|
||||
|
||||
// Fetch XML from TED website
|
||||
resp, err := http.Get("https://ted.europa.eu/en/notice/447983-2025/xml")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Parse XML
|
||||
notice, err := parser.Parse(resp.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Notice ID: %s\n", notice.GetID())
|
||||
fmt.Printf("Notice Type: %s\n", notice.GetNoticeType())
|
||||
}
|
||||
```
|
||||
|
||||
### Parse XML from String
|
||||
|
||||
```go
|
||||
xmlData := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ContractNotice xmlns="urn:oasis:names:specification:ubl:schema:xsd:ContractNotice-2">
|
||||
<ID>sample-notice-123</ID>
|
||||
<ContractFolderID>sample-folder-456</ContractFolderID>
|
||||
<IssueDate>2025-01-09+02:00</IssueDate>
|
||||
<NoticeTypeCode>cn-standard</NoticeTypeCode>
|
||||
<NoticeLanguageCode>ENG</NoticeLanguageCode>
|
||||
</ContractNotice>`
|
||||
|
||||
parser := xmlparser.NewParser[ted.ContractNotice](nil)
|
||||
notice, err := parser.ParseString(xmlData)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Parsed Notice ID: %s\n", notice.GetID())
|
||||
```
|
||||
|
||||
## Key Interfaces
|
||||
|
||||
### Parseable Interface
|
||||
|
||||
Every struct to be parsed must implement this interface:
|
||||
|
||||
```go
|
||||
type Parseable interface {
|
||||
Validate() error // Validate the parsed structure
|
||||
GetID() string // Return unique identifier
|
||||
}
|
||||
```
|
||||
|
||||
### Parser Interface
|
||||
|
||||
```go
|
||||
type Parser[T Parseable] interface {
|
||||
Parse(reader io.Reader) (*T, error)
|
||||
ParseString(xmlData string) (*T, error)
|
||||
ParseBytes(xmlData []byte) (*T, error)
|
||||
Validate(reader io.Reader) error
|
||||
}
|
||||
```
|
||||
|
||||
## Parser Configuration
|
||||
|
||||
```go
|
||||
options := &xmlparser.ParserOptions{
|
||||
StrictValidation: true, // Strict XML validation
|
||||
AllowEmptyElements: true, // Allow empty XML elements
|
||||
PreserveWhitespace: false, // Preserve whitespace
|
||||
IgnoreUnknownElements: false, // Ignore unknown XML elements
|
||||
MaxDepth: 100, // Maximum nesting depth
|
||||
MaxSize: 10 * 1024 * 1024, // Maximum XML size (10MB)
|
||||
}
|
||||
|
||||
parser := xmlparser.NewParser[ted.ContractNotice](options)
|
||||
```
|
||||
|
||||
## Parse with Detailed Results
|
||||
|
||||
```go
|
||||
parser := xmlparser.NewParser[ted.ContractNotice](nil).(*xmlparser.GenericParser[ted.ContractNotice])
|
||||
|
||||
result, err := parser.ParseWithResult(strings.NewReader(xmlData))
|
||||
if err != nil {
|
||||
fmt.Printf("Parse error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Display parsed information
|
||||
if result.Data != nil {
|
||||
fmt.Printf("Notice ID: %s\n", result.Data.GetID())
|
||||
}
|
||||
|
||||
// Display metadata
|
||||
if result.Metadata != nil {
|
||||
fmt.Printf("XML Version: %s\n", result.Metadata.Version)
|
||||
fmt.Printf("Encoding: %s\n", result.Metadata.Encoding)
|
||||
fmt.Printf("Namespace: %s\n", result.Metadata.Namespace)
|
||||
}
|
||||
|
||||
// Display errors and warnings
|
||||
for _, err := range result.Errors {
|
||||
fmt.Printf("Error: %s\n", err)
|
||||
}
|
||||
|
||||
for _, warning := range result.Warnings {
|
||||
fmt.Printf("Warning: %s\n", warning)
|
||||
}
|
||||
```
|
||||
|
||||
## TED XML Models
|
||||
|
||||
The package includes ready-to-use models for TED XML formats:
|
||||
|
||||
### Contract Notice
|
||||
|
||||
```go
|
||||
type ContractNotice struct {
|
||||
ID string
|
||||
ContractFolderID string
|
||||
IssueDate string
|
||||
NoticeTypeCode string
|
||||
NoticeLanguageCode string
|
||||
ProcurementProject *ProcurementProject
|
||||
// ... other fields
|
||||
}
|
||||
```
|
||||
|
||||
## Creating Custom Models
|
||||
|
||||
To create custom models, implement the `Parseable` interface:
|
||||
|
||||
```go
|
||||
type CustomModel struct {
|
||||
XMLName xml.Name `xml:"CustomElement"`
|
||||
ID string `xml:"id,attr"`
|
||||
Name string `xml:"name"`
|
||||
Value string `xml:"value"`
|
||||
}
|
||||
|
||||
func (c CustomModel) Validate() error {
|
||||
if c.ID == "" {
|
||||
return fmt.Errorf("ID is required")
|
||||
}
|
||||
if c.Name == "" {
|
||||
return fmt.Errorf("Name is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c CustomModel) GetID() string {
|
||||
return c.ID
|
||||
}
|
||||
|
||||
// Usage
|
||||
parser := xmlparser.NewParser[CustomModel](nil)
|
||||
result, err := parser.ParseString(`<CustomElement id="123"><name>Test</name><value>Sample</value></CustomElement>`)
|
||||
```
|
||||
|
||||
## Factory Function for Creating Parsers
|
||||
|
||||
```go
|
||||
func CreateTEDParser[T xmlparser.Parseable](options *xmlparser.ParserOptions) xmlparser.Parser[T] {
|
||||
return xmlparser.NewParser[T](options)
|
||||
}
|
||||
|
||||
// Usage
|
||||
contractParser := CreateTEDParser[ted.ContractNotice](nil)
|
||||
```
|
||||
|
||||
## Practical Examples
|
||||
|
||||
The `example/` folder contains complete usage examples:
|
||||
|
||||
- Parse from URL
|
||||
- Parse from String
|
||||
- Using different parser options
|
||||
- Parse with detailed results
|
||||
|
||||
To run the examples:
|
||||
|
||||
```bash
|
||||
cd pkg/xmlparser/example
|
||||
go run main.go
|
||||
```
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
pkg/xmlparser/
|
||||
├── interfaces.go # Interface definitions
|
||||
├── parser.go # Generic parser implementation
|
||||
├── utils.go # Utility functions
|
||||
├── parser_test.go # Comprehensive tests
|
||||
├── ted/
|
||||
│ └── models.go # TED XML models
|
||||
├── example/
|
||||
│ └── main.go # Usage examples
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## TED XML Support
|
||||
|
||||
This package is specifically designed for parsing TED (Tenders Electronic Daily) XML:
|
||||
|
||||
- **Contract Notice**: Tender announcements
|
||||
- **Prior Information Notice**: Preliminary notices
|
||||
- **Contract Award Notice**: Award announcements
|
||||
|
||||
### Supported Formats
|
||||
|
||||
- eForms XML (new TED version)
|
||||
- TED Schema XML (legacy version)
|
||||
- UBL 2.3 with eForms Extensions
|
||||
|
||||
## Error Handling
|
||||
|
||||
The parser supports various types of errors:
|
||||
|
||||
```go
|
||||
result, err := parser.ParseString(invalidXML)
|
||||
if err != nil {
|
||||
// General parsing error
|
||||
fmt.Printf("Parse failed: %v\n", err)
|
||||
}
|
||||
|
||||
// Detailed errors in result
|
||||
for _, parseError := range result.Errors {
|
||||
fmt.Printf("Parse error: %s\n", parseError)
|
||||
}
|
||||
|
||||
// Warnings
|
||||
for _, warning := range result.Warnings {
|
||||
fmt.Printf("Warning: %s\n", warning)
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
- XML size limits (default: 10MB)
|
||||
- Nesting depth limits (default: 100 levels)
|
||||
- Stream parsing for large files
|
||||
- Memory-efficient parsing
|
||||
|
||||
## Testing
|
||||
|
||||
Run comprehensive tests:
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
go test ./...
|
||||
|
||||
# Run with verbose output
|
||||
go test -v ./...
|
||||
|
||||
# Run benchmarks
|
||||
go test -bench=. ./...
|
||||
```
|
||||
|
||||
## Utility Functions
|
||||
|
||||
The package includes helpful utility functions:
|
||||
|
||||
- `SanitizeXMLString()` - Clean invalid XML characters
|
||||
- `ValidateXMLName()` - Validate XML element names
|
||||
- `IsValidNamespace()` - Validate namespace URIs
|
||||
- `NormalizeWhitespace()` - Normalize text content
|
||||
- `TruncateString()` - Truncate strings for display
|
||||
|
||||
Reference in New Issue
Block a user