forked from ExtendedXmlSerializer/home
-
Notifications
You must be signed in to change notification settings - Fork 1
/
CodeProject.html
1023 lines (761 loc) · 40.9 KB
/
CodeProject.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<h2>Welcome!</h2>
<p>Welcome to ExtendedXmlSerializer's source repository, our little home on the webternets for ExtendedXmlSerializer. Please have a look around. If you have a question about our serializer, please do not hesitate to post a question in our issues:</p>
<p>https://github.com/wojtpl2/ExtendedXmlSerializer/issues/new/</p>
<p></p>
<p>We will mark it as a question/discussion and talk it through with you using sample code. This process is currently being used to fill in the gaps with our documentation, which could use a little love.</p>
<p></p>
<p>Additionally, please make use of our documentation tag seen here:</p>
<p></p>
<p>https://github.com/wojtpl2/ExtendedXmlSerializer/issues?q=is%3Aissue+label%3ADocumentation</p>
<p></p>
<p>You can see the history of questions asked that we feel could eventually be added to our documentation. We'll eventually get to it, we swear. 😆</p>
<p></p>
<p></p>
<h2>Information</h2>
<p>Support platforms:</p>
<ul>
<li>.NET 4.5</li>
<li>.NET Standard 2.0</li>
</ul>
<p>Support features:</p>
<ul>
<li>Deserialization xml from standard <code>XMLSerializer</code> (mostly, see: https://github.com/wojtpl2/ExtendedXmlSerializer/issues/240)</li>
<li>Serialization class, struct, generic class, primitive type, generic list and dictionary, array, enum</li>
<li>Serialization class with property interface</li>
<li>Serialization circular reference and reference Id</li>
<li>Deserialization of old version of xml</li>
<li>Property encryption</li>
<li>Custom serializer</li>
<li>Support <code>XmlElementAttribute</code>, <code>XmlRootAttribute</code>, and <code>XmlTypeAttribute</code> for identity resolution.</li>
<li>Additional attribute support: <code>XmlIgnoreAttribute</code>, <code>XmlAttributeAttribute</code>, and <code>XmlEnumAttribute</code>.</li>
<li>POCO - all configurations (migrations, custom serializer...) are outside the clas</li>
</ul>
<p>Standard XML Serializer in .NET is very limited:</p>
<ul>
<li>Does not support serialization of class with circular reference or class with interface property.</li>
<li>There is no mechanism for reading the old version of XML.</li>
<li>Does not support properties that are defined with interface types.</li>
<li>Does not support read-only collection properties (like Xaml does).</li>
<li>Does not support parameterized constructors.</li>
<li>Does not support private constructors.</li>
<li>If you want create custom serializer, your class must inherit from <code>IXmlSerializable</code>. This means that your class will not be a POCO class.</li>
<li>Does not support IoC</li>
</ul>
<h2>The Basics</h2>
<p>Everything in <code>ExtendedXmlSerializer</code> begins with a configuration container, from which you can use to configure the serializer and ultimately create it:</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer()
// Configure...
.Create();
</pre>
<p>Using this simple subject class:</p>
<pre lang="cs">
public sealed class Subject
{
public string Message { get; set; }
public int Count { get; set; }
}
</pre>
<p>The results of the default serializer will look like this:</p>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<Subject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Introduction;assembly=ExtendedXmlSerializer.Samples">
<Message>Hello World!</Message>
<Count>6776</Count>
</Subject>
</pre>
<p>We can take this a step further by configuring the <code>Subject</code>'s Type and Member properties, which will effect how its Xml is emitted. Here is an example of configuring the <code>Subject</code>'s name to emit as <code>ModifiedSubject</code>:</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().ConfigureType<Subject>()
.Name("ModifiedSubject")
.Create();
</pre>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<ModifiedSubject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Introduction;assembly=ExtendedXmlSerializer.Samples">
<Message>Hello World!</Message>
<Count>6776</Count>
</ModifiedSubject>
</pre>
<p>Diving a bit further, we can also configure the type's member information. For example, configuring <code>Subject.Message</code> to emit as <code>Text</code> instead:</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().ConfigureType<Subject>()
.Member(x => x.Message)
.Name("Text")
.Create();
</pre>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<Subject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Introduction;assembly=ExtendedXmlSerializer.Samples">
<Text>Hello World!</Text>
<Count>6776</Count>
</Subject>
</pre>
<h2>Xml Settings</h2>
<p>In case you want to configure the XML write and read settings via <code>XmlWriterSettings</code> and <code>XmlReaderSettings</code> respectively, you can do that via extension methods created for you to do so:</p>
<pre lang="cs">
Subject subject = new Subject{ Count = 6776, Message = "Hello World!" };
IExtendedXmlSerializer serializer = new ConfigurationContainer().Create();
string contents = serializer.Serialize(new XmlWriterSettings {Indent = true}, subject);
// ...
</pre>
<p>And for reading:</p>
<pre lang="cs">
Subject instance = serializer.Deserialize<Subject>(new XmlReaderSettings{IgnoreWhitespace = false}, contents);
// ...
</pre>
<h2>Serialization</h2>
<p>Now that your configuration container has been configured and your serializer has been created, it's time to get to the serialization.</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().Create();
TestClass obj = new TestClass();
string xml = serializer.Serialize(obj);
</pre>
<h2>Deserialization</h2>
<pre lang="cs">
TestClass obj2 = serializer.Deserialize<TestClass>(xml);
</pre>
<h2>Classic Serialization/Deserialization</h2>
<p>Most of the code examples that you see in this documentation make use of useful extension methods that make serialization and deserialization a snap with <code>ExtendedXmlSerializer. However, if you would like to break down into the basic, classical pattern of serialization, and deserialization, this is supported too, as seen by the following examples. Here's serialization:</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().Create();
TestClass instance = new TestClass();
MemoryStream stream = new MemoryStream();
using (XmlWriter writer = XmlWriter.Create(stream))
{
serializer.Serialize(writer, instance);
writer.Flush();
}
stream.Seek(0, SeekOrigin.Begin);
string contents = new StreamReader(stream).ReadToEnd();
</pre>
<p>And here's how to deserialize:</p>
<pre lang="cs">
TestClass deserialized;
MemoryStream contentStream = new MemoryStream(Encoding.UTF8.GetBytes(contents));
using (XmlReader reader = XmlReader.Create(contentStream))
{
deserialized = (TestClass)serializer.Deserialize(reader);
}
</pre>
<h2>Serialization/Deserialization with Settings Overrides</h2>
<p>Additionally, <code>ExtendedXmlSerializer</code> also supports overrides for serialization and deserialization that allow you to pass in <code>XmlWriterSettings</code> and <code>XmlReaderSettings</code> respectively. Here's an example of this for serialization:</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().Create();
TestClass instance = new TestClass();
MemoryStream stream = new MemoryStream();
string contents = serializer.Serialize(new XmlWriterSettings { /* ... */ }, stream, instance);
</pre>
<p>And for deserialization:</p>
<pre lang="cs">
MemoryStream contentStream = new MemoryStream(Encoding.UTF8.GetBytes(contents));
TestClass deserialized = serializer.Deserialize<TestClass>(new XmlReaderSettings{ /* ... */ }, contentStream);
</pre>
<h2>Fluent API</h2>
<p>ExtendedXmlSerializer use fluent API to configuration. Example:</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer()
.UseEncryptionAlgorithm(new CustomEncryption())
.Type<Person>() // Configuration of Person class
.Member(p => p.Password) // First member
.Name("P")
.Encrypt()
.Member(p => p.Name) // Second member
.Name("T")
.Type<TestClass>() // Configuration of another class
.CustomSerializer(new TestClassSerializer())
.Create();
</pre>
<h2>Serialization of dictionary</h2>
<p>You can serialize generic dictionary, that can store any type.</p>
<pre lang="cs">
public class TestClass
{
public Dictionary<int, string> Dictionary { get; set; }
}
</pre>
<pre lang="cs">
TestClass obj = new TestClass
{
Dictionary = new Dictionary<int, string>
{
{1, "First"},
{2, "Second"},
{3, "Other"},
}
};
</pre>
<p>Output XML will look like:</p>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<TestClass xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Dictianary;assembly=ExtendedXmlSerializer.Samples">
<Dictionary>
<Item xmlns="https://extendedxmlserializer.github.io/system">
<Key>1</Key>
<Value>First</Value>
</Item>
<Item xmlns="https://extendedxmlserializer.github.io/system">
<Key>2</Key>
<Value>Second</Value>
</Item>
<Item xmlns="https://extendedxmlserializer.github.io/system">
<Key>3</Key>
<Value>Other</Value>
</Item>
</Dictionary>
</TestClass>
</pre>
<p>If you use UseOptimizedNamespaces function xml will look like:</p>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<TestClass xmlns:sys="https://extendedxmlserializer.github.io/system" xmlns:exs="https://extendedxmlserializer.github.io/v2" xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Dictianary;assembly=ExtendedXmlSerializer.Samples">
<Dictionary>
<sys:Item>
<Key>1</Key>
<Value>First</Value>
</sys:Item>
<sys:Item>
<Key>2</Key>
<Value>Second</Value>
</sys:Item>
<sys:Item>
<Key>3</Key>
<Value>Other</Value>
</sys:Item>
</Dictionary>
</TestClass>
</pre>
<h2>Custom serialization</h2>
<p>If your class has to be serialized in a non-standard way:</p>
<pre lang="cs">
public class TestClass
{
public TestClass(string paramStr, int paramInt)
{
PropStr = paramStr;
PropInt = paramInt;
}
public string PropStr { get; private set; }
public int PropInt { get; private set; }
}
</pre>
<p>You must create custom serializer:</p>
<pre lang="cs">
public class TestClassSerializer : IExtendedXmlCustomSerializer<TestClass>
{
public TestClass Deserialize(XElement element)
{
XElement xElement = element.Member("String");
XElement xElement1 = element.Member("Int");
if (xElement != null && xElement1 != null)
{
string strValue = xElement.Value;
int intValue = Convert.ToInt32(xElement1.Value);
return new TestClass(strValue, intValue);
}
throw new InvalidOperationException("Invalid xml for class TestClassWithSerializer");
}
public void Serializer(XmlWriter writer, TestClass obj)
{
writer.WriteElementString("String", obj.PropStr);
writer.WriteElementString("Int", obj.PropInt.ToString(CultureInfo.InvariantCulture));
}
}
</pre>
<p>Then, you have to add custom serializer to configuration of TestClass:</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().Type<TestClass>()
.CustomSerializer(new TestClassSerializer())
.Create();
</pre>
<h2>Deserialize old version of xml</h2>
<p>In standard <code>XMLSerializer</code> you can't deserialize XML in case you change model. In <code>ExtendedXMLSerializer</code> you can create migrator for each class separately. E.g.: If you have big class, that uses small class and this small class will be changed you can create migrator only for this small class. You don't have to modify whole big XML. Now I will show you a simple example:</p>
<p>If you had a class:</p>
<pre lang="cs">
public class TestClass
{
public int Id { get; set; }
public string Type { get; set; }
}
</pre>
<p>and generated XML look like:</p>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<TestClass xmlns="clr-namespace:ExtendedXmlSerialization.Samples.MigrationMap;assembly=ExtendedXmlSerializer.Samples">
<Id>1</Id>
<Type>Type</Type>
</TestClass>
</pre>
<p>Then you renamed property:</p>
<pre lang="cs">
public class TestClass
{
public int Id { get; set; }
public string Name { get; set; }
}
</pre>
<p>and generated XML look like:</p>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<TestClass xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:version="1" xmlns="clr-namespace:ExtendedXmlSerialization.Samples.MigrationMap;assembly=ExtendedXmlSerializer.Samples">
<Id>1</Id>
<Name>Type</Name>
</TestClass>
</pre>
<p>Then, you added new property and you wanted to calculate a new value during deserialization.</p>
<pre lang="cs">
public class TestClass
{
public int Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
</pre>
<p>and new XML should look like:</p>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<TestClass xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:version="2" xmlns="clr-namespace:ExtendedXmlSerializer.Samples.MigrationMap;assembly=ExtendedXmlSerializer.Samples">
<Id>1</Id>
<Name>Type</Name>
<Value>Calculated</Value>
</TestClass>
</pre>
<p>You can migrate (read) old version of XML using migrations:</p>
<pre lang="cs">
public class TestClassMigrations : IEnumerable<Action<XElement>>
{
public static void MigrationV0(XElement node)
{
XElement typeElement = node.Member("Type");
// Add new node
node.Add(new XElement("Name", typeElement.Value));
// Remove old node
typeElement.Remove();
}
public static void MigrationV1(XElement node)
{
// Add new node
node.Add(new XElement("Value", "Calculated"));
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<Action<XElement>> GetEnumerator()
{
yield return MigrationV0;
yield return MigrationV1;
}
}
</pre>
<p>Then, you must register your <code>TestClassMigrations</code> class in configuration</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().ConfigureType<TestClass>()
.AddMigration(new TestClassMigrations())
.Create();
</pre>
<h2>Extensibility</h2>
<p>With type and member configuration out of the way, we can turn our attention to what really makes ExtendedXmlSeralizer tick: extensibility. As its name suggests, ExtendedXmlSeralizer offers a very flexible (but albeit new) extension model from which you can build your own extensions. Pretty much all if not all features you encounter with ExtendedXmlSeralizer are through extensions. There are quite a few in our latest version here that showcase this extensibility. The remainder of this document will showcase the top features of ExtendedXmlSerializer that are accomplished through its extension system.</p>
<p></p>
<h2>Object reference and circular reference</h2>
<p>If you have a class:</p>
<pre lang="cs">
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public Person Boss { get; set; }
}
public class Company
{
public List<Person> Employees { get; set; }
}
</pre>
<p>then you create object with circular reference, like this:</p>
<pre lang="cs">
Person boss = new Person {Id = 1, Name = "John"};
boss.Boss = boss; //himself boss
Person worker = new Person {Id = 2, Name = "Oliver"};
worker.Boss = boss;
Company obj = new Company
{
Employees = new List<Person>
{
worker,
boss
}
};
</pre>
<p>You must configure Person class as reference object:</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().ConfigureType<Person>()
.EnableReferences(p => p.Id)
.Create();
</pre>
<p>Output XML will look like this:</p>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<Company xmlns="clr-namespace:ExtendedXmlSerializer.Samples.ObjectReference;assembly=ExtendedXmlSerializer.Samples">
<Employees>
<Capacity>4</Capacity>
<Person Id="2">
<Name>Oliver</Name>
<Boss Id="1">
<Name>John</Name>
<Boss xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:entity="1" />
</Boss>
</Person>
<Person xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:entity="1" />
</Employees>
</Company>
</pre>
<h2>Property Encryption</h2>
<p>If you have a class with a property that needs to be encrypted:</p>
<pre lang="cs">
public class Person
{
public string Name { get; set; }
public string Password { get; set; }
}
</pre>
<p>You must implement interface IEncryption. For example, it will show the Base64 encoding, but in the real world better to use something safer, eg. RSA.:</p>
<pre lang="cs">
public class CustomEncryption : IEncryption
{
public string Parse(string data)
=> Encoding.UTF8.GetString(Convert.FromBase64String(data));
public string Format(string instance)
=> Convert.ToBase64String(Encoding.UTF8.GetBytes(instance));
}
</pre>
<p>Then, you have to specify which properties are to be encrypted and register your IEncryption implementation.</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().UseEncryptionAlgorithm(new CustomEncryption())
.ConfigureType<Person>()
.Member(p => p.Password)
.Encrypt()
.Create();
</pre>
<h2>Custom Conversion</h2>
<p>ExtendedXmlSerializer does a pretty decent job (if we do say so ourselves) of composing and decomposing objects, but if you happen to have a type that you want serialized in a certain way, and this type can be destructured into a <code>string</code>, then you can register a custom converter for it.</p>
<p></p>
<p>Using the following:</p>
<pre lang="cs">
public sealed class CustomStructConverter : IConverter<CustomStruct>
{
public static CustomStructConverter Default { get; } = new CustomStructConverter();
CustomStructConverter() {}
public bool IsSatisfiedBy(TypeInfo parameter) => typeof(CustomStruct).GetTypeInfo()
.IsAssignableFrom(parameter);
public CustomStruct Parse(string data) =>
int.TryParse(data, out int number) ? new CustomStruct(number) : CustomStruct.Default;
public string Format(CustomStruct instance) => instance.Number.ToString();
}
public struct CustomStruct
{
public static CustomStruct Default { get; } = new CustomStruct(6776);
public CustomStruct(int number)
{
Number = number;
}
public int Number { get; }
}
</pre>
<p>Register the converter:</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().Register(CustomStructConverter.Default).Create();
CustomStruct subject = new CustomStruct(123);
string contents = serializer.Serialize(subject);
// ...
</pre>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<CustomStruct xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">123</CustomStruct>
</pre>
<h2>Optimized Namespaces</h2>
<p>By default Xml namespaces are emitted on an "as needed" basis:</p>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<List xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:arguments="Object" xmlns="https://extendedxmlserializer.github.io/system">
<Capacity>4</Capacity>
<Subject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">
<Message>First</Message>
</Subject>
<Subject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">
<Message>Second</Message>
</Subject>
<Subject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">
<Message>Third</Message>
</Subject>
</List>
</pre>
<p>But with one call to the <code>UseOptimizedNamespaces</code> call, namespaces get placed at the root of the document, thereby reducing document footprint:</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().UseOptimizedNamespaces()
.Create();
List<object> subject = new List<object>
{
new Subject {Message = "First"},
new Subject {Message = "Second"},
new Subject {Message = "Third"}
};
string contents = serializer.Serialize(subject);
// ...
</pre>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<List xmlns:ns1="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples" xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:arguments="Object" xmlns="https://extendedxmlserializer.github.io/system">
<Capacity>4</Capacity>
<ns1:Subject>
<Message>First</Message>
</ns1:Subject>
<ns1:Subject>
<Message>Second</Message>
</ns1:Subject>
<ns1:Subject>
<Message>Third</Message>
</ns1:Subject>
</List>
</pre>
<h2>Implicit Namespaces/Typing</h2>
<p>If you don't like namespaces at all, you can register types so that they do not emit namespaces when they are rendered into a document:</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().EnableImplicitTyping(typeof(Subject))
.Create();
Subject subject = new Subject{ Message = "Hello World! No namespaces, yay!" };
string contents = serializer.Serialize(subject);
// ...
</pre>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<Subject>
<Message>Hello World! No namespaces, yay!</Message>
</Subject>
</pre>
<h2>Auto-Formatting (Attributes)</h2>
<p>The default behavior for emitting data in an Xml document is to use elements, which can be a little chatty and verbose:</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().UseOptimizedNamespaces()
.Create();
List<object> subject = new List<object>
{
new Subject {Message = "First"},
new Subject {Message = "Second"},
new Subject {Message = "Third"}
};
string contents = serializer.Serialize(subject);
// ...
</pre>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<SubjectWithThreeProperties xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">
<Number>123</Number>
<Message>Hello World!</Message>
<Time>2018-05-26T11:52:19.4981212-04:00</Time>
</SubjectWithThreeProperties>
</pre>
<p>Making use of the <code>UseAutoFormatting</code> call will enable all types that have a registered <code>IConverter</code> (convert to string and back) to emit as attributes:</p>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<SubjectWithThreeProperties Number="123" Message="Hello World!" Time="2018-05-26T11:52:19.4981212-04:00" xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples" />
</pre>
<h2>Verbatim Content (CDATA)</h2>
<p>If you have an element with a member that can hold lots of data, or data that has illegal characters, you configure it to be a verbatim field and it will emit a CDATA section around it:</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().Type<Subject>()
.Member(x => x.Message)
.Verbatim()
.Create();
Subject subject = new Subject {Message = @"<{""Ilegal characters and such""}>"};
string contents = serializer.Serialize(subject);
// ...
</pre>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<Subject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">
<Message><![CDATA[<{"Ilegal characters and such"}>]]></Message>
</Subject>
</pre>
<p>You can also denote these fields with an attribute and get the same functionality:</p>
<pre lang="cs">
public sealed class VerbatimSubject
{
[Verbatim]
public string Message { get; set; }
}
</pre>
<h2>Private Constructors</h2>
<p>One of the limitations of the classic <code>XmlSerializer</code> is that it does not support private constructors, but <code>ExtendedXmlSerializer</code> does via its <code>EnableAllConstructors</code> call:</p>
<pre lang="cs">
public sealed class SubjectByFactory
{
public static SubjectByFactory Create(string message) => new SubjectByFactory(message);
SubjectByFactory() : this(null) {} // Used by serializer.
SubjectByFactory(string message) => Message = message;
public string Message { get; set; }
}
</pre>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().EnableAllConstructors()
.Create();
SubjectByFactory subject = SubjectByFactory.Create("Hello World!");
string contents = serializer.Serialize(subject);
// ...
</pre>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<SubjectByFactory xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">
<Message>Hello World!</Message>
</SubjectByFactory>
</pre>
<h2>Parameterized Members and Content</h2>
<p>Taking this concept bit further leads to a favorite feature of ours in <code>ExtendedXmlSerlializer</code>. The classic serializer only supports parameterless public constructors. With <code>ExtendedXmlSerializer</code>, you can use the <code>EnableParameterizedContent</code> call to enable parameterized parameters in the constructor that by convention have the same name as the property for which they are meant to assign:</p>
<pre lang="cs">
public sealed class ParameterizedSubject
{
public ParameterizedSubject(string message, int number, DateTime time)
{
Message = message;
Number = number;
Time = time;
}
public string Message { get; }
public int Number { get; }
public DateTime Time { get; }
}
</pre>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().EnableParameterizedContent()
.Create();
ParameterizedSubject subject = new ParameterizedSubject("Hello World!", 123, DateTime.Now);
string contents = serializer.Serialize(subject);
// ...
</pre>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<ParameterizedSubject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">
<Message>Hello World!</Message>
<Number>123</Number>
<Time>2018-05-26T11:52:19.7551187-04:00</Time>
</ParameterizedSubject>
</pre>
<h2>Tuples</h2>
<p>By enabling parameterized content, it opens up a lot of possibilities, like being able to serialize Tuples. Of course, serializable Tuples were introduced recently with the latest version of C#. Here, however, you can couple this with our member-naming funtionality and provide better naming for your tuple properties:</p>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer().EnableParameterizedContent()
.Type<Tuple<string>>()
.Member(x => x.Item1)
.Name("Message")
.Create();
Tuple<string> subject = Tuple.Create("Hello World!");
string contents = serializer.Serialize(subject);
// ...
</pre>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<Tuple xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:arguments="string" xmlns="https://extendedxmlserializer.github.io/system">
<Message>Hello World!</Message>
</Tuple>
</pre>
<h2>Experimental Xaml-ness: Attached Properties</h2>
<p>We went ahead and got a little cute with v2 of <code>ExtendedXmlSerializer</code>, adding support for Attached Properties on objects in your serialized object graph. But instead of constraining it to objects that inherit from <code>DependencyObject</code>, *every* object can benefit from it. Check it out:</p>
<pre lang="cs">
sealed class NameProperty : ReferenceProperty<Subject, string>
{
public const string DefaultMessage = "The Name Has Not Been Set";
public static NameProperty Default { get; } = new NameProperty();
NameProperty() : base(() => Default, x => DefaultMessage) {}
}
sealed class NumberProperty : StructureProperty<Subject, int>
{
public const int DefaultValue = 123;
public static NumberProperty Default { get; } = new NumberProperty();
NumberProperty() : base(() => Default, x => DefaultValue) {}
}
</pre>
<pre lang="cs">
IExtendedXmlSerializer serializer = new ConfigurationContainer()
.EnableAttachedProperties(NameProperty.Default, NumberProperty.Default)
.Create();
Subject subject = new Subject {Message = "Hello World!"};
subject.Set(NameProperty.Default, "Hello World from Attached Properties!");
subject.Set(NumberProperty.Default, 123);
string contents = serializer.Serialize(subject);
// ...
</pre>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<Subject xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples">
<Message>Hello World!</Message>
<NameProperty.Default>Hello World from Attached Properties!</NameProperty.Default>
<NumberProperty.Default>123</NumberProperty.Default>
</Subject>
</pre>
<p>(Please note that this feature is experimental, but please try it out and let us know what you think!)</p>
<p></p>
<h2>Experimental Xaml-ness: Markup Extensions</h2>
<p>Saving the best feaure for last, we have experimental support for one of Xaml's greatest features, Markup Extensions:</p>
<pre lang="cs">
sealed class Extension : IMarkupExtension
{
const string Message = "Hello World from Markup Extension! Your message is: ", None = "N/A";
readonly string _message;
public Extension() : this(None) {}
public Extension(string message)
{
_message = message;
}
public object ProvideValue(IServiceProvider serviceProvider) => string.Concat(Message, _message);
}
</pre>
<pre lang="cs">
string contents =
@"<?xml version=""1.0"" encoding=""utf-8""?>
<Subject xmlns=""clr-namespace:ExtendedXmlSerializer.Samples.Extensibility;assembly=ExtendedXmlSerializer.Samples""
Message=""{Extension 'PRETTY COOL HUH!!!'}"" />";
IExtendedXmlSerializer serializer = new ConfigurationContainer().EnableMarkupExtensions()
.Create();
Subject subject = serializer.Deserialize<Subject>(contents);
Console.WriteLine(subject.Message); // "Hello World from Markup Extension! Your message is: PRETTY COOL HUH!!!"
</pre>
<p>(Please note that this feature is experimental, but please try it out and let us know what you think!)</p>
<p></p>
<h2>How to Upgrade from v1.x to v2</h2>
<p>Finally, if you have documents from v1, you will need to upgrade them to v2 to work. This involves reading the document in an instance of v1 serializer, and then writing it in an instance of v2 serializer. We have provided the <code>ExtendedXmlSerializer.Legacy</code> nuget package to assist in this goal.</p>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?><ArrayOfSubject><Subject type="ExtendedXmlSerializer.Samples.Introduction.Subject"><Message>First</Message><Count>0</Count></Subject><Subject type="ExtendedXmlSerializer.Samples.Introduction.Subject"><Message>Second</Message><Count>0</Count></Subject><Subject type="ExtendedXmlSerializer.Samples.Introduction.Subject"><Message>Third</Message><Count>0</Count></Subject></ArrayOfSubject>
</pre>
<pre lang="cs">
ExtendedXmlSerialization.ExtendedXmlSerializer legacySerializer = new ExtendedXmlSerialization.ExtendedXmlSerializer();
string content = File.ReadAllText(@"bin\Upgrade.Example.v1.xml"); // Path to your legacy xml file.
List<Subject> subject = legacySerializer.Deserialize<List<Subject>>(content);
// Upgrade:
IExtendedXmlSerializer serializer = new ConfigurationContainer().Create();
string contents = serializer.Serialize(new XmlWriterSettings {Indent = true}, subject);
File.WriteAllText(@"bin\Upgrade.Example.v2.xml", contents);
// ...
</pre>
<pre lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<List xmlns:ns1="clr-namespace:ExtendedXmlSerializer.Samples.Introduction;assembly=ExtendedXmlSerializer.Samples" xmlns:exs="https://extendedxmlserializer.github.io/v2" exs:arguments="ns1:Subject" xmlns="https://extendedxmlserializer.github.io/system">
<Capacity>4</Capacity>
<ns1:Subject>
<Message>First</Message>
<Count>0</Count>
</ns1:Subject>
<ns1:Subject>
<Message>Second</Message>
<Count>0</Count>