Skip to content

Commit 0d451ad

Browse files
authored
feat: add XML encoding and decoding exercise template (#100)
1 parent b87c663 commit 0d451ad

5 files changed

Lines changed: 352 additions & 0 deletions

File tree

internal/exercises/catalog.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,14 @@ concepts:
134134
test_regex: ".*"
135135
hints:
136136
- Define a custom error type and return it from a function.
137+
- slug: 37_xml
138+
title: XML Encoding and Decoding
139+
test_regex: ".*"
140+
hints:
141+
- Use encoding/xml package for marshaling and unmarshaling XML data.
142+
- Add XML struct tags using `xml:"fieldname"` to map struct fields to XML elements.
143+
- Use xml.Marshal to convert structs to XML bytes.
144+
- Use xml.Unmarshal to parse XML bytes into structs.
137145

138146
projects:
139147
- slug: 101_text_analyzer
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package xml
2+
3+
import "encoding/xml"
4+
5+
// Person represents a person with basic information
6+
type Person struct {
7+
Name string `xml:"name"`
8+
Age int `xml:"age"`
9+
Email string `xml:"email"`
10+
}
11+
12+
// Book represents a book with title, author, and year
13+
type Book struct {
14+
Title string `xml:"title"`
15+
Author string `xml:"author"`
16+
Year int `xml:"year"`
17+
}
18+
19+
// MarshalPerson converts a Person struct to XML bytes
20+
func MarshalPerson(p Person) ([]byte, error) {
21+
return xml.Marshal(p)
22+
}
23+
24+
// UnmarshalPerson converts XML bytes to a Person struct
25+
func UnmarshalPerson(data []byte) (Person, error) {
26+
var p Person
27+
err := xml.Unmarshal(data, &p)
28+
return p, err
29+
}
30+
31+
// MarshalBook converts a Book struct to XML bytes
32+
func MarshalBook(b Book) ([]byte, error) {
33+
return xml.Marshal(b)
34+
}
35+
36+
// UnmarshalBook converts XML bytes to a Book struct
37+
func UnmarshalBook(data []byte) (Book, error) {
38+
var b Book
39+
err := xml.Unmarshal(data, &b)
40+
return b, err
41+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package xml
2+
3+
import (
4+
"strings"
5+
"testing"
6+
)
7+
8+
func TestMarshalPerson(t *testing.T) {
9+
person := Person{
10+
Name: "Alice",
11+
Age: 30,
12+
Email: "alice@example.com",
13+
}
14+
15+
data, err := MarshalPerson(person)
16+
if err != nil {
17+
t.Fatalf("MarshalPerson() error = %v", err)
18+
}
19+
20+
xmlStr := string(data)
21+
if !strings.Contains(xmlStr, "<name>Alice</name>") {
22+
t.Errorf("Expected XML to contain <name>Alice</name>, got %s", xmlStr)
23+
}
24+
if !strings.Contains(xmlStr, "<age>30</age>") {
25+
t.Errorf("Expected XML to contain <age>30</age>, got %s", xmlStr)
26+
}
27+
if !strings.Contains(xmlStr, "<email>alice@example.com</email>") {
28+
t.Errorf("Expected XML to contain <email>alice@example.com</email>, got %s", xmlStr)
29+
}
30+
}
31+
32+
func TestUnmarshalPerson(t *testing.T) {
33+
xmlData := []byte(`<Person><name>Bob</name><age>25</age><email>bob@example.com</email></Person>`)
34+
35+
person, err := UnmarshalPerson(xmlData)
36+
if err != nil {
37+
t.Fatalf("UnmarshalPerson() error = %v", err)
38+
}
39+
40+
if person.Name != "Bob" {
41+
t.Errorf("Expected Name to be Bob, got %s", person.Name)
42+
}
43+
if person.Age != 25 {
44+
t.Errorf("Expected Age to be 25, got %d", person.Age)
45+
}
46+
if person.Email != "bob@example.com" {
47+
t.Errorf("Expected Email to be bob@example.com, got %s", person.Email)
48+
}
49+
}
50+
51+
func TestMarshalBook(t *testing.T) {
52+
book := Book{
53+
Title: "Go Programming",
54+
Author: "John Doe",
55+
Year: 2023,
56+
}
57+
58+
data, err := MarshalBook(book)
59+
if err != nil {
60+
t.Fatalf("MarshalBook() error = %v", err)
61+
}
62+
63+
xmlStr := string(data)
64+
if !strings.Contains(xmlStr, "<title>Go Programming</title>") {
65+
t.Errorf("Expected XML to contain <title>Go Programming</title>, got %s", xmlStr)
66+
}
67+
if !strings.Contains(xmlStr, "<author>John Doe</author>") {
68+
t.Errorf("Expected XML to contain <author>John Doe</author>, got %s", xmlStr)
69+
}
70+
if !strings.Contains(xmlStr, "<year>2023</year>") {
71+
t.Errorf("Expected XML to contain <year>2023</year>, got %s", xmlStr)
72+
}
73+
}
74+
75+
func TestUnmarshalBook(t *testing.T) {
76+
xmlData := []byte(`<Book><title>Learning XML</title><author>Jane Smith</author><year>2024</year></Book>`)
77+
78+
book, err := UnmarshalBook(xmlData)
79+
if err != nil {
80+
t.Fatalf("UnmarshalBook() error = %v", err)
81+
}
82+
83+
if book.Title != "Learning XML" {
84+
t.Errorf("Expected Title to be Learning XML, got %s", book.Title)
85+
}
86+
if book.Author != "Jane Smith" {
87+
t.Errorf("Expected Author to be Jane Smith, got %s", book.Author)
88+
}
89+
if book.Year != 2024 {
90+
t.Errorf("Expected Year to be 2024, got %d", book.Year)
91+
}
92+
}
93+
94+
func TestMarshalUnmarshalRoundTrip(t *testing.T) {
95+
originalPerson := Person{
96+
Name: "Charlie",
97+
Age: 35,
98+
Email: "charlie@example.com",
99+
}
100+
101+
// Marshal
102+
data, err := MarshalPerson(originalPerson)
103+
if err != nil {
104+
t.Fatalf("MarshalPerson() error = %v", err)
105+
}
106+
107+
// Unmarshal
108+
parsedPerson, err := UnmarshalPerson(data)
109+
if err != nil {
110+
t.Fatalf("UnmarshalPerson() error = %v", err)
111+
}
112+
113+
// Compare
114+
if parsedPerson.Name != originalPerson.Name {
115+
t.Errorf("Name mismatch: got %s, want %s", parsedPerson.Name, originalPerson.Name)
116+
}
117+
if parsedPerson.Age != originalPerson.Age {
118+
t.Errorf("Age mismatch: got %d, want %d", parsedPerson.Age, originalPerson.Age)
119+
}
120+
if parsedPerson.Email != originalPerson.Email {
121+
t.Errorf("Email mismatch: got %s, want %s", parsedPerson.Email, originalPerson.Email)
122+
}
123+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package xml
2+
3+
// Person represents a person with basic information
4+
type Person struct {
5+
// TODO: Add XML struct tags to define how this struct maps to XML
6+
// Use `xml:"name"` for the Name field
7+
// Use `xml:"age"` for the Age field
8+
// Use `xml:"email"` for the Email field
9+
Name string
10+
Age int
11+
Email string
12+
}
13+
14+
// Book represents a book with title, author, and year
15+
type Book struct {
16+
// TODO: Add XML struct tags
17+
// Use `xml:"title"` for Title
18+
// Use `xml:"author"` for Author
19+
// Use `xml:"year"` for Year
20+
Title string
21+
Author string
22+
Year int
23+
}
24+
25+
// MarshalPerson converts a Person struct to XML bytes
26+
// TODO: Implement this function using xml.Marshal
27+
func MarshalPerson(p Person) ([]byte, error) {
28+
// TODO: Use xml.Marshal to convert the Person struct to XML
29+
// Import "encoding/xml" package
30+
return nil, nil
31+
}
32+
33+
// UnmarshalPerson converts XML bytes to a Person struct
34+
// TODO: Implement this function using xml.Unmarshal
35+
func UnmarshalPerson(data []byte) (Person, error) {
36+
// TODO: Use xml.Unmarshal to parse XML data into a Person struct
37+
// Import "encoding/xml" package
38+
var p Person
39+
return p, nil
40+
}
41+
42+
// MarshalBook converts a Book struct to XML bytes
43+
// TODO: Implement this function using xml.Marshal
44+
func MarshalBook(b Book) ([]byte, error) {
45+
// TODO: Use xml.Marshal to convert the Book struct to XML
46+
// Import "encoding/xml" package
47+
return nil, nil
48+
}
49+
50+
// UnmarshalBook converts XML bytes to a Book struct
51+
// TODO: Implement this function using xml.Unmarshal
52+
func UnmarshalBook(data []byte) (Book, error) {
53+
// TODO: Use xml.Unmarshal to parse XML data into a Book struct
54+
// Import "encoding/xml" package
55+
var b Book
56+
return b, nil
57+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package xml
2+
3+
import (
4+
"strings"
5+
"testing"
6+
)
7+
8+
func TestMarshalPerson(t *testing.T) {
9+
person := Person{
10+
Name: "Alice",
11+
Age: 30,
12+
Email: "alice@example.com",
13+
}
14+
15+
data, err := MarshalPerson(person)
16+
if err != nil {
17+
t.Fatalf("MarshalPerson() error = %v", err)
18+
}
19+
20+
xmlStr := string(data)
21+
if !strings.Contains(xmlStr, "<name>Alice</name>") {
22+
t.Errorf("Expected XML to contain <name>Alice</name>, got %s", xmlStr)
23+
}
24+
if !strings.Contains(xmlStr, "<age>30</age>") {
25+
t.Errorf("Expected XML to contain <age>30</age>, got %s", xmlStr)
26+
}
27+
if !strings.Contains(xmlStr, "<email>alice@example.com</email>") {
28+
t.Errorf("Expected XML to contain <email>alice@example.com</email>, got %s", xmlStr)
29+
}
30+
}
31+
32+
func TestUnmarshalPerson(t *testing.T) {
33+
xmlData := []byte(`<Person><name>Bob</name><age>25</age><email>bob@example.com</email></Person>`)
34+
35+
person, err := UnmarshalPerson(xmlData)
36+
if err != nil {
37+
t.Fatalf("UnmarshalPerson() error = %v", err)
38+
}
39+
40+
if person.Name != "Bob" {
41+
t.Errorf("Expected Name to be Bob, got %s", person.Name)
42+
}
43+
if person.Age != 25 {
44+
t.Errorf("Expected Age to be 25, got %d", person.Age)
45+
}
46+
if person.Email != "bob@example.com" {
47+
t.Errorf("Expected Email to be bob@example.com, got %s", person.Email)
48+
}
49+
}
50+
51+
func TestMarshalBook(t *testing.T) {
52+
book := Book{
53+
Title: "Go Programming",
54+
Author: "John Doe",
55+
Year: 2023,
56+
}
57+
58+
data, err := MarshalBook(book)
59+
if err != nil {
60+
t.Fatalf("MarshalBook() error = %v", err)
61+
}
62+
63+
xmlStr := string(data)
64+
if !strings.Contains(xmlStr, "<title>Go Programming</title>") {
65+
t.Errorf("Expected XML to contain <title>Go Programming</title>, got %s", xmlStr)
66+
}
67+
if !strings.Contains(xmlStr, "<author>John Doe</author>") {
68+
t.Errorf("Expected XML to contain <author>John Doe</author>, got %s", xmlStr)
69+
}
70+
if !strings.Contains(xmlStr, "<year>2023</year>") {
71+
t.Errorf("Expected XML to contain <year>2023</year>, got %s", xmlStr)
72+
}
73+
}
74+
75+
func TestUnmarshalBook(t *testing.T) {
76+
xmlData := []byte(`<Book><title>Learning XML</title><author>Jane Smith</author><year>2024</year></Book>`)
77+
78+
book, err := UnmarshalBook(xmlData)
79+
if err != nil {
80+
t.Fatalf("UnmarshalBook() error = %v", err)
81+
}
82+
83+
if book.Title != "Learning XML" {
84+
t.Errorf("Expected Title to be Learning XML, got %s", book.Title)
85+
}
86+
if book.Author != "Jane Smith" {
87+
t.Errorf("Expected Author to be Jane Smith, got %s", book.Author)
88+
}
89+
if book.Year != 2024 {
90+
t.Errorf("Expected Year to be 2024, got %d", book.Year)
91+
}
92+
}
93+
94+
func TestMarshalUnmarshalRoundTrip(t *testing.T) {
95+
originalPerson := Person{
96+
Name: "Charlie",
97+
Age: 35,
98+
Email: "charlie@example.com",
99+
}
100+
101+
// Marshal
102+
data, err := MarshalPerson(originalPerson)
103+
if err != nil {
104+
t.Fatalf("MarshalPerson() error = %v", err)
105+
}
106+
107+
// Unmarshal
108+
parsedPerson, err := UnmarshalPerson(data)
109+
if err != nil {
110+
t.Fatalf("UnmarshalPerson() error = %v", err)
111+
}
112+
113+
// Compare
114+
if parsedPerson.Name != originalPerson.Name {
115+
t.Errorf("Name mismatch: got %s, want %s", parsedPerson.Name, originalPerson.Name)
116+
}
117+
if parsedPerson.Age != originalPerson.Age {
118+
t.Errorf("Age mismatch: got %d, want %d", parsedPerson.Age, originalPerson.Age)
119+
}
120+
if parsedPerson.Email != originalPerson.Email {
121+
t.Errorf("Email mismatch: got %s, want %s", parsedPerson.Email, originalPerson.Email)
122+
}
123+
}

0 commit comments

Comments
 (0)