@zio.dev/zio-blocks 0.0.27 → 0.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/reference/xml.md CHANGED
@@ -3,10 +3,31 @@ id: xml
3
3
  title: "XML"
4
4
  ---
5
5
 
6
- Zero-dependency XML codec for ZIO Blocks Schema with cross-platform support.
6
+ `Xml` is a **sealed trait representing XML nodes**. It provides a type-safe, immutable representation of all valid XML document structures including elements, text nodes, CDATA sections, comments, and processing instructions.
7
+
8
+ ```scala
9
+ sealed trait Xml {
10
+ def xmlType: XmlType
11
+ def is(xmlType: XmlType): Boolean
12
+ def as(xmlType: XmlType): Option[xmlType.Type]
13
+ def unwrap(xmlType: XmlType): Option[xmlType.Unwrap]
14
+ def print: String
15
+ def printPretty: String
16
+ def select: XmlSelection
17
+ }
18
+ ```
19
+
20
+ `Xml` supports:
21
+ - Type-safe representation of all XML node types
22
+ - Fluent navigation and querying with XmlSelection
23
+ - Schema-derived automatic codec generation
24
+ - Zero external dependencies
25
+ - Full cross-platform support (JVM and Scala.js)
7
26
 
8
27
  ## Overview
9
28
 
29
+ Zero-dependency XML codec for ZIO Blocks Schema with cross-platform support.
30
+
10
31
  The schema-xml module provides automatic XML codec derivation for any type with a `Schema`. It includes a complete XML AST, fluent navigation API, and support for XML-specific features like attributes and namespaces.
11
32
 
12
33
  Key features:
@@ -21,14 +42,20 @@ Key features:
21
42
 
22
43
  ## Installation
23
44
 
45
+ To use the schema-xml module, add the following dependency to your `build.sbt`:
46
+
24
47
  ```scala
25
48
  libraryDependencies += "dev.zio" %% "zio-blocks-schema-xml" % "0.0.14"
26
49
  ```
27
50
 
28
51
  ## Basic Usage
29
52
 
53
+ Start by deriving an XML codec from your Schema definition:
54
+
30
55
  ### Deriving Codecs
31
56
 
57
+ To create an XML codec, use `Schema[A].derive(XmlFormat)`:
58
+
32
59
  ```scala
33
60
  import zio.blocks.schema._
34
61
  import zio.blocks.schema.xml._
@@ -45,6 +72,8 @@ val codec = Schema[Person].derive(XmlFormat)
45
72
 
46
73
  ### Encoding to XML
47
74
 
75
+ Encode your values to XML using the codec:
76
+
48
77
  ```scala
49
78
  import zio.blocks.schema._
50
79
  import zio.blocks.schema.xml._
@@ -56,24 +85,85 @@ object Person {
56
85
 
57
86
  val codec = Schema[Person].derive(XmlFormat)
58
87
  val person = Person("Alice", 30)
88
+ ```
89
+
90
+ Encode to XML bytes:
59
91
 
60
- // Encode to XML bytes
92
+ ```scala
61
93
  val bytes: Array[Byte] = codec.encode(person)
94
+ // bytes: Array[Byte] = Array(
95
+ // 60,
96
+ // 80,
97
+ // 101,
98
+ // 114,
99
+ // 115,
100
+ // 111,
101
+ // 110,
102
+ // 62,
103
+ // 60,
104
+ // 110,
105
+ // 97,
106
+ // 109,
107
+ // 101,
108
+ // 62,
109
+ // 65,
110
+ // 108,
111
+ // 105,
112
+ // 99,
113
+ // 101,
114
+ // 60,
115
+ // 47,
116
+ // 110,
117
+ // 97,
118
+ // 109,
119
+ // 101,
120
+ // 62,
121
+ // 60,
122
+ // 97,
123
+ // 103,
124
+ // 101,
125
+ // 62,
126
+ // 51,
127
+ // 48,
128
+ // 60,
129
+ // 47,
130
+ // 97,
131
+ // 103,
132
+ // 101,
133
+ // 62,
134
+ // 60,
135
+ // 47,
136
+ // 80,
137
+ // 101,
138
+ // 114,
139
+ // 115,
140
+ // 111,
141
+ // 110,
142
+ // 62
143
+ // )
144
+ ```
62
145
 
63
- // Encode to XML string
146
+ Encode to XML string:
147
+
148
+ ```scala
64
149
  val xmlString: String = codec.encodeToString(person)
65
- // <Person><name>Alice</name><age>30</age></Person>
150
+ // xmlString: String = "<Person><name>Alice</name><age>30</age></Person>"
151
+ ```
66
152
 
67
- // Encode to pretty-printed XML
153
+ Encode to pretty-printed XML:
154
+
155
+ ```scala
68
156
  val prettyXml = codec.encodeToString(person, WriterConfig.pretty)
69
- // <Person>
157
+ // prettyXml: String = """<Person>
70
158
  // <name>Alice</name>
71
159
  // <age>30</age>
72
- // </Person>
160
+ // </Person>"""
73
161
  ```
74
162
 
75
163
  ### Decoding from XML
76
164
 
165
+ Decode XML strings or bytes back to your typed values:
166
+
77
167
  ```scala
78
168
  import zio.blocks.schema._
79
169
  import zio.blocks.schema.xml._
@@ -87,12 +177,28 @@ val codec = Schema[Person].derive(XmlFormat)
87
177
 
88
178
  // Decode from XML string
89
179
  val xml = "<Person><name>Alice</name><age>30</age></Person>"
180
+ ```
181
+
182
+ Decode the XML string and see the result:
183
+
184
+ ```scala
90
185
  val result: Either[SchemaError, Person] = codec.decode(xml)
91
- // Right(Person("Alice", 30))
186
+ // result: Either[SchemaError, Person] = Right(
187
+ // Person(name = "Alice", age = 30)
188
+ // )
189
+ ```
92
190
 
93
- // Decode from bytes
191
+ You can also decode from bytes:
192
+
193
+ ```scala
94
194
  val bytes = xml.getBytes("UTF-8")
195
+ ```
196
+
197
+ ```scala
95
198
  val fromBytes: Either[SchemaError, Person] = codec.decode(bytes)
199
+ // fromBytes: Either[SchemaError, Person] = Right(
200
+ // Person(name = "Alice", age = 30)
201
+ // )
96
202
  ```
97
203
 
98
204
  ## XML AST
@@ -110,6 +216,8 @@ Xml
110
216
 
111
217
  ### Creating XML Nodes
112
218
 
219
+ Construct XML nodes directly using the case class constructors:
220
+
113
221
  ```scala
114
222
  import zio.blocks.schema.xml._
115
223
  import zio.blocks.chunk.Chunk
@@ -136,7 +244,7 @@ val pi = Xml.ProcessingInstruction("xml-stylesheet", "href=\"style.css\"")
136
244
 
137
245
  ### XmlName
138
246
 
139
- `XmlName` represents an element or attribute name with optional namespace:
247
+ `XmlName` represents an element or attribute name with optional namespace. Create instances with different namespace configurations:
140
248
 
141
249
  ```scala
142
250
  import zio.blocks.schema.xml.XmlName
@@ -185,9 +293,11 @@ val commentNode = XmlBuilder.comment("comment text")
185
293
 
186
294
  ## Configuration
187
295
 
296
+ The schema-xml module provides configuration options for both parsing and writing:
297
+
188
298
  ### WriterConfig
189
299
 
190
- Controls XML output formatting:
300
+ Use `WriterConfig` to control XML output formatting:
191
301
 
192
302
  ```scala
193
303
  import zio.blocks.schema.xml.WriterConfig
@@ -268,11 +378,13 @@ object Person {
268
378
 
269
379
  val codec = Schema[Person].derive(XmlFormat)
270
380
  val person = Person("123", "active", "Alice", 30)
381
+ ```
382
+
383
+ Encode the person with attributes:
384
+
385
+ ```scala
271
386
  val xml = codec.encodeToString(person)
272
- // <Person id="123" status="active">
273
- // <name>Alice</name>
274
- // <age>30</age>
275
- // </Person>
387
+ // xml: String = "<Person><id>123</id><active>active</active><name>Alice</name><age>30</age></Person>"
276
388
  ```
277
389
 
278
390
  The `@xmlAttribute` annotation accepts an optional custom name:
@@ -300,19 +412,18 @@ object Feed {
300
412
 
301
413
  val codec = Schema[Feed].derive(XmlFormat)
302
414
  val feed = Feed("My Blog", "2024-01-01T00:00:00Z")
415
+ ```
416
+
417
+ Encode the feed with a namespace prefix:
418
+
419
+ ```scala
303
420
  val xml = codec.encodeToString(feed)
304
- // <atom:Feed xmlns:atom="http://www.w3.org/2005/Atom">
305
- // <title>My Blog</title>
306
- // <updated>2024-01-01T00:00:00Z</updated>
307
- // </atom:Feed>
421
+ // xml: String = "<Feed><title>My Blog</title><updated>2024-01-01T00:00:00Z</updated></Feed>"
308
422
  ```
309
423
 
310
424
  Without a prefix (default namespace):
311
425
 
312
426
  ```scala
313
- import zio.blocks.schema._
314
- import zio.blocks.schema.xml._
315
-
316
427
  @xmlNamespace(uri = "http://www.w3.org/2005/Atom")
317
428
  case class Feed(title: String)
318
429
 
@@ -322,10 +433,13 @@ object Feed {
322
433
 
323
434
  val codec = Schema[Feed].derive(XmlFormat)
324
435
  val feed = Feed("My Blog")
436
+ ```
437
+
438
+ Encode the feed with default namespace:
439
+
440
+ ```scala
325
441
  val xml = codec.encodeToString(feed)
326
- // <Feed xmlns="http://www.w3.org/2005/Atom">
327
- // <title>My Blog</title>
328
- // </Feed>
442
+ // xml: String = "<Feed><title>My Blog</title></Feed>"
329
443
  ```
330
444
 
331
445
  ## XmlSelection
@@ -334,6 +448,8 @@ val xml = codec.encodeToString(feed)
334
448
 
335
449
  ### Navigation
336
450
 
451
+ Navigate to child elements, filter by type, and extract content:
452
+
337
453
  ```scala
338
454
  import zio.blocks.schema.xml._
339
455
 
@@ -357,17 +473,43 @@ val books = xml.select.get("library").get("books")
357
473
 
358
474
  // Navigate by index
359
475
  val firstBook = books.get("book")(0)
476
+ ```
360
477
 
361
- // Extract text content
478
+ Extract text content from the first book:
479
+
480
+ ```scala
362
481
  val title: Either[XmlError, String] = firstBook.get("title").text
363
- // Right("Functional Programming")
482
+ // title: Either[XmlError, String] = Left(
483
+ // zio.blocks.schema.xml.XmlError: Expected single value but got 0
484
+ // )
485
+ ```
486
+
487
+ You can also navigate descendants recursively:
364
488
 
365
- // Navigate descendants (recursive search)
489
+ ```scala
366
490
  val allTitles = xml.select.descendant("title")
491
+ // allTitles: XmlSelection = XmlSelection(
492
+ // Right(
493
+ // IndexedSeq(
494
+ // Element(
495
+ // name = XmlName(localName = "title", prefix = None, namespace = None),
496
+ // attributes = IndexedSeq(),
497
+ // children = IndexedSeq(Text("Functional Programming"))
498
+ // ),
499
+ // Element(
500
+ // name = XmlName(localName = "title", prefix = None, namespace = None),
501
+ // attributes = IndexedSeq(),
502
+ // children = IndexedSeq(Text("Advanced Scala"))
503
+ // )
504
+ // )
505
+ // )
506
+ // )
367
507
  ```
368
508
 
369
509
  ### Filtering
370
510
 
511
+ Filter selections by node type or custom predicates:
512
+
371
513
  ```scala
372
514
  import zio.blocks.schema.xml._
373
515
 
@@ -384,6 +526,8 @@ val filtered = selection.filter(xml => xml.is(XmlType.Element))
384
526
 
385
527
  ### Terminal Operations
386
528
 
529
+ Execute a selection to extract values or convert to other formats:
530
+
387
531
  ```scala
388
532
  import zio.blocks.schema.xml._
389
533
 
@@ -408,6 +552,8 @@ val allText: String = selection.textContent
408
552
 
409
553
  ### Combinators
410
554
 
555
+ Combine and transform selections using monadic operations:
556
+
411
557
  ```scala
412
558
  import zio.blocks.schema.xml._
413
559
 
@@ -433,6 +579,8 @@ val withFallback = selection1.orElse(selection2)
433
579
 
434
580
  ### Creating Patches
435
581
 
582
+ Create patches for add, remove, replace, and attribute operations:
583
+
436
584
  ```scala
437
585
  import zio.blocks.schema._
438
586
  import zio.blocks.schema.xml._
@@ -464,6 +612,8 @@ val removeAttrPatch = XmlPatch.removeAttribute(path, "id")
464
612
 
465
613
  ### Position Options
466
614
 
615
+ Position options control where new content is inserted relative to the target:
616
+
467
617
  ```scala
468
618
  import zio.blocks.schema.xml.XmlPatch.Position
469
619
 
@@ -475,6 +625,8 @@ Position.AppendChild // Insert as last child of target
475
625
 
476
626
  ### Applying Patches
477
627
 
628
+ Apply a patch to an XML document to produce a modified result:
629
+
478
630
  ```scala
479
631
  import zio.blocks.schema._
480
632
  import zio.blocks.schema.xml._
@@ -488,6 +640,8 @@ val result: Either[XmlError, Xml] = patch(xml)
488
640
 
489
641
  ### Composing Patches
490
642
 
643
+ Combine multiple patches to apply transformations in sequence:
644
+
491
645
  ```scala
492
646
  import zio.blocks.schema._
493
647
  import zio.blocks.schema.xml._
@@ -503,6 +657,175 @@ val patch2 = XmlPatch.add(
503
657
  val combined = patch1 ++ patch2
504
658
  ```
505
659
 
660
+ ## XmlEncoder and XmlDecoder
661
+
662
+ For more fine-grained control over XML serialization, use the separate `XmlEncoder` and `XmlDecoder` traits:
663
+
664
+ ### XmlEncoder
665
+
666
+ `XmlEncoder[A]` provides type-safe XML encoding:
667
+
668
+ ```scala
669
+ import zio.blocks.schema._
670
+ import zio.blocks.schema.xml._
671
+
672
+ // Automatic derivation from Schema
673
+ case class Person(name: String, age: Int)
674
+ object Person {
675
+ implicit val schema: Schema[Person] = Schema.derived
676
+ implicit val encoder: XmlEncoder[Person] = XmlEncoder.fromSchema
677
+ }
678
+
679
+ val person = Person("Alice", 30)
680
+ val xml: Xml = XmlEncoder[Person].encode(person)
681
+ ```
682
+
683
+ #### Creating custom encoders
684
+
685
+ Create custom encoders from functions or using contravariance:
686
+
687
+ ```scala
688
+ import zio.blocks.schema.xml._
689
+
690
+ // Create from a function
691
+ val customEncoder: XmlEncoder[Int] = XmlEncoder.instance(n =>
692
+ Xml.Element("number", Xml.Text(n.toString))
693
+ )
694
+
695
+ // Map with contravariance - encode a wrapper type
696
+ case class UserId(value: Int)
697
+
698
+ val userIdEncoder: XmlEncoder[UserId] =
699
+ customEncoder.contramap[UserId](_.value)
700
+ ```
701
+
702
+ #### Using implicit resolution
703
+
704
+ Leverage implicit resolution for automatic encoder derivation:
705
+
706
+ ```scala
707
+ import zio.blocks.schema._
708
+ import zio.blocks.schema.xml._
709
+
710
+ case class Product(id: String, price: Double)
711
+ object Product {
712
+ implicit val schema: Schema[Product] = Schema.derived
713
+ }
714
+
715
+ // No explicit encoder needed - derives automatically
716
+ def encodeProduct[A](value: A)(implicit encoder: XmlEncoder[A]): Xml =
717
+ encoder.encode(value)
718
+
719
+ val result = encodeProduct(Product("item-1", 99.99))
720
+ ```
721
+
722
+ ### XmlDecoder
723
+
724
+ `XmlDecoder[A]` provides type-safe XML decoding with error handling:
725
+
726
+ ```scala
727
+ import zio.blocks.schema._
728
+ import zio.blocks.schema.xml._
729
+
730
+ // Automatic derivation from Schema
731
+ case class Person(name: String, age: Int)
732
+ object Person {
733
+ implicit val schema: Schema[Person] = Schema.derived
734
+ implicit val decoder: XmlDecoder[Person] = XmlDecoder.fromSchema
735
+ }
736
+
737
+ val xml = Xml.Element("Person",
738
+ Xml.Element("name", Xml.Text("Alice")),
739
+ Xml.Element("age", Xml.Text("30"))
740
+ )
741
+ ```
742
+
743
+ Decode the XML:
744
+
745
+ ```scala
746
+ val result: Either[XmlError, Person] = XmlDecoder[Person].decode(xml)
747
+ // result: Either[XmlError, Person] = Right(Person(name = "Alice", age = 30))
748
+ ```
749
+
750
+ #### Creating custom decoders
751
+
752
+ Create custom decoders from functions or using covariance:
753
+
754
+ ```scala
755
+ import zio.blocks.schema.xml._
756
+ import zio.blocks.chunk.Chunk
757
+
758
+ // Create from a function
759
+ val numberDecoder: XmlDecoder[Int] = XmlDecoder.instance { xml =>
760
+ xml match {
761
+ case Xml.Element(_, _, Chunk(Xml.Text(text), _*)) =>
762
+ text.toIntOption.toRight(XmlError("Invalid number"))
763
+ case _ => Left(XmlError("Expected number element"))
764
+ }
765
+ }
766
+
767
+ // Map for covariance - decode to a wrapper type
768
+ case class UserId(value: Int)
769
+
770
+ val userIdDecoder: XmlDecoder[UserId] =
771
+ numberDecoder.map(UserId(_))
772
+ ```
773
+
774
+ #### Error handling with decoders
775
+
776
+ Handle decoding errors gracefully with fallback strategies:
777
+
778
+ ```scala
779
+ import zio.blocks.schema._
780
+ import zio.blocks.schema.xml._
781
+
782
+ case class Person(name: String, age: Int)
783
+ object Person {
784
+ implicit val schema: Schema[Person] = Schema.derived
785
+ }
786
+
787
+ def decodeWithFallback[A](
788
+ xml: Xml,
789
+ fallback: A
790
+ )(implicit decoder: XmlDecoder[A]): A = {
791
+ decoder.decode(xml).getOrElse(fallback)
792
+ }
793
+
794
+ val invalidXml = Xml.Element("Empty")
795
+ val defaultPerson = Person("Unknown", 0)
796
+ val result = decodeWithFallback(invalidXml, defaultPerson)
797
+ // Person("Unknown", 0)
798
+ ```
799
+
800
+ #### Combining encoders and decoders
801
+
802
+ Round-trip values by encoding and decoding:
803
+
804
+ ```scala
805
+ import zio.blocks.schema._
806
+ import zio.blocks.schema.xml._
807
+ import zio.blocks.schema.xml.syntax._
808
+
809
+ case class Message(id: String, text: String)
810
+ object Message {
811
+ implicit val schema: Schema[Message] = Schema.derived
812
+ }
813
+
814
+ // Round-trip: encode then decode
815
+ val message = Message("msg-1", "Hello")
816
+ val encoded: Xml = message.toXml
817
+ ```
818
+
819
+ Decode the encoded value:
820
+
821
+ ```scala
822
+ val result: Either[XmlError, Message] =
823
+ implicitly[XmlDecoder[Message]].decode(encoded)
824
+ // result: Either[XmlError, Message] = Right(
825
+ // Message(id = "msg-1", text = "Hello")
826
+ // )
827
+ ```
828
+
506
829
  ## Extension Syntax
507
830
 
508
831
  When a `Schema` is in scope, use convenient extension methods:
@@ -539,29 +862,40 @@ val fromBytes: Either[SchemaError, Person] = bytes.fromXml[Person]
539
862
 
540
863
  ## Printing XML
541
864
 
542
- ### Basic Printing
865
+ Format XML documents using compact or pretty-printed output. You can convert XML to string with different formatting options:
543
866
 
544
867
  ```scala
545
868
  import zio.blocks.schema.xml._
546
869
 
547
870
  val xml = Xml.Element("person", Xml.Element("name", Xml.Text("Alice")))
871
+ ```
548
872
 
549
- // Compact output
873
+ Compact output:
874
+
875
+ ```scala
550
876
  val compact: String = xml.print
551
- // <person><name>Alice</name></person>
877
+ // compact: String = "<person><name>Alice</name></person>"
878
+ ```
552
879
 
553
- // Pretty-printed output
880
+ Pretty-printed output:
881
+
882
+ ```scala
554
883
  val pretty: String = xml.printPretty
555
- // <person>
884
+ // pretty: String = """<person>
556
885
  // <name>Alice</name>
557
- // </person>
886
+ // </person>"""
887
+ ```
558
888
 
559
- // Custom configuration
889
+ Custom configuration:
890
+
891
+ ```scala
560
892
  val custom: String = xml.print(WriterConfig(indentStep = 4))
561
893
  ```
562
894
 
563
895
  ## Type Testing and Access
564
896
 
897
+ Test and extract values from XML nodes using type guards and unwrapping:
898
+
565
899
  ```scala
566
900
  import zio.blocks.schema.xml._
567
901
 
@@ -608,9 +942,43 @@ All standard ZIO Blocks Schema types are supported:
608
942
  - Options (`Option[A]`)
609
943
  - Wrappers (newtypes)
610
944
 
945
+ ## XmlBinaryCodec
946
+
947
+ `XmlBinaryCodec[A]` is the low-level codec interface that bridges Schema definitions with XML serialization. While usually derived automatically, you can work with it directly:
948
+
949
+ ```scala
950
+ import zio.blocks.schema._
951
+ import zio.blocks.schema.xml._
952
+
953
+ case class Person(name: String, age: Int)
954
+ object Person {
955
+ implicit val schema: Schema[Person] = Schema.derived
956
+ }
957
+
958
+ // Get the underlying binary codec
959
+ val codec: XmlBinaryCodec[Person] =
960
+ Schema[Person].derive(XmlBinaryCodecDeriver)
961
+
962
+ // Encode to Xml directly
963
+ val person = Person("Alice", 30)
964
+ val xml: Xml = codec.encodeValue(person)
965
+
966
+ // Decode from Xml directly
967
+ val decoded: Either[XmlError, Person] = codec.decodeValue(xml)
968
+ ```
969
+
970
+ **XmlBinaryCodec supports all Schema types:**
971
+ - Primitives (Int, String, Boolean, etc.)
972
+ - Java time types (Instant, LocalDate, Duration, etc.)
973
+ - Records (case classes with field-level configuration)
974
+ - Variants (sealed traits with discriminators)
975
+ - Collections (List, Vector, Map, etc.)
976
+ - Optional fields (Option[A])
977
+ - Custom wrappers and dynamic values
978
+
611
979
  ## Error Handling
612
980
 
613
- All decoding operations return `Either[SchemaError, A]` or `Either[XmlError, A]`:
981
+ All decoding operations return `Either[SchemaError, A]` or `Either[XmlError, A]`. The `XmlError` type provides detailed error information:
614
982
 
615
983
  ```scala
616
984
  import zio.blocks.schema._
@@ -629,12 +997,45 @@ val result = codec.decode(invalid)
629
997
 
630
998
  result match {
631
999
  case Right(person) => println(s"Decoded: $person")
632
- case Left(error) =>
1000
+ case Left(error) =>
633
1001
  println(s"Error: ${error.getMessage}")
634
1002
  // Error information includes parse location and context
635
1003
  }
636
1004
  ```
637
1005
 
1006
+ `XmlError` provides detailed error information for debugging:
1007
+
1008
+ ```scala
1009
+ import zio.blocks.schema.xml._
1010
+
1011
+ val error = XmlError("Parse failed")
1012
+
1013
+ // Error message
1014
+ val message: String = error.getMessage
1015
+
1016
+ // Get error message for inspection
1017
+ val errorMsg: String = error.getMessage
1018
+ ```
1019
+
1020
+ Error handling best practices:
1021
+
1022
+ ```scala
1023
+ import zio.blocks.schema._
1024
+ import zio.blocks.schema.xml._
1025
+
1026
+ case class Config(database: String, port: Int)
1027
+ object Config {
1028
+ implicit val schema: Schema[Config] = Schema.derived
1029
+ }
1030
+
1031
+ def loadConfig(xml: String): Either[String, Config] = {
1032
+ val codec = Schema[Config].derive(XmlFormat)
1033
+ codec.decode(xml).left.map { error =>
1034
+ s"Configuration error: ${error.getMessage}\nCheck XML format and required fields."
1035
+ }
1036
+ }
1037
+ ```
1038
+
638
1039
  ## Cross-Platform Support
639
1040
 
640
1041
  The XML module works across all platforms:
@@ -646,8 +1047,12 @@ All features including parsing, writing, navigation, and patching work identical
646
1047
 
647
1048
  ## Examples
648
1049
 
1050
+ These examples demonstrate common use cases and patterns with the XML module:
1051
+
649
1052
  ### Complete Example with Attributes and Namespaces
650
1053
 
1054
+ Define a schema with attributes and namespaces, then encode and decode:
1055
+
651
1056
  ```scala
652
1057
  import zio.blocks.schema._
653
1058
  import zio.blocks.schema.xml._
@@ -675,26 +1080,41 @@ val entry = Entry(
675
1080
  updated = "2024-01-01T00:00:00Z",
676
1081
  author = Author("Alice", "alice@example.com")
677
1082
  )
1083
+ ```
678
1084
 
679
- // Encode with pretty printing
680
- val xml = codec.encodeToString(entry, WriterConfig.pretty)
681
- println(xml)
682
- // <atom:Entry xmlns:atom="http://www.w3.org/2005/Atom" id="entry-1">
1085
+ Encode the entry with pretty printing:
1086
+
1087
+ ```scala
1088
+ val xmlStr = codec.encodeToString(entry, WriterConfig.pretty)
1089
+ // xmlStr: String = """<Entry>
1090
+ // <id>entry-1</id>
683
1091
  // <title>First Post</title>
684
1092
  // <updated>2024-01-01T00:00:00Z</updated>
685
1093
  // <author>
686
1094
  // <name>Alice</name>
687
1095
  // <email>alice@example.com</email>
688
1096
  // </author>
689
- // </atom:Entry>
1097
+ // </Entry>"""
1098
+ ```
1099
+
1100
+ Decode the XML back to a typed value:
690
1101
 
691
- // Decode back to typed value
692
- val decoded = codec.decode(xml)
693
- // Right(Entry("entry-1", "First Post", "2024-01-01T00:00:00Z", Author("Alice", "alice@example.com")))
1102
+ ```scala
1103
+ val decoded = codec.decode(xmlStr)
1104
+ // decoded: Either[SchemaError, Entry] = Right(
1105
+ // Entry(
1106
+ // id = "entry-1",
1107
+ // title = "First Post",
1108
+ // updated = "2024-01-01T00:00:00Z",
1109
+ // author = Author(name = "Alice", email = "alice@example.com")
1110
+ // )
1111
+ // )
694
1112
  ```
695
1113
 
696
1114
  ### Navigation and Transformation
697
1115
 
1116
+ Find elements, extract data, and apply patches to modify XML:
1117
+
698
1118
  ```scala
699
1119
  import zio.blocks.schema._
700
1120
  import zio.blocks.schema.xml._
@@ -741,3 +1161,144 @@ val patch = XmlPatch.add(
741
1161
 
742
1162
  val updated = patch(xml)
743
1163
  ```
1164
+
1165
+ ## Real-World Examples
1166
+
1167
+ Learn by examining practical examples of XML codecs in action:
1168
+
1169
+ ### RSS Feed Parsing
1170
+
1171
+ Parse RSS feeds by defining a schema and decoding XML:
1172
+
1173
+ ```scala
1174
+ import zio.blocks.schema._
1175
+ import zio.blocks.schema.xml._
1176
+
1177
+ @xmlNamespace(uri = "http://www.rss.org/", prefix = "rss")
1178
+ case class Item(
1179
+ @xmlAttribute() guid: String,
1180
+ title: String,
1181
+ link: String,
1182
+ pubDate: String,
1183
+ description: String
1184
+ )
1185
+
1186
+ @xmlNamespace(uri = "http://www.rss.org/", prefix = "rss")
1187
+ case class Channel(
1188
+ title: String,
1189
+ link: String,
1190
+ description: String,
1191
+ items: List[Item]
1192
+ )
1193
+
1194
+ object Channel {
1195
+ implicit val itemSchema: Schema[Item] = Schema.derived
1196
+ implicit val schema: Schema[Channel] = Schema.derived
1197
+ }
1198
+
1199
+ // Parse RSS feed from string
1200
+ val feedXml = """<rss:Channel xmlns:rss="http://www.rss.org/">
1201
+ <title>Tech Blog</title>
1202
+ <link>https://example.com</link>
1203
+ <description>Latest tech articles</description>
1204
+ <items>
1205
+ <Item guid="1">
1206
+ <title>Functional Programming in Scala</title>
1207
+ <link>https://example.com/fp</link>
1208
+ <pubDate>2024-01-01</pubDate>
1209
+ <description>Deep dive into FP concepts</description>
1210
+ </Item>
1211
+ </items>
1212
+ </rss:Channel>"""
1213
+
1214
+ val codec = Schema[Channel].derive(XmlFormat)
1215
+ val result: Either[SchemaError, Channel] = codec.decode(feedXml)
1216
+ ```
1217
+
1218
+ ### Atom Feed with Advanced Features
1219
+
1220
+ Work with Atom feeds using attributes, multiple entries, and custom configurations:
1221
+
1222
+ ```scala
1223
+ import zio.blocks.schema._
1224
+ import zio.blocks.schema.xml._
1225
+
1226
+ @xmlNamespace(uri = "http://www.w3.org/2005/Atom", prefix = "atom")
1227
+ case class Entry(
1228
+ @xmlAttribute() id: String,
1229
+ title: String,
1230
+ author: String,
1231
+ updated: String,
1232
+ @xmlAttribute("href") link: String
1233
+ )
1234
+
1235
+ @xmlNamespace(uri = "http://www.w3.org/2005/Atom", prefix = "atom")
1236
+ case class Feed(
1237
+ @xmlAttribute() id: String,
1238
+ title: String,
1239
+ updated: String,
1240
+ entries: List[Entry]
1241
+ )
1242
+
1243
+ object Feed {
1244
+ implicit val entrySchema: Schema[Entry] = Schema.derived
1245
+ implicit val schema: Schema[Feed] = Schema.derived
1246
+ }
1247
+
1248
+ // Encode feed to XML with custom formatting
1249
+ val feed = Feed(
1250
+ id = "urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6",
1251
+ title = "Example Feed",
1252
+ updated = "2024-01-01T18:30:02Z",
1253
+ entries = List(
1254
+ Entry(
1255
+ id = "urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a",
1256
+ title = "Atom-Powered Robots Run Amok",
1257
+ author = "John Doe",
1258
+ updated = "2024-01-01T18:30:02Z",
1259
+ link = "http://example.org/2024/01/entry"
1260
+ )
1261
+ )
1262
+ )
1263
+
1264
+ val codec = Schema[Feed].derive(XmlFormat)
1265
+ val xmlOutput = codec.encodeToString(feed, WriterConfig.pretty)
1266
+ ```
1267
+
1268
+ ### Sitemap XML
1269
+
1270
+ Generate sitemap XML with URLs and optional metadata fields:
1271
+
1272
+ ```scala
1273
+ import zio.blocks.schema._
1274
+ import zio.blocks.schema.xml._
1275
+
1276
+ case class Url(
1277
+ loc: String,
1278
+ lastmod: Option[String],
1279
+ changefreq: Option[String],
1280
+ priority: Option[Double]
1281
+ )
1282
+
1283
+ case class Urlset(
1284
+ urls: List[Url]
1285
+ )
1286
+
1287
+ object Urlset {
1288
+ implicit val urlSchema: Schema[Url] = Schema.derived
1289
+ implicit val schema: Schema[Urlset] = Schema.derived
1290
+ }
1291
+
1292
+ // Build and encode sitemap
1293
+ val sitemap = Urlset(List(
1294
+ Url("https://example.com", Some("2024-01-01"), Some("monthly"), Some(1.0)),
1295
+ Url("https://example.com/about", Some("2024-01-01"), Some("monthly"), Some(0.8)),
1296
+ Url("https://example.com/contact", None, Some("monthly"), Some(0.5))
1297
+ ))
1298
+
1299
+ val codec = Schema[Urlset].derive(XmlFormat)
1300
+ val sitemapXml = codec.encodeToString(sitemap, WriterConfig(
1301
+ indentStep = 2,
1302
+ includeDeclaration = true
1303
+ ))
1304
+ ```