XML 文档中的错误。意外的 XML 声明。 XML 声明必须是文档中的第一个节点

error in XML document. Unexpected XML declaration. XML declaration must be the first node in the document

There is an error in XML document (8, 20). Inner 1: Unexpected XML declaration. The XML declaration must be the first node in the document, and no white space characters are allowed to appear before it.

好的,我理解这个错误。

然而,让我困惑的是我是如何得到它的。

我使用 Microsoft 的序列化工具创建文档。然后,我转身尝试再次使用 Microsoft 的反序列化工具将其读回。

我无法控制以正确的格式编写 XML 文件 - 我可以看到。

这是我用来读写的单个例程。

private string xmlPath = System.Web.Hosting.HostingEnvironment.MapPath(WebConfigurationManager.AppSettings["DATA_XML"]);
private object objLock = new Object();
public string ErrorMessage { get; set; }

public StoredMsgs Operation(string from, string message, FileAccess access) {
    StoredMsgs list = null;
    lock (objLock) {
        ErrorMessage = null;
        try {
            if (!File.Exists(xmlPath)) {
                var root = new XmlRootAttribute(rootName);
                var serializer = new XmlSerializer(typeof(StoredMsgs), root);
                if (String.IsNullOrEmpty(message)) {
                    from = "Code Window";
                    message = "Created File";
                }
                var item = new StoredMsg() {
                    From = from,
                    Date = DateTime.Now.ToString("s"),
                    Message = message
                };
                using (var stream = File.Create(xmlPath)) {
                    list = new StoredMsgs();
                    list.Add(item);
                    serializer.Serialize(stream, list);
                }
            } else {
                var root = new XmlRootAttribute("MessageHistory");
                var serializer = new XmlSerializer(typeof(StoredMsgs), root);
                var item = new StoredMsg() {
                    From = from,
                    Date = DateTime.Now.ToString("s"),
                    Message = message
                };
                using (var stream = File.Open(xmlPath, FileMode.Open, FileAccess.ReadWrite)) {
                    list = (StoredMsgs)serializer.Deserialize(stream);
                    if ((access == FileAccess.ReadWrite) || (access == FileAccess.Write)) {
                        list.Add(item);
                        serializer.Serialize(stream, list);
                    }
                }
            }
        } catch (Exception error) {
            var sb = new StringBuilder();
            int index = 0;
            sb.AppendLine(String.Format("Top Level Error: <b>{0}</b>", error.Message));
            var err = error.InnerException;
            while (err != null) {
                index++;
                sb.AppendLine(String.Format("\tInner {0}: {1}", index, err.Message));
                err = err.InnerException;
            }
            ErrorMessage = sb.ToString();
        }
    }
    return list;
}

我的作息有问题吗?如果微软写了这个文件,我觉得应该可以读回来。

它应该足够通用,任何人都可以使用。

这是我的 StoredMsg class:

[Serializable()]
[XmlType("StoredMessage")]
public class StoredMessage {
    public StoredMessage() {
    }
    [XmlElement("From")]
    public string From { get; set; }
    [XmlElement("Date")]
    public string Date { get; set; }
    [XmlElement("Message")]
    public string Message { get; set; }
}

[Serializable()]
[XmlRoot("MessageHistory")]
public class MessageHistory : List<StoredMessage> {
}

它生成的文件在我看来没有任何问题。

我在这里看到了解决方案:

Error: The XML declaration must be the first node in the document

但是,在那种情况下,似乎有人已经有了他们想要阅读的 XML 文档。他们只需要修复它。

我的 Microsoft 创建了一个 XML 文档,因此 Microsoft 应该会读回它。

问题是您正在 添加 到文件中。您反序列化,然后重新序列化为同一流,而无需倒带和调整大小为零。这给你多个 root elements:

<?xml version="1.0"?>
<StoredMessage>
</StoredMessage
<?xml version="1.0"?>
<StoredMessage>
</StoredMessage

多个根元素和多个 XML 声明根据 XML 标准是 invalid,因此 .NET XML 解析器在这种情况下抛出异常默认情况下。

有关可能的解决方案,请参阅 XML Error: There are multiple root elements,建议您:

  1. 将您的 StoredMessage 元素列表包含在一些合成外部元素中,例如StoredMessageList.

    这需要您从文件中加载消息列表,添加新消息,然后在添加单个消息时截断文件并重新序列化整个列表物品。因此,性能可能比您当前的方法差,但 XML 将有效。

  2. 反序列化包含串联根元素的文件时,使用 XmlReaderSettings.ConformanceLevel = ConformanceLevel.Fragment and iteratively walk through the concatenated root node(s) and deserialize each one individually as shown, e.g., 创建一个 XML 编写器。使用 ConformanceLevel.Fragment 允许 reader 解析具有多个根元素的流(尽管多个 XML 声明 仍然会导致抛出错误)。

    稍后,当使用 XmlSerializer 将新元素添加到文件末尾时,查找文件末尾并使用从 XmlWriter.Create(TextWriter, XmlWriterSettings) 返回的 XML 编写器进行序列化 XmlWriterSettings.OmitXmlDeclaration = true. This prevents output of multiple XML declarations as explained here

对于选项 #2,您的 Operation 将如下所示:

private string xmlPath = System.Web.Hosting.HostingEnvironment.MapPath(WebConfigurationManager.AppSettings["DATA_XML"]);
private object objLock = new Object();
public string ErrorMessage { get; set; }

const string rootName = "MessageHistory";
static readonly XmlSerializer serializer = new XmlSerializer(typeof(StoredMessage), new XmlRootAttribute(rootName));

public MessageHistory Operation(string from, string message, FileAccess access)
{
    var list = new MessageHistory();
    lock (objLock)
    {
        ErrorMessage = null;
        try
        {
            using (var file = File.Open(xmlPath, FileMode.OpenOrCreate))
            {
                list.AddRange(XmlSerializerHelper.ReadObjects<StoredMessage>(file, false, serializer));
                if (list.Count == 0 && String.IsNullOrEmpty(message))
                {
                    from = "Code Window";
                    message = "Created File";
                }
                var item = new StoredMessage()
                {
                    From = from,
                    Date = DateTime.Now.ToString("s"),
                    Message = message
                };
                if ((access == FileAccess.ReadWrite) || (access == FileAccess.Write))
                {
                    file.Seek(0, SeekOrigin.End);
                    var writerSettings = new XmlWriterSettings
                    {
                        OmitXmlDeclaration = true,
                        Indent = true, // Optional; remove if compact XML is desired.
                    };
                    using (var textWriter = new StreamWriter(file))
                    {
                        if (list.Count > 0)
                            textWriter.WriteLine();
                        using (var xmlWriter = XmlWriter.Create(textWriter, writerSettings))
                        {
                            serializer.Serialize(xmlWriter, item);
                        }
                    }
                }
                list.Add(item);
            }
        }
        catch (Exception error)
        {
            var sb = new StringBuilder();
            int index = 0;
            sb.AppendLine(String.Format("Top Level Error: <b>{0}</b>", error.Message));
            var err = error.InnerException;
            while (err != null)
            {
                index++;
                sb.AppendLine(String.Format("\tInner {0}: {1}", index, err.Message));
                err = err.InnerException;
            }
            ErrorMessage = sb.ToString();
        }
    }
    return list;
}

使用以下改编自的扩展方法:

public partial class XmlSerializerHelper
{
    public static List<T> ReadObjects<T>(Stream stream, bool closeInput = true, XmlSerializer serializer = null)
    {
        var list = new List<T>();

        serializer = serializer ?? new XmlSerializer(typeof(T));
        var settings = new XmlReaderSettings
        {
            ConformanceLevel = ConformanceLevel.Fragment,
            CloseInput = closeInput,
        };
        using (var xmlTextReader = XmlReader.Create(stream, settings))
        {
            while (xmlTextReader.Read())
            {   // Skip whitespace
                if (xmlTextReader.NodeType == XmlNodeType.Element)
                {
                    using (var subReader = xmlTextReader.ReadSubtree())
                    {
                        var logEvent = (T)serializer.Deserialize(subReader);
                        list.Add(logEvent);
                    }
                }
            }
        }

        return list;
    }    
}

请注意,如果您要使用自定义 XmlRootAttribute 创建 XmlSerializer,则必须 cache the serializer to avoid a memory leak

示例 fiddle.