LotSizingDataModel.Solution 2.0.1
Solution objects for production, setup, inventory and related decisions.
Loading...
Searching...
No Matches
LotSizingSolutionXmlSerializer.cs
Go to the documentation of this file.
1using System;
2using System.IO;
3using System.Text;
4using System.Xml;
5using System.Xml.Serialization;
6using LotSizingDataModel.Core;
8
10
11/// <summary>
12/// Serializes and deserializes lot-sizing solutions
13/// using the XML format.
14/// </summary>
15/// <remarks>
16/// XML loading prohibits DTD processing and external
17/// resource resolution.
18///
19/// After deserialization, nested property-change
20/// notifications are reconnected automatically.
21/// </remarks>
23{
24 private readonly XmlSerializer _serializer;
25 private readonly LotSizingSolutionValidator _validator;
26
27 /// <summary>
28 /// Initializes a solution XML serializer using
29 /// an indented XML representation.
30 /// </summary>
32 : this(
34 indentOutput: true)
35 {
36 }
37
38 /// <summary>
39 /// Initializes a solution XML serializer.
40 /// </summary>
41 /// <param name="validator">
42 /// Validator used when validation is requested.
43 /// </param>
44 /// <param name="indentOutput">
45 /// True to indent generated XML; otherwise, false.
46 /// </param>
49 bool indentOutput = true)
50 {
51 ArgumentNullException.ThrowIfNull(validator);
52
53 _validator = validator;
54 _serializer =
55 new XmlSerializer(
56 typeof(LotSizingSolution));
57
58 IndentOutput = indentOutput;
59 }
60
61 /// <summary>
62 /// Gets a value indicating whether generated XML
63 /// is indented.
64 /// </summary>
65 public bool IndentOutput { get; }
66
67 /// <summary>
68 /// Saves a solution to an XML file.
69 /// </summary>
70 /// <param name="filePath">
71 /// Destination XML file path.
72 /// </param>
73 /// <param name="solution">
74 /// Solution to serialize.
75 /// </param>
76 /// <param name="validateBeforeSave">
77 /// True to validate the internal structure before
78 /// serialization; otherwise, false.
79 /// </param>
80 /// <exception cref="ArgumentException">
81 /// Thrown when the file path is empty.
82 /// </exception>
83 /// <exception cref="InvalidOperationException">
84 /// Thrown when validation is requested and the solution
85 /// contains at least one validation error.
86 /// </exception>
87 public void Save(
88 string filePath,
89 LotSizingSolution solution,
90 bool validateBeforeSave = false)
91 {
92 ArgumentException.ThrowIfNullOrWhiteSpace(
93 filePath);
94
95 ArgumentNullException.ThrowIfNull(solution);
96
97 string fullPath =
98 Path.GetFullPath(filePath);
99
100 string? directoryPath =
101 Path.GetDirectoryName(fullPath);
102
103 if (!string.IsNullOrWhiteSpace(
104 directoryPath))
105 {
106 Directory.CreateDirectory(
107 directoryPath);
108 }
109
110 using var stream =
111 new FileStream(
112 fullPath,
113 FileMode.Create,
114 FileAccess.Write,
115 FileShare.None);
116
117 Save(
118 stream,
119 solution,
120 validateBeforeSave,
121 leaveOpen: false);
122 }
123
124 /// <summary>
125 /// Validates a solution against a supply-chain instance
126 /// and saves it to an XML file.
127 /// </summary>
128 /// <param name="filePath">
129 /// Destination XML file path.
130 /// </param>
131 /// <param name="solution">
132 /// Solution to serialize.
133 /// </param>
134 /// <param name="supplyChain">
135 /// Supply-chain instance against which the solution
136 /// must be validated.
137 /// </param>
138 /// <exception cref="InvalidOperationException">
139 /// Thrown when the solution is not compatible with
140 /// the supply-chain instance.
141 /// </exception>
142 public void Save(
143 string filePath,
144 LotSizingSolution solution,
145 SupplyChain supplyChain)
146 {
147 ArgumentNullException.ThrowIfNull(solution);
148 ArgumentNullException.ThrowIfNull(supplyChain);
149
150 _validator.ThrowIfInvalid(
151 solution,
152 supplyChain);
153
154 Save(
155 filePath,
156 solution,
157 validateBeforeSave: false);
158 }
159
160 /// <summary>
161 /// Saves a solution to a writable stream.
162 /// </summary>
163 /// <param name="stream">
164 /// Destination stream.
165 /// </param>
166 /// <param name="solution">
167 /// Solution to serialize.
168 /// </param>
169 /// <param name="validateBeforeSave">
170 /// True to validate the internal structure before
171 /// serialization; otherwise, false.
172 /// </param>
173 /// <param name="leaveOpen">
174 /// True to leave the stream open after serialization;
175 /// otherwise, false.
176 /// </param>
177 public void Save(
178 Stream stream,
179 LotSizingSolution solution,
180 bool validateBeforeSave = false,
181 bool leaveOpen = true)
182 {
183 ArgumentNullException.ThrowIfNull(stream);
184 ArgumentNullException.ThrowIfNull(solution);
185
186 if (!stream.CanWrite)
187 {
188 throw new ArgumentException(
189 "The destination stream is not writable.",
190 nameof(stream));
191 }
192
193 if (validateBeforeSave)
194 {
195 _validator.ThrowIfInvalid(solution);
196 }
197
198 XmlWriterSettings settings =
199 CreateWriterSettings(leaveOpen);
200
201 using XmlWriter writer =
202 XmlWriter.Create(
203 stream,
204 settings);
205
206 _serializer.Serialize(
207 writer,
208 solution);
209
210 writer.Flush();
211 }
212
213 /// <summary>
214 /// Loads a solution from an XML file.
215 /// </summary>
216 /// <param name="filePath">
217 /// Source XML file path.
218 /// </param>
219 /// <param name="validateAfterLoad">
220 /// True to validate the internal structure after
221 /// deserialization; otherwise, false.
222 /// </param>
223 /// <returns>
224 /// Deserialized lot-sizing solution.
225 /// </returns>
226 /// <exception cref="FileNotFoundException">
227 /// Thrown when the source file does not exist.
228 /// </exception>
229 /// <exception cref="InvalidDataException">
230 /// Thrown when the XML does not contain a valid
231 /// lot-sizing solution document.
232 /// </exception>
234 string filePath,
235 bool validateAfterLoad = false)
236 {
237 ArgumentException.ThrowIfNullOrWhiteSpace(
238 filePath);
239
240 string fullPath =
241 Path.GetFullPath(filePath);
242
243 using var stream =
244 new FileStream(
245 fullPath,
246 FileMode.Open,
247 FileAccess.Read,
248 FileShare.Read);
249
250 return Load(
251 stream,
252 validateAfterLoad,
253 leaveOpen: false);
254 }
255
256 /// <summary>
257 /// Loads a solution from an XML file and validates it
258 /// against a supply-chain instance.
259 /// </summary>
260 /// <param name="filePath">
261 /// Source XML file path.
262 /// </param>
263 /// <param name="supplyChain">
264 /// Supply-chain instance against which the loaded
265 /// solution must be validated.
266 /// </param>
267 /// <returns>
268 /// Deserialized and validated lot-sizing solution.
269 /// </returns>
270 /// <exception cref="InvalidOperationException">
271 /// Thrown when the solution is not compatible with
272 /// the supply-chain instance.
273 /// </exception>
275 string filePath,
276 SupplyChain supplyChain)
277 {
278 ArgumentNullException.ThrowIfNull(supplyChain);
279
280 LotSizingSolution solution =
281 Load(
282 filePath,
283 validateAfterLoad: false);
284
285 _validator.ThrowIfInvalid(
286 solution,
287 supplyChain);
288
289 return solution;
290 }
291
292 /// <summary>
293 /// Loads a solution from a readable stream.
294 /// </summary>
295 /// <param name="stream">
296 /// Source stream.
297 /// </param>
298 /// <param name="validateAfterLoad">
299 /// True to validate the internal structure after
300 /// deserialization; otherwise, false.
301 /// </param>
302 /// <param name="leaveOpen">
303 /// True to leave the stream open after deserialization;
304 /// otherwise, false.
305 /// </param>
306 /// <returns>
307 /// Deserialized lot-sizing solution.
308 /// </returns>
309 /// <exception cref="InvalidDataException">
310 /// Thrown when the stream does not contain a valid
311 /// lot-sizing solution document.
312 /// </exception>
314 Stream stream,
315 bool validateAfterLoad = false,
316 bool leaveOpen = true)
317 {
318 ArgumentNullException.ThrowIfNull(stream);
319
320 if (!stream.CanRead)
321 {
322 throw new ArgumentException(
323 "The source stream is not readable.",
324 nameof(stream));
325 }
326
327 XmlReaderSettings settings =
328 CreateReaderSettings(leaveOpen);
329
330 try
331 {
332 using XmlReader reader =
333 XmlReader.Create(
334 stream,
335 settings);
336
337 LotSizingSolution solution =
338 Deserialize(reader);
339
340 if (validateAfterLoad)
341 {
342 _validator.ThrowIfInvalid(solution);
343 }
344
345 return solution;
346 }
347 catch (XmlException exception)
348 {
349 throw new InvalidDataException(
350 "The XML document is malformed.",
351 exception);
352 }
353 catch (InvalidOperationException exception)
354 when (exception.InnerException is XmlException)
355 {
356 throw new InvalidDataException(
357 "The XML document does not contain a valid " +
358 "lot-sizing solution.",
359 exception);
360 }
361 }
362
363 /// <summary>
364 /// Serializes a solution to an XML string.
365 /// </summary>
366 /// <param name="solution">
367 /// Solution to serialize.
368 /// </param>
369 /// <param name="validateBeforeSerialization">
370 /// True to validate the solution before serialization;
371 /// otherwise, false.
372 /// </param>
373 /// <returns>
374 /// UTF-8 XML representation of the solution.
375 /// </returns>
376 public string SerializeToString(
377 LotSizingSolution solution,
378 bool validateBeforeSerialization = false)
379 {
380 ArgumentNullException.ThrowIfNull(solution);
381
382 using var stream =
383 new MemoryStream();
384
385 Save(
386 stream,
387 solution,
388 validateBeforeSerialization,
389 leaveOpen: true);
390
391 return Encoding.UTF8.GetString(
392 stream.ToArray());
393 }
394
395 /// <summary>
396 /// Deserializes a solution from an XML string.
397 /// </summary>
398 /// <param name="xml">
399 /// XML representation of the solution.
400 /// </param>
401 /// <param name="validateAfterDeserialization">
402 /// True to validate the internal structure after
403 /// deserialization; otherwise, false.
404 /// </param>
405 /// <returns>
406 /// Deserialized lot-sizing solution.
407 /// </returns>
409 string xml,
410 bool validateAfterDeserialization = false)
411 {
412 ArgumentException.ThrowIfNullOrWhiteSpace(xml);
413
414 byte[] data =
415 Encoding.UTF8.GetBytes(xml);
416
417 using var stream =
418 new MemoryStream(
419 data,
420 writable: false);
421
422 return Load(
423 stream,
424 validateAfterDeserialization,
425 leaveOpen: false);
426 }
427
428 private LotSizingSolution Deserialize(
429 XmlReader reader)
430 {
431 object? deserializedObject =
432 _serializer.Deserialize(reader);
433
434 if (deserializedObject is not
435 LotSizingSolution solution)
436 {
437 throw new InvalidDataException(
438 "The XML document does not contain a " +
439 "lot-sizing solution.");
440 }
441
443
444 return solution;
445 }
446
447 private XmlWriterSettings CreateWriterSettings(
448 bool leaveOpen)
449 {
450 return new XmlWriterSettings
451 {
452 Encoding =
453 new UTF8Encoding(
454 encoderShouldEmitUTF8Identifier: false),
455
456 Indent = IndentOutput,
457 OmitXmlDeclaration = false,
458 CloseOutput = !leaveOpen,
459 NewLineHandling =
460 NewLineHandling.Entitize
461 };
462 }
463
464 private static XmlReaderSettings CreateReaderSettings(
465 bool leaveOpen)
466 {
467 return new XmlReaderSettings
468 {
469 DtdProcessing =
470 DtdProcessing.Prohibit,
471
472 XmlResolver = null,
473 CloseInput = !leaveOpen,
474 IgnoreComments = true,
475 IgnoreProcessingInstructions = true
476 };
477 }
478}
Represents one candidate solution for a lot-sizing supply-chain instance.
string SerializeToString(LotSizingSolution solution, bool validateBeforeSerialization=false)
Serializes a solution to an XML string.
LotSizingSolution Load(string filePath, bool validateAfterLoad=false)
Loads a solution from an XML file.
bool IndentOutput
Gets a value indicating whether generated XML is indented.
LotSizingSolution Load(string filePath, SupplyChain supplyChain)
Loads a solution from an XML file and validates it against a supply-chain instance.
void Save(string filePath, LotSizingSolution solution, bool validateBeforeSave=false)
Saves a solution to an XML file.
void Save(string filePath, LotSizingSolution solution, SupplyChain supplyChain)
Validates a solution against a supply-chain instance and saves it to an XML file.
LotSizingSolution DeserializeFromString(string xml, bool validateAfterDeserialization=false)
Deserializes a solution from an XML string.
void Save(Stream stream, LotSizingSolution solution, bool validateBeforeSave=false, bool leaveOpen=true)
Saves a solution to a writable stream.
LotSizingSolutionXmlSerializer(LotSizingSolutionValidator validator, bool indentOutput=true)
Initializes a solution XML serializer.
LotSizingSolution Load(Stream stream, bool validateAfterLoad=false, bool leaveOpen=true)
Loads a solution from a readable stream.
LotSizingSolutionXmlSerializer()
Initializes a solution XML serializer using an indented XML representation.
Validates the structure and instance compatibility of a lot-sizing solution.