LotSizingDataModel.Instance 2.0.1
Lot-sizing instance representation, descriptors and problem characterization.
Loading...
Searching...
No Matches
LotSizingInstanceXmlSerializer.cs
Go to the documentation of this file.
1using System;
2using System.IO;
3using System.Text;
4using System.Xml;
5using System.Xml.Serialization;
7
9
10/// <summary>
11/// Serializes and deserializes complete lot-sizing problem
12/// instances using XML.
13/// </summary>
14/// <remarks>
15/// The serializer supports:
16/// <list type="bullet">
17/// <item>
18/// <description>serialization to a file;</description>
19/// </item>
20/// <item>
21/// <description>serialization to a stream;</description>
22/// </item>
23/// <item>
24/// <description>serialization to an XML string;</description>
25/// </item>
26/// <item>
27/// <description>deserialization from the same sources;</description>
28/// </item>
29/// <item>
30/// <description>
31/// optional validation before writing and after reading.
32/// </description>
33/// </item>
34/// </list>
35///
36/// File serialization uses UTF-8 without a byte-order mark.
37/// Files are first written to a temporary file and then moved
38/// to their final location in order to reduce the risk of
39/// leaving a partially written instance file.
40/// </remarks>
42{
43 /// <summary>
44 /// Gets the encoding used when writing XML instance files.
45 /// </summary>
46 public static Encoding XmlEncoding { get; } =
47 new UTF8Encoding(
48 encoderShouldEmitUTF8Identifier: false);
49
50 /// <summary>
51 /// Serializes an instance to an XML file.
52 /// </summary>
53 /// <param name="instance">
54 /// Instance to serialize.
55 /// </param>
56 /// <param name="filePath">
57 /// Destination XML file path.
58 /// </param>
59 /// <param name="validateBeforeSerialization">
60 /// Value indicating whether the instance must be validated
61 /// before serialization.
62 /// </param>
63 /// <param name="validateCurrentFingerprint">
64 /// Value indicating whether validation must compare
65 /// recorded fingerprints with the current supply-chain
66 /// fingerprint.
67 /// </param>
68 /// <param name="indent">
69 /// Value indicating whether the generated XML must be
70 /// indented.
71 /// </param>
72 /// <exception cref="ArgumentNullException">
73 /// Thrown when <paramref name="instance"/> is
74 /// <see langword="null"/>.
75 /// </exception>
76 /// <exception cref="ArgumentException">
77 /// Thrown when <paramref name="filePath"/> is empty.
78 /// </exception>
79 /// <exception cref="DirectoryNotFoundException">
80 /// Thrown when the destination directory does not exist.
81 /// </exception>
82 /// <exception cref="InvalidOperationException">
83 /// Thrown when validation fails or the instance cannot be
84 /// serialized.
85 /// </exception>
86 public static void SerializeToFile(
87 LotSizingInstance instance,
88 string filePath,
89 bool validateBeforeSerialization = true,
90 bool validateCurrentFingerprint = true,
91 bool indent = true)
92 {
93 ArgumentNullException.ThrowIfNull(instance);
94
95 if (string.IsNullOrWhiteSpace(filePath))
96 {
97 throw new ArgumentException(
98 "A destination file path is required.",
99 nameof(filePath));
100 }
101
102 string fullFilePath =
103 Path.GetFullPath(
104 filePath.Trim());
105
106 string? directoryPath =
107 Path.GetDirectoryName(
108 fullFilePath);
109
110 if (!string.IsNullOrWhiteSpace(directoryPath) &&
111 !Directory.Exists(directoryPath))
112 {
113 throw new DirectoryNotFoundException(
114 $"The destination directory " +
115 $"'{directoryPath}' does not exist.");
116 }
117
118 string temporaryFilePath =
119 fullFilePath +
120 "." +
121 Guid.NewGuid().ToString("N") +
122 ".tmp";
123
124 try
125 {
126 using (var stream =
127 new FileStream(
128 temporaryFilePath,
129 FileMode.CreateNew,
130 FileAccess.Write,
131 FileShare.None))
132 {
133 Serialize(
134 instance:
135 instance,
136
137 stream:
138 stream,
139
140 validateBeforeSerialization:
141 validateBeforeSerialization,
142
143 validateCurrentFingerprint:
144 validateCurrentFingerprint,
145
146 indent:
147 indent);
148
149 stream.Flush(
150 flushToDisk: true);
151 }
152
153 File.Move(
154 temporaryFilePath,
155 fullFilePath,
156 overwrite: true);
157 }
158 finally
159 {
160 if (File.Exists(temporaryFilePath))
161 {
162 File.Delete(
163 temporaryFilePath);
164 }
165 }
166 }
167
168 /// <summary>
169 /// Serializes an instance to a writable stream.
170 /// </summary>
171 /// <param name="instance">
172 /// Instance to serialize.
173 /// </param>
174 /// <param name="stream">
175 /// Writable destination stream.
176 /// </param>
177 /// <param name="validateBeforeSerialization">
178 /// Value indicating whether the instance must be validated
179 /// before serialization.
180 /// </param>
181 /// <param name="validateCurrentFingerprint">
182 /// Value indicating whether validation must compare
183 /// recorded fingerprints with the current supply-chain
184 /// fingerprint.
185 /// </param>
186 /// <param name="indent">
187 /// Value indicating whether the generated XML must be
188 /// indented.
189 /// </param>
190 /// <remarks>
191 /// Serialization begins at the current stream position.
192 ///
193 /// The supplied stream remains open after serialization.
194 /// </remarks>
195 /// <exception cref="ArgumentNullException">
196 /// Thrown when <paramref name="instance"/> or
197 /// <paramref name="stream"/> is <see langword="null"/>.
198 /// </exception>
199 /// <exception cref="ArgumentException">
200 /// Thrown when the stream is not writable.
201 /// </exception>
202 /// <exception cref="InvalidOperationException">
203 /// Thrown when validation fails or the instance cannot be
204 /// serialized.
205 /// </exception>
206 public static void Serialize(
207 LotSizingInstance instance,
208 Stream stream,
209 bool validateBeforeSerialization = true,
210 bool validateCurrentFingerprint = true,
211 bool indent = true)
212 {
213 ArgumentNullException.ThrowIfNull(instance);
214 ArgumentNullException.ThrowIfNull(stream);
215
216 if (!stream.CanWrite)
217 {
218 throw new ArgumentException(
219 "The destination stream must be writable.",
220 nameof(stream));
221 }
222
223 if (validateBeforeSerialization)
224 {
226 instance:
227 instance,
228
229 validateCurrentFingerprint:
230 validateCurrentFingerprint);
231 }
232
233 XmlWriterSettings writerSettings =
234 CreateWriterSettings(
235 indent);
236
237 XmlSerializerNamespaces namespaces =
238 CreateEmptyNamespaces();
239
240 XmlSerializer serializer =
241 CreateSerializer();
242
243 try
244 {
245 using XmlWriter writer =
246 XmlWriter.Create(
247 stream,
248 writerSettings);
249
250 serializer.Serialize(
251 writer,
252 instance,
253 namespaces);
254
255 writer.Flush();
256 }
257 catch (InvalidOperationException exception)
258 {
259 throw new InvalidOperationException(
260 "The lot-sizing instance could not be " +
261 "serialized to XML.",
262 exception);
263 }
264 }
265
266 /// <summary>
267 /// Serializes an instance to an XML string.
268 /// </summary>
269 /// <param name="instance">
270 /// Instance to serialize.
271 /// </param>
272 /// <param name="validateBeforeSerialization">
273 /// Value indicating whether the instance must be validated
274 /// before serialization.
275 /// </param>
276 /// <param name="validateCurrentFingerprint">
277 /// Value indicating whether validation must compare
278 /// recorded fingerprints with the current supply-chain
279 /// fingerprint.
280 /// </param>
281 /// <param name="indent">
282 /// Value indicating whether the generated XML must be
283 /// indented.
284 /// </param>
285 /// <returns>
286 /// UTF-8 XML representation of the instance.
287 /// </returns>
288 public static string SerializeToString(
289 LotSizingInstance instance,
290 bool validateBeforeSerialization = true,
291 bool validateCurrentFingerprint = true,
292 bool indent = true)
293 {
294 ArgumentNullException.ThrowIfNull(instance);
295
296 using var stream =
297 new MemoryStream();
298
299 Serialize(
300 instance:
301 instance,
302
303 stream:
304 stream,
305
306 validateBeforeSerialization:
307 validateBeforeSerialization,
308
309 validateCurrentFingerprint:
310 validateCurrentFingerprint,
311
312 indent:
313 indent);
314
315 return XmlEncoding.GetString(
316 stream.ToArray());
317 }
318
319 /// <summary>
320 /// Deserializes a lot-sizing instance from an XML file.
321 /// </summary>
322 /// <param name="filePath">
323 /// Source XML file path.
324 /// </param>
325 /// <param name="validateAfterDeserialization">
326 /// Value indicating whether the reconstructed instance
327 /// must be validated.
328 /// </param>
329 /// <param name="validateCurrentFingerprint">
330 /// Value indicating whether validation must compare
331 /// recorded fingerprints with the reconstructed current
332 /// supply-chain fingerprint.
333 /// </param>
334 /// <returns>
335 /// Deserialized lot-sizing instance.
336 /// </returns>
337 /// <exception cref="ArgumentException">
338 /// Thrown when <paramref name="filePath"/> is empty.
339 /// </exception>
340 /// <exception cref="FileNotFoundException">
341 /// Thrown when the source file does not exist.
342 /// </exception>
343 /// <exception cref="InvalidOperationException">
344 /// Thrown when the XML cannot be deserialized or when the
345 /// reconstructed instance is invalid.
346 /// </exception>
348 string filePath,
349 bool validateAfterDeserialization = true,
350 bool validateCurrentFingerprint = true)
351 {
352 if (string.IsNullOrWhiteSpace(filePath))
353 {
354 throw new ArgumentException(
355 "A source file path is required.",
356 nameof(filePath));
357 }
358
359 string fullFilePath =
360 Path.GetFullPath(
361 filePath.Trim());
362
363 if (!File.Exists(fullFilePath))
364 {
365 throw new FileNotFoundException(
366 "The lot-sizing instance file was not found.",
367 fullFilePath);
368 }
369
370 using var stream =
371 new FileStream(
372 fullFilePath,
373 FileMode.Open,
374 FileAccess.Read,
375 FileShare.Read);
376
377 return Deserialize(
378 stream:
379 stream,
380
381 validateAfterDeserialization:
382 validateAfterDeserialization,
383
384 validateCurrentFingerprint:
385 validateCurrentFingerprint);
386 }
387
388 /// <summary>
389 /// Deserializes a lot-sizing instance from a readable
390 /// stream.
391 /// </summary>
392 /// <param name="stream">
393 /// Readable stream containing the XML document.
394 /// </param>
395 /// <param name="validateAfterDeserialization">
396 /// Value indicating whether the reconstructed instance
397 /// must be validated.
398 /// </param>
399 /// <param name="validateCurrentFingerprint">
400 /// Value indicating whether validation must compare
401 /// recorded fingerprints with the reconstructed current
402 /// supply-chain fingerprint.
403 /// </param>
404 /// <returns>
405 /// Deserialized lot-sizing instance.
406 /// </returns>
407 /// <remarks>
408 /// Reading begins at the current stream position.
409 ///
410 /// The supplied stream remains open after deserialization.
411 /// </remarks>
412 /// <exception cref="ArgumentNullException">
413 /// Thrown when <paramref name="stream"/> is
414 /// <see langword="null"/>.
415 /// </exception>
416 /// <exception cref="ArgumentException">
417 /// Thrown when the stream is not readable.
418 /// </exception>
419 /// <exception cref="InvalidOperationException">
420 /// Thrown when the XML cannot be deserialized or when the
421 /// reconstructed instance is invalid.
422 /// </exception>
424 Stream stream,
425 bool validateAfterDeserialization = true,
426 bool validateCurrentFingerprint = true)
427 {
428 ArgumentNullException.ThrowIfNull(stream);
429
430 if (!stream.CanRead)
431 {
432 throw new ArgumentException(
433 "The source stream must be readable.",
434 nameof(stream));
435 }
436
437 XmlReaderSettings readerSettings =
438 CreateReaderSettings();
439
440 using XmlReader reader =
441 XmlReader.Create(
442 stream,
443 readerSettings);
444
445 return DeserializeFromReader(
446 reader:
447 reader,
448
449 validateAfterDeserialization:
450 validateAfterDeserialization,
451
452 validateCurrentFingerprint:
453 validateCurrentFingerprint);
454 }
455
456 /// <summary>
457 /// Deserializes a lot-sizing instance from an XML string.
458 /// </summary>
459 /// <param name="xml">
460 /// XML representation of the instance.
461 /// </param>
462 /// <param name="validateAfterDeserialization">
463 /// Value indicating whether the reconstructed instance
464 /// must be validated.
465 /// </param>
466 /// <param name="validateCurrentFingerprint">
467 /// Value indicating whether validation must compare
468 /// recorded fingerprints with the reconstructed current
469 /// supply-chain fingerprint.
470 /// </param>
471 /// <returns>
472 /// Deserialized lot-sizing instance.
473 /// </returns>
474 /// <exception cref="ArgumentException">
475 /// Thrown when <paramref name="xml"/> is empty.
476 /// </exception>
477 /// <exception cref="InvalidOperationException">
478 /// Thrown when the XML cannot be deserialized or when the
479 /// reconstructed instance is invalid.
480 /// </exception>
482 string xml,
483 bool validateAfterDeserialization = true,
484 bool validateCurrentFingerprint = true)
485 {
486 if (string.IsNullOrWhiteSpace(xml))
487 {
488 throw new ArgumentException(
489 "An XML document is required.",
490 nameof(xml));
491 }
492
493 using var stringReader =
494 new StringReader(
495 xml);
496
497 XmlReaderSettings readerSettings =
498 CreateReaderSettings();
499
500 using XmlReader reader =
501 XmlReader.Create(
502 stringReader,
503 readerSettings);
504
505 return DeserializeFromReader(
506 reader:
507 reader,
508
509 validateAfterDeserialization:
510 validateAfterDeserialization,
511
512 validateCurrentFingerprint:
513 validateCurrentFingerprint);
514 }
515
516 private static LotSizingInstance DeserializeFromReader(
517 XmlReader reader,
518 bool validateAfterDeserialization,
519 bool validateCurrentFingerprint)
520 {
521 XmlSerializer serializer =
522 CreateSerializer();
523
524 var document = LotSizingDataModel.Core.Serialization.TransportXmlMigration.ReadAndMigrate(reader);
525 using var migratedReader = document.CreateReader();
526 object? deserializedObject;
527
528 try
529 {
530 deserializedObject =
531 serializer.Deserialize(
532 migratedReader);
533 }
534 catch (InvalidOperationException exception)
535 {
536 throw new InvalidOperationException(
537 "The XML document could not be deserialized " +
538 "as a lot-sizing instance.",
539 exception);
540 }
541
542 if (deserializedObject is not
543 LotSizingInstance instance)
544 {
545 throw new InvalidOperationException(
546 "The XML document does not contain a valid " +
547 "lot-sizing instance root object.");
548 }
549
550 if (validateAfterDeserialization)
551 {
553 instance:
554 instance,
555
556 validateCurrentFingerprint:
557 validateCurrentFingerprint);
558 }
559
560 return instance;
561 }
562
563 private static XmlSerializer CreateSerializer()
564 {
565 return new XmlSerializer(
566 typeof(LotSizingInstance));
567 }
568
569 private static XmlSerializerNamespaces
570 CreateEmptyNamespaces()
571 {
572 var namespaces =
573 new XmlSerializerNamespaces();
574
575 namespaces.Add(
576 string.Empty,
577 string.Empty);
578
579 return namespaces;
580 }
581
582 private static XmlWriterSettings CreateWriterSettings(
583 bool indent)
584 {
585 return new XmlWriterSettings
586 {
587 Encoding =
589
590 Indent =
591 indent,
592
593 IndentChars =
594 " ",
595
596 OmitXmlDeclaration =
597 false,
598
599 NewLineHandling =
600 NewLineHandling.None,
601
602 CloseOutput =
603 false
604 };
605 }
606
607 private static XmlReaderSettings CreateReaderSettings()
608 {
609 return new XmlReaderSettings
610 {
611 DtdProcessing =
612 DtdProcessing.Prohibit,
613
614 XmlResolver =
615 null,
616
617 IgnoreComments =
618 false,
619
620 IgnoreWhitespace =
621 false,
622
623 CloseInput =
624 false
625 };
626 }
627}
Adds explicit closed-loop return streams to a lot-sizing instance without changing the historical Sup...
Serializes and deserializes complete lot-sizing problem instances using XML.
static Encoding XmlEncoding
Gets the encoding used when writing XML instance files.
static LotSizingInstance DeserializeFromFile(string filePath, bool validateAfterDeserialization=true, bool validateCurrentFingerprint=true)
Deserializes a lot-sizing instance from an XML file.
static LotSizingInstance Deserialize(Stream stream, bool validateAfterDeserialization=true, bool validateCurrentFingerprint=true)
Deserializes a lot-sizing instance from a readable stream.
static void SerializeToFile(LotSizingInstance instance, string filePath, bool validateBeforeSerialization=true, bool validateCurrentFingerprint=true, bool indent=true)
Serializes an instance to an XML file.
static string SerializeToString(LotSizingInstance instance, bool validateBeforeSerialization=true, bool validateCurrentFingerprint=true, bool indent=true)
Serializes an instance to an XML string.
static LotSizingInstance DeserializeFromString(string xml, bool validateAfterDeserialization=true, bool validateCurrentFingerprint=true)
Deserializes a lot-sizing instance from an XML string.
static void Serialize(LotSizingInstance instance, Stream stream, bool validateBeforeSerialization=true, bool validateCurrentFingerprint=true, bool indent=true)
Serializes an instance to a writable stream.
Validates the structural and referential consistency of a complete lot-sizing problem instance.
static void EnsureValid(LotSizingInstance instance, bool validateCurrentFingerprint=true)
Validates a lot-sizing instance and throws an exception when at least one error is found.