如何控制.NET XML序列化/反序列化以排除元素,当它是MS类时,我无法编辑源以添加XMLIgnore等

时间:2014-07-21 21:01:55

标签: .net serialization xml-serialization deserialization

为了在下次启动时保留对象并恢复它们,XML序列化对我来说已经完成了这项工作。但我目前在Microsoft.SqlServer.Management.Smo命名空间中遇到ScriptingOptions类问题。

序列化对象会生成如下所示的XML片段。但是当我尝试反序列化它时,XmlSerializer会抛出异常:

    There is an error in XML document (n,n)

内部异常是InvalidOperationException:

    Instance is read-only

堆栈跟踪(下面进一步包含)标识它正在尝试设置EncoderFallback属性。如果我编辑XML以删除以下元素,则反序列化成功的事实进一步证实了这一点:

  <Encoding xsi:type="UnicodeEncoding">
    <EncoderFallback xsi:type="EncoderReplacementFallback" />
    <DecoderFallback xsi:type="DecoderReplacementFallback" />
  </Encoding>

所以我想在对象的序列化或反序列化过程中排除这个Encoding元素。

我已经搜索了答案,但我发现的所有自定义序列化/反序列化方法似乎都假定可以访问类源代码,添加XMLIgnore或实现ISerializable等。

我想我可以编写一个预处理器来解析XML并在反序列化之前删除有问题的元素,但肯定必须有更多的&#34;正确的&#34;比那样的方式?

序列化对象:

<ScriptingOptions>
  <FileName />
  <Encoding xsi:type="UnicodeEncoding">
    <EncoderFallback xsi:type="EncoderReplacementFallback" />
    <DecoderFallback xsi:type="DecoderReplacementFallback" />
  </Encoding>
  <DriWithNoCheck>false</DriWithNoCheck>
  <IncludeFullTextCatalogRootPath>false</IncludeFullTextCatalogRootPath>
  <BatchSize>1</BatchSize>
  <ScriptDrops>false</ScriptDrops>
  <TargetServerVersion>Version120</TargetServerVersion>
  <TargetDatabaseEngineType>Standalone</TargetDatabaseEngineType>
  <AnsiFile>false</AnsiFile>
  <AppendToFile>false</AppendToFile>
  <ToFileOnly>false</ToFileOnly>
  <SchemaQualify>true</SchemaQualify>
  <IncludeHeaders>false</IncludeHeaders>
  <IncludeIfNotExists>true</IncludeIfNotExists>
  <WithDependencies>false</WithDependencies>
  <DriPrimaryKey>false</DriPrimaryKey>
  <DriForeignKeys>false</DriForeignKeys>
  <DriUniqueKeys>false</DriUniqueKeys>
  <DriClustered>false</DriClustered>
  <DriNonClustered>false</DriNonClustered>
  <DriChecks>false</DriChecks>
  <DriDefaults>false</DriDefaults>
  <Triggers>false</Triggers>
  <Statistics>false</Statistics>
  <ClusteredIndexes>false</ClusteredIndexes>
  <NonClusteredIndexes>false</NonClusteredIndexes>
  <NoAssemblies>false</NoAssemblies>
  <PrimaryObject>true</PrimaryObject>
  <Default>true</Default>
  <XmlIndexes>false</XmlIndexes>
  <FullTextCatalogs>false</FullTextCatalogs>
  <FullTextIndexes>false</FullTextIndexes>
  <FullTextStopLists>false</FullTextStopLists>
  <Indexes>false</Indexes>
  <DriIndexes>false</DriIndexes>
  <DriAllKeys>false</DriAllKeys>
  <DriAllConstraints>false</DriAllConstraints>
  <DriAll>false</DriAll>
  <Bindings>false</Bindings>
  <NoFileGroup>false</NoFileGroup>
  <NoFileStream>false</NoFileStream>
  <NoFileStreamColumn>false</NoFileStreamColumn>
  <NoCollation>false</NoCollation>
  <ContinueScriptingOnError>false</ContinueScriptingOnError>
  <IncludeDatabaseRoleMemberships>false</IncludeDatabaseRoleMemberships>
  <Permissions>false</Permissions>
  <AllowSystemObjects>true</AllowSystemObjects>
  <NoIdentities>false</NoIdentities>
  <ConvertUserDefinedDataTypesToBaseType>false</ConvertUserDefinedDataTypesToBaseType>
  <TimestampToBinary>false</TimestampToBinary>
  <AnsiPadding>false</AnsiPadding>
  <ExtendedProperties>false</ExtendedProperties>
  <DdlHeaderOnly>false</DdlHeaderOnly>
  <DdlBodyOnly>false</DdlBodyOnly>
  <NoViewColumns>false</NoViewColumns>
  <SchemaQualifyForeignKeysReferences>false</SchemaQualifyForeignKeysReferences>
  <AgentAlertJob>false</AgentAlertJob>
  <AgentJobId>true</AgentJobId>
  <AgentNotify>false</AgentNotify>
  <LoginSid>false</LoginSid>
  <NoCommandTerminator>false</NoCommandTerminator>
  <NoIndexPartitioningSchemes>false</NoIndexPartitioningSchemes>
  <NoTablePartitioningSchemes>false</NoTablePartitioningSchemes>
  <IncludeDatabaseContext>false</IncludeDatabaseContext>
  <NoXmlNamespaces>false</NoXmlNamespaces>
  <DriIncludeSystemNames>false</DriIncludeSystemNames>
  <OptimizerData>false</OptimizerData>
  <NoExecuteAs>false</NoExecuteAs>
  <EnforceScriptingOptions>false</EnforceScriptingOptions>
  <NoMailProfileAccounts>false</NoMailProfileAccounts>
  <NoMailProfilePrincipals>false</NoMailProfilePrincipals>
  <NoVardecimal>true</NoVardecimal>
  <ChangeTracking>false</ChangeTracking>
  <ScriptDataCompression>true</ScriptDataCompression>
  <ScriptSchema>true</ScriptSchema>
  <ScriptData>false</ScriptData>
  <ScriptBatchTerminator>false</ScriptBatchTerminator>
  <ScriptOwner>false</ScriptOwner>
</ScriptingOptions>

尝试反序列化上述XML时的堆栈跟踪:

    StackTrace  "   at System.Text.Encoding.set_EncoderFallback(EncoderFallback value)\r\n   
    at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderUserSettings.Read13_UnicodeEncoding(Boolean isNullable, Boolean checkType)\r\n   
    at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderUserSettings.Read7_Encoding(Boolean isNullable, Boolean checkType)\r\n
    at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderUserSettings.Read10_ScriptingOptions(Boolean isNullable, Boolean checkType)\r\n   
    at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderUserSettings.Read14_TableScriptingOptions(Boolean isNullable, Boolean checkType)\r\n   
    at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderUserSettings.Read15_UserSettings(Boolean isNullable, Boolean checkType)\r\n   
    at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderUserSettings.Read16_UserSettings()"  string

2 个答案:

答案 0 :(得分:2)

有时候最简单的解决方案是最好的,但你可以用你想要的属性创建一个DTO,使用AutoMapper从源到目标映射(CreateMap不是线程安全btw)并从那里直接反序列化为源类型:

using AutoMapper;
using Microsoft.SqlServer.Management.Smo;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace SerializationTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Mapper.CreateMap<ScriptingOptions, ScriptingOptionsDto>();

            var so = new ScriptingOptions();
            var soDto = Mapper.Map<ScriptingOptionsDto>(so); 
            string xml = Serialize(soDto);

            Console.WriteLine(xml); 
            Console.ReadLine(); 

            so = Deserialize(xml);

            Console.WriteLine(so.ToString());
            Console.ReadLine();
        }

        public static string Serialize(ScriptingOptionsDto dto)
        {
            var serializer = new XmlSerializer(dto.GetType()); 
            var ms = new MemoryStream();
            serializer.Serialize(ms, dto); 
            return Encoding.UTF8.GetString(ms.ToArray());
        }

        public static ScriptingOptions Deserialize(string xml)
        {
            var serializer = new XmlSerializer(typeof(ScriptingOptions));
            return serializer.Deserialize(new MemoryStream(UnicodeEncoding.UTF8.GetBytes(xml))) as ScriptingOptions;
        }
    }

    [XmlType("ScriptingOptions")]
    public class ScriptingOptionsDto
    {
        public bool AgentAlertJob { get; set; }
        public bool AgentJobId { get; set; }
        public bool AgentNotify { get; set; }
        public bool AllowSystemObjects { get; set; }
        public bool AnsiFile { get; set; }
        public bool AnsiPadding { get; set; }
        public bool AppendToFile { get; set; }
        public int BatchSize { get; set; }
        public bool Bindings { get; set; }
        public bool ChangeTracking { get; set; }
        public bool ClusteredIndexes { get; set; }
        public bool ContinueScriptingOnError { get; set; }
        public bool ConvertUserDefinedDataTypesToBaseType { get; set; }
        public bool DdlBodyOnly { get; set; }
        public bool DdlHeaderOnly { get; set; }
        public bool Default { get; set; }
        public bool DriAll { get; set; }
        public bool DriAllConstraints { get; set; }
        public bool DriAllKeys { get; set; }
        public bool DriChecks { get; set; }
        public bool DriClustered { get; set; }
        public bool DriDefaults { get; set; }
        public bool DriForeignKeys { get; set; }
        public bool DriIncludeSystemNames { get; set; }
        public bool DriIndexes { get; set; }
        public bool DriNonClustered { get; set; }
        public bool DriPrimaryKey { get; set; }
        public bool DriUniqueKeys { get; set; }
        public bool DriWithNoCheck { get; set; }
        public bool EnforceScriptingOptions { get; set; }
        public bool ExtendedProperties { get; set; }
        public string FileName { get; set; }
        public bool FullTextCatalogs { get; set; }
        public bool FullTextIndexes { get; set; }
        public bool FullTextStopLists { get; set; }
        public bool IncludeDatabaseContext { get; set; }
        public bool IncludeDatabaseRoleMemberships { get; set; }
        public bool IncludeFullTextCatalogRootPath { get; set; }
        public bool IncludeHeaders { get; set; }
        public bool IncludeIfNotExists { get; set; }
        public bool Indexes { get; set; }
        public bool LoginSid { get; set; }
        public bool NoAssemblies { get; set; }
        public bool NoCollation { get; set; }
        public bool NoCommandTerminator { get; set; }
        public bool NoExecuteAs { get; set; }
        public bool NoFileGroup { get; set; }
        public bool NoFileStream { get; set; }
        public bool NoFileStreamColumn { get; set; }
        public bool NoIdentities { get; set; }
        public bool NoIndexPartitioningSchemes { get; set; }
        public bool NoMailProfileAccounts { get; set; }
        public bool NoMailProfilePrincipals { get; set; }
        public bool NonClusteredIndexes { get; set; }
        public bool NoTablePartitioningSchemes { get; set; }
        public bool NoVardecimal { get; set; }
        public bool NoViewColumns { get; set; }
        public bool NoXmlNamespaces { get; set; }
        public bool OptimizerData { get; set; }
        public bool Permissions { get; set; }
        public bool PrimaryObject { get; set; }
        public bool SchemaQualify { get; set; }
        public bool SchemaQualifyForeignKeysReferences { get; set; }
        public bool ScriptBatchTerminator { get; set; }
        public bool ScriptData { get; set; }
        public bool ScriptDataCompression { get; set; }
        public bool ScriptDrops { get; set; }
        public bool ScriptOwner { get; set; }
        public bool ScriptSchema { get; set; }
        public bool Statistics { get; set; }
        public Microsoft.SqlServer.Management.Common.DatabaseEngineType TargetDatabaseEngineType { get; set; }
        public SqlServerVersion TargetServerVersion { get; set; }
        public bool TimestampToBinary { get; set; }
        public bool ToFileOnly { get; set; }
        public bool Triggers { get; set; }
        public bool WithDependencies { get; set; }
        public bool XmlIndexes { get; set; }
    }
}

答案 1 :(得分:2)

您可以使用XmlAttributeOverrides类来指定您不拥有的类的属性:

var overrides = new XmlAttributeOverrides();
overrides.Add(
    typeof(ScriptingOptions),
    "Encoding",
    new XmlAttributes { XmlIgnore = true });
var serializer = new XmlSerializer(typeof(ScriptingOptions), overrides);

但总的来说,使用特定的类来处理序列化通常更方便,如a-h所示,因为它为您提供了更大的灵活性。