我正在序列化这样一个类
public MyClass
{
public int? a { get; set; }
public int? b { get; set; }
public int? c { get; set; }
}
所有类型都可以为空,因为我希望在序列化此类对象时存储最少的数据。但是,当它仅使用“a”进行序列化时,我得到以下xml
<MyClass ...>
<a>3</a>
<b xsi:nil="true" />
<c xsi:nil="true" />
</MyClass>
如何将此设置为仅获取非null属性的xml?期望的输出将是
<MyClass ...>
<a>3</a>
</MyClass>
我想排除这些空值,因为会有几个属性,这会存储在数据库中(是的,这不是我的调用)所以我想保持未使用的数据最小化。
答案 0 :(得分:32)
public MyClass
{
public int? a { get; set; }
[System.Xml.Serialization.XmlIgnore]
public bool aSpecified { get { return this.a != null; } }
public int? b { get; set; }
[System.Xml.Serialization.XmlIgnore]
public bool bSpecified { get { return this.b != null; } }
public int? c { get; set; }
[System.Xml.Serialization.XmlIgnore]
public bool cSpecified { get { return this.c != null; } }
}
{field}指定的属性将通过返回true / false告诉序列化程序是否应序列化相应的字段。
答案 1 :(得分:6)
我想你可以创建一个XmlWriter来过滤掉所有带有xsi:nil属性的元素,并将所有其他调用传递给底层真正的writer。
答案 2 :(得分:4)
迟到总比不上......
我找到了一种方法(可能只有最新的框架,我不知道)才能做到这一点。 我正在使用DataMember属性来获取WCF Web服务合同,并且我将对象标记为:
[DataMember(EmitDefaultValue = false)]
public decimal? RentPrice { get; set; }
答案 3 :(得分:3)
您是否阅读过SO: Serialize a nullable int?
答案 4 :(得分:3)
又一个解决方案:正则表达式救援,使用\s+<\w+ xsi:nil="true" \/>
从包含XML的字符串中删除所有null属性。
我同意,不是最优雅的解决方案,只有在你只需序列化时才有效。但这就是我今天所需要的,而且我不想为所有可以为空的属性添加{Foo}Specified
属性。
public string ToXml()
{
string result;
var serializer = new XmlSerializer(this.GetType());
using (var writer = new StringWriter())
{
serializer.Serialize(writer, this);
result = writer.ToString();
}
serializer = null;
// Replace all nullable fields, other solution would be to use add PropSpecified property for all properties that are not strings
result = Regex.Replace(result, "\\s+<\\w+ xsi:nil=\"true\" \\/>", string.Empty);
return result;
}
答案 5 :(得分:2)
1)扩展
public static string Serialize<T>(this T value) {
if (value == null) {
return string.Empty;
}
try {
var xmlserializer = new XmlSerializer(typeof(T));
var stringWriter = new Utf8StringWriter();
using (var writer = XmlWriter.Create(stringWriter)) {
xmlserializer.Serialize(writer, value);
return stringWriter.ToString();
}
} catch (Exception ex) {
throw new Exception("An error occurred", ex);
}
}
1a)Utf8StringWriter
public class Utf8StringWriter : StringWriter {
public override Encoding Encoding { get { return Encoding.UTF8; } }
}
2)创建XElement
XElement xml = XElement.Parse(objectToSerialization.Serialize());
3)删除Nil的
xml.Descendants().Where(x => x.Value.IsNullOrEmpty() && x.Attributes().Where(y => y.Name.LocalName == "nil" && y.Value == "true").Count() > 0).Remove();
4)保存到文件
xml.Save(xmlFilePath);
答案 6 :(得分:2)
这个问题很久以前就被问过了,但即使在2017年仍然非常重要。这里提出的答案都没有让我感到满意,所以这里是一个简单的解决方案我来了用:
使用正则表达式是关键。由于我们无法控制XmlSerializer的行为,因此请不要试图阻止它序列化那些可以为空的值类型。相反,只需使用Regex获取序列化输出并使用空字符串替换不需要的元素。使用的模式(在C#中)是:
<\w+\s+\w+:nil="true"(\s+xmlns:\w+="http://www.w3.org/2001/XMLSchema-instance")?\s*/>
以下是一个例子:
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Serialization;
namespace MyNamespace
{
/// <summary>
/// Provides extension methods for XML-related operations.
/// </summary>
public static class XmlSerializerExtension
{
/// <summary>
/// Serializes the specified object and returns the XML document as a string.
/// </summary>
/// <param name="obj">The object to serialize.</param>
/// <param name="namespaces">The <see cref="XmlSerializerNamespaces"/> referenced by the object.</param>
/// <returns>An XML string that represents the serialized object.</returns>
public static string Serialize(this object obj, XmlSerializerNamespaces namespaces = null)
{
var xser = new XmlSerializer(obj.GetType());
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
{
using (var xtw = new XmlTextWriter(sw))
{
if (namespaces == null)
xser.Serialize(xtw, obj);
else
xser.Serialize(xtw, obj, namespaces);
}
}
return sb.ToString().StripNullableEmptyXmlElements();
}
/// <summary>
/// Removes all empty XML elements that are marked with the nil="true" attribute.
/// </summary>
/// <param name="input">The input for which to replace the content. </param>
/// <param name="compactOutput">true to make the output more compact, if indentation was used; otherwise, false.</param>
/// <returns>A cleansed string.</returns>
public static string StripNullableEmptyXmlElements(this string input, bool compactOutput = false)
{
const RegexOptions OPTIONS =
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline;
var result = Regex.Replace(
input,
@"<\w+\s+\w+:nil=""true""(\s+xmlns:\w+=""http://www.w3.org/2001/XMLSchema-instance"")?\s*/>",
string.Empty,
OPTIONS
);
if (compactOutput)
{
var sb = new StringBuilder();
using (var sr = new StringReader(result))
{
string ln;
while ((ln = sr.ReadLine()) != null)
{
if (!string.IsNullOrWhiteSpace(ln))
{
sb.AppendLine(ln);
}
}
}
result = sb.ToString();
}
return result;
}
}
}
我希望这会有所帮助。
答案 7 :(得分:0)
编写这样的代码的最简单方法是确切输出很重要:
xsd.exe
将您的架构转换为类。xsd.exe
)并根据原始架构进行检查,以确保序列化程序正确地再现了您想要的所有行为。调整并重复,直到您有工作代码。
如果您不确定最初使用的确切数据类型,请从步骤3开始,而不是步骤1,然后进行调整。
IIRC,对于你的例子,你几乎肯定会得到你已经描述过的Specified
属性,但是为你生成这些属性肯定会手动编写它们。 : - )
答案 8 :(得分:0)
如果使要序列化的类实现IXmlSerializable,则可以使用以下编写器。请注意,您将需要实现阅读器,但这并不难。
public void WriteXml(XmlWriter writer)
{
foreach (var p in GetType().GetProperties())
{
if (p.GetCustomAttributes(typeof(XmlIgnoreAttribute), false).Any())
continue;
var value = p.GetValue(this, null);
if (value != null)
{
writer.WriteStartElement(p.Name);
writer.WriteValue(value);
writer.WriteEndElement();
}
}
}
答案 9 :(得分:-2)
标记元素 [XmlElement(“elementName”,IsNullable = false)] 空值将被省略。