【问题标题】:XML serialization results in a file full of null charactersXML 序列化导致文件充满空字符
【发布时间】:2017-02-06 14:06:55
【问题描述】:

我正在序列化一个(Car 类的)对象并将其保存为 xml 文件。但是有时(非常罕见的事件,我无法复制它)我得到一个只包含空字符(ASCII 代码为 0 的字符)的文件,即使文件大小是正确的。

public void SaveCar(Car car)
{
    var serializer = new XmlSerializer(typeof(Car));
    using (var stream = new MemoryStream())
    {
        serializer.Serialize(stream, car);
        byte[] binaryCar = stream.ToArray();
        FileHelper.WriteAllBytes(@"C:\car.xml", binaryCar);
    }
}

FileHelper.WriteAllBytes 与 System.IO.File.WriteAllBytes 相同,只是如果父目录不存在则创建父目录。

public static class FileHelper
{
    public static void WriteAllBytes(string path, byte[] bytes)
    {
        CreateParentDirectoryForPathIfDoesntExist(path);
        File.WriteAllBytes(path, bytes);
    }

    public static void CreateParentDirectoryForPathIfDoesntExist(string filePath)
    {
        var file = new FileInfo(filePath);
        if (file.Exists)
        {
            return;
        }

        if (file.Directory != null && !file.Directory.Exists)
        {
            Directory.CreateDirectory(file.Directory.FullName);
        }
    }
}

知道是什么原因造成的吗?编码问题?

【问题讨论】:

  • “有时”是什么时候?对于特定文件?特定的车? minimal reproducible example 能够可靠地演示使用 NUL 字符创建文件会很有用。
  • 更具体地了解FileHelper。这看起来像是候选问题的来源。
  • 在你的问题中包含FileHelper的代码。
  • 对不起,我澄清了一点,并添加了 FileHelper 类。问题是我无法复制它(我不知道什么样的数据会导致它在数据丢失时表现得像那样)。它很少发生,甚至在我的电脑上也没有。大多数时候它工作得很好。

标签: c# xml serialization stream corrupt-data


【解决方案1】:

您的“SaveCar”方法可以很好地工作,但我希望您可以使用以下实现

public class XmlCar {
    public static readonly Encoding Utf8Encoding = Encoding.UTF8;
    public static readonly XmlSerializer CarSerializer = new XmlSerializer(typeof(Car));
    public static Car Load(string path) {
        using (var stream = new StreamReader(path, Utf8Encoding, false)) {
            using (var reader = XmlReader.Create(stream)) {
                return (Car)CarSerializer.Deserialize(reader);
            }
        }
    }
    public static void Save(string path, Car instance) {
        using (var stream = new StreamWriter(path, false, Utf8Encoding)) {
            using (var writer = XmlWriter.Create(stream)) {
                CarSerializer.Serialize(writer, instance);
            }
        }
    }
}

与您的示例最大的不同是,serializer-instance 是创建一次并强制使用 UTF8 编码。

您的方法已尝试使用以下类

[XmlRoot]
public class Car {
    [XmlAttribute]
    public string Model { get; set; }
    [XmlAttribute]
    public string Plate { get; set; }
    [XmlArray]
    [XmlArrayItem]
    public List<Part> Parts { get; set; }
}
[XmlRoot]
public class Part {
    [XmlAttribute]
    public string Type { get; set; }
    [XmlAttribute]
    public string Code { get; set; }
    [XmlAttribute]
    public int Quantity { get; set; }
}

请注意,此示例源自本网站上的示例。

您的方法稍作更改,以适应对文件名的一些控制。

    public static void SaveCar(Car car, int i)
    {
        var serializer = new XmlSerializer(typeof(Car));
        using (var stream = new MemoryStream())
        {
            serializer.Serialize(stream, car);
            byte[] binaryCar = stream.ToArray();
            File.WriteAllBytes("car"+ i + ".xml", binaryCar);
        }
    }

使用以下功能测试方法

    static void Main(string[] args) {

        var random = new Random();
        var cars = new List<Car>();

        for (var c = 0; c < 10; c++) {
            var car = new Car() {
                Model = Guid.NewGuid().ToString(),
                Plate = (random.Next(500) + 500).ToString(),
                Parts = new List<Part>()
            };
            for (var p = 0; p < 100; p++) {
                car.Parts.Add(new Part() {
                    Type = Guid.NewGuid().ToString(),
                    Code = (random.Next(500) + 1500).ToString(),
                    Quantity = random.Next(100) + 1
                });
            }
            SaveCar(car, c);
            cars.Add(XmlCar.Load("car" + c + ".xml"));
        }
    }

【讨论】:

  • 不推荐 XmlTextReaderXmlTextWriter(请参阅链接文档)。您应该使用XmlReader.CreateXmlWriter.CreateStreamReaderStreamWriter 默认为 UTF8,但如果您使用接受文件路径的 Create 重载,则两者都不需要。尽管这些变化并没有造成太大的危害,但我认为它们并没有回答问题。
  • 感谢您尝试循环复制,不幸的是我也无法通过这种方式找到错误。如果我没有找到错误的原因,那么我可能会选择您的解决方案,也许缺少编码是导致问题的原因。
  • @Bedford,无论何时写入您将在之后阅读的流,请确保您已在其上调用 Flush()(如果支持,以确保所有内容都在其中)。此外,可能被证明有用的是在下次读取之前将位置移动到流的开头。
  • @CharlesMager,感谢您指出 XmlTextReader/Writer 的缺点。到目前为止,我已经将它们用于应用程序到应用程序的对话以及在受到良好限制的环境中,因此没有空间让坏的一面显现出来。关于答案我同意,它不能解决问题,但它确实提供了一个可行的样本,该样本被多次使用和测试。
猜你喜欢
  • 2018-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-10
相关资源
最近更新 更多