seb-win-refactoring/SafeExamBrowser.Configuration/DataFormats/XmlSerializer.cs

199 lines
4.6 KiB
C#

/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Configuration.Contracts.DataFormats;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Configuration.DataFormats
{
public class XmlSerializer : IDataSerializer
{
private readonly ILogger logger;
public XmlSerializer(ILogger logger)
{
this.logger = logger;
}
public bool CanSerialize(FormatType format)
{
return format == FormatType.Xml;
}
public SerializeResult TrySerialize(IDictionary<string, object> data, EncryptionParameters encryption = null)
{
var result = new SerializeResult();
var settings = new XmlWriterSettings { Encoding = new UTF8Encoding(), Indent = true };
var stream = new MemoryStream();
logger.Debug("Starting to serialize data...");
using (var writer = XmlWriter.Create(stream, settings))
{
writer.WriteStartDocument();
writer.WriteDocType("plist", "-//Apple Computer//DTD PLIST 1.0//EN", "https://www.apple.com/DTDs/PropertyList-1.0.dtd", null);
writer.WriteStartElement(XmlElement.Root);
writer.WriteAttributeString("version", "1.0");
result.Status = WriteDictionary(writer, data);
result.Data = stream;
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
writer.Close();
}
logger.Debug($"Finished serialization -> Result: {result.Status}.");
return result;
}
private SaveStatus WriteArray(XmlWriter writer, List<object> array)
{
var status = SaveStatus.Success;
writer.WriteStartElement(XmlElement.Array);
foreach (var item in array)
{
status = WriteElement(writer, item);
if (status != SaveStatus.Success)
{
break;
}
}
writer.WriteEndElement();
return status;
}
private SaveStatus WriteDictionary(XmlWriter writer, IDictionary<string, object> data)
{
var status = SaveStatus.Success;
writer.WriteStartElement(XmlElement.Dictionary);
foreach (var item in data.OrderBy(i => i.Key))
{
status = WriteKeyValuePair(writer, item);
if (status != SaveStatus.Success)
{
break;
}
}
writer.WriteEndElement();
return status;
}
private SaveStatus WriteKeyValuePair(XmlWriter writer, KeyValuePair<string, object> item)
{
var status = SaveStatus.InvalidData;
if (item.Key != null)
{
writer.WriteElementString(XmlElement.Key, item.Key);
status = WriteElement(writer, item.Value);
}
else
{
logger.Error($"Key of item '{item}' is null!");
}
return status;
}
private SaveStatus WriteElement(XmlWriter writer, object element)
{
var status = default(SaveStatus);
if (element is List<object> array)
{
status = WriteArray(writer, array);
}
else if (element is Dictionary<string, object> dictionary)
{
status = WriteDictionary(writer, dictionary);
}
else
{
status = WriteSimpleType(writer, element);
}
return status;
}
private SaveStatus WriteSimpleType(XmlWriter writer, object element)
{
var name = default(string);
var value = default(string);
var status = SaveStatus.Success;
switch (element)
{
case byte[] data:
name = XmlElement.Data;
value = Convert.ToBase64String(data);
break;
case DateTime date:
name = XmlElement.Date;
value = XmlConvert.ToString(date, XmlDateTimeSerializationMode.Utc);
break;
case bool boolean when boolean == false:
name = XmlElement.False;
break;
case bool boolean when boolean == true:
name = XmlElement.True;
break;
case int integer:
name = XmlElement.Integer;
value = integer.ToString(NumberFormatInfo.InvariantInfo);
break;
case double real:
name = XmlElement.Real;
value = real.ToString(NumberFormatInfo.InvariantInfo);
break;
case string text:
name = XmlElement.String;
value = text;
break;
case null:
name = XmlElement.String;
break;
default:
status = SaveStatus.InvalidData;
break;
}
if (status == SaveStatus.Success)
{
writer.WriteElementString(name, value);
}
else
{
logger.Error($"Element '{element}' is not supported!");
}
return status;
}
}
}