我的申请表中有一个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
中的名称/设计,只要它的比例相同。
答案 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);
}