C#XML反序列化设置默认值

时间:2016-11-24 08:47:53

标签: c# xml deserialization xml-deserialization

我的申请表中有一个XML,如下面的XML

<Default>       
     <Port>7252</Port>
     <FileLocation>D:/test</FileLocation>
</Default>
<Files>
    <File>
        <Type>Send</Type>
            <FileName>xyz</FileName>            
            <Port>7252</Port>
            <FileLocation>c:/test</FileLocation>
    </File>
    <File>
        <Type>Send</Type>
            <FileName>abc</FileName>            
            <Port></Port>
            <FileLocation></FileLocation>
    </File>
</Files>

在反序列化时我想确保File元素没有任何值,它将从Default元素中挑选出来。是否已经有任何类/方法,或者我是否需要在我的程序中为此编写自定义逻辑?

PS:我可以灵活地更改XML中的名称/设计,只要它的比例相同。

3 个答案:

答案 0 :(得分:1)

试试这个:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XDocument doc = XDocument.Load(FILENAME);
            XElement _default = doc.Descendants("Default").FirstOrDefault();

            int defaultPort = (int)_default.Element("Port");
            string defaultFileLocation = (string)_default.Element("FileLocation");

            var files = doc.Descendants("File").Select(x => new {
                type = (string)x.Element("Type"),
                fileName = (string)x.Element("FileName"),
                port = (string)x.Element("Port") == "" ? defaultPort : (int)x.Element("Port"),
                fileLocation = (string)x.Element("FileLocation") == "" ? defaultFileLocation : (string)x.Element("FileLocation"),
            }).ToList();
        }
    }
}

答案 1 :(得分:1)

XmlSerializer中的魔法或其周围的支持属性无法实现您想要的效果。您可以冒险创建自己的XmlReader重载,在读取时存储Default节点,并在读取时重写任何File节点的值。如果您的文件较大,我会查看该解决方案。但是,你说文件很小所以我想它很小,可以在被XmlSerializer读取之前先被转换。

以下XSLT表实现了对于给定的xml(当包装在Root节点中时)。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
   <!-- copy template -->
   <xsl:template match="@*|node()">
     <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
     </xsl:copy>
   </xsl:template>
   <!-- handle nodes that should have defaults -->
   <xsl:template match="File/*">
      <xsl:choose>
        <!-- there is a value -->
        <xsl:when test="string-length(.)>0">
           <xsl:copy-of select="."/>
        </xsl:when>
        <xsl:otherwise>
           <!-- no value, look up an default -->
           <xsl:variable name="def" select="name()"/>
           <xsl:copy-of select="/Root/Default/*[name(.) = $def]/."/>
        </xsl:otherwise>
      </xsl:choose>
   </xsl:template>

 </xsl:stylesheet>

以下代码将上述样式应用于带有XslCompiledTransform的输入XML,并将其结果传递给XmlSerializer:

   // create the Xsl Transformation
   var xct = new XslCompiledTransform();
   // use any another Stream if needed
   // xsl holds the XSLT stylesheet
   xct.Load(XmlReader.Create(new StringReader(xsl)));
   Root result;
   // we stream the result in memory
   using(var ms = new MemoryStream())
   {
      // write it
      using(var xw = XmlWriter.Create(ms))
      {
         // transform input XML  
         // the string xml holds the test XML input, replace with a stream
         xct.Transform(XmlReader.Create(new StringReader(xml)), xw);
      }
      Encoding.UTF8.GetString(ms.ToArray()).Dump(); // linqpad testing
      // now we're ready to deserialize
      var xs = new XmlSerializer(typeof(Root));
      ms.Position = 0;
      result= (Root) xs.Deserialize(ms);
      result.Dump(); // Linqpad testing
   }

基本上发生的是,将以下XML输入序列化程序而不是输入XML:

<?xml version="1.0" encoding="utf-8"?>
<Root>
  <Default>       
     <Port>7252</Port>
     <FileLocation>D:/test</FileLocation>
  </Default>
  <Files>
    <File>
        <Type>Send</Type>
        <FileName>xyz</FileName>            
        <Port>7252</Port>
        <FileLocation>c:/test</FileLocation>
    </File>
    <File>
        <Type>Send</Type>
        <FileName>abc</FileName>            
        <Port>7252</Port>
        <FileLocation>D:/test</FileLocation>
    </File>
  </Files>
</Root>

为了完整性,这里是序列化类型:

public class Root
{
    public File Default;
    public List<File> Files;
}

public class File
{
   public int Port;
   public string FileName;
   public string FileLocation;
}

答案 2 :(得分:0)

我可以建议以下方法。

创建xml架构,它将为某些元素设置默认值。

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
           xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Files">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" name="File">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="Type"         type="xs:string" />
              <xs:element name="FileName"     type="xs:string" />
              <xs:element name="Port"         type="xs:int"    default="7252" />
              <xs:element name="FileLocation" type="xs:string" default="D:/test" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

您的xml数据文件应如下所示:

<?xml version="1.0" encoding="utf-8"?>
<Files>
  <File>
    <Type>Send</Type>
    <FileName>xyz</FileName>
    <Port>7252</Port>
    <FileLocation>c:/test</FileLocation>
  </File>
  <File>
    <Type>Send</Type>
    <FileName>abc</FileName>
    <Port></Port>
    <FileLocation></FileLocation>
  </File>
</Files>

您的反序列化类也是如此:

public class Files
{
    [XmlElement("File")]
    public File[] File { get; set; }
}

public class File
{
    public string Type { get; set; }
    public string FileName { get; set; }
    public int Port { get; set; }
    public string FileLocation { get; set; }
}

现在,在反序列化期间添加xml架构就足以检索默认值。

Files files;

var settings = new XmlReaderSettings();
settings.Schemas.Add("", "test.xsd");
settings.ValidationType = ValidationType.Schema;

var xs = new XmlSerializer(typeof(Files));
using (var reader = XmlReader.Create("test.xml", settings))
{
    files = (Files)xs.Deserialize(reader);
}