我需要生成一个Xml文件,格式如下:

<root>
   <![CDATA[
   <info>
      <number>12</number>
      <files>1</files>
   </info> 
   ]]>
   <info2>
      <prop1> prop1 </prop1>
   </info2>
</root>

其中一个标记必须包含CDATA部分,而其他标记必须为标准格式.

我可以使用XmlSerializer生成它,但没有CDATA节,但我想不出用这个CDATA节序列化它的方法

推荐答案

您可以使用[XmlText]属性来指示属性应序列化为文本而不是标记,例如:

[XmlRoot("root")]
public class Root
{
    [XmlText]
    public string Value { get; set; }

    [XmlElement("info2")]
    public Info2 Info2 { get; set; }    
}

但是,如果您用问题中所示的CDATA内容填充Root.Value,则生成的XML虽然格式良好,但其字符将单独转义,而不是作为CDATA:

<root>&lt;info&gt;
   &lt;number&gt;12&lt;/number&gt;
   &lt;files&gt;1&lt;/files&gt;
&lt;/info&gt; <Info2><prop1> prop1 </prop1></Info2></root>

演示小提琴#1here.

这种形式的XML转义在语义上等同于CDATA转义,但如果出于某种原因您对CDATA转义require,您可以使用remarks for XmlTextAttribute中提到的以下功能.

XmlTextAttribute还可以应用于返回XmlNodeXmlNode对象数组的字段.

诀窍是引入一个代理XmlNode []属性,该属性返回封装在XmlNode数组中包含的XmlCDataSection对象中的所需文本内容.如果我们进一步假设内容是序列化名为Info的其他属性的结果,您的数据模型可能如下所示:

[XmlRoot("root")]
public class Root
{
    [XmlIgnore]
    public Info Info { get; set; }
    
    //https://learn.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmltextattribute?view=net-8.0
    //The XmlTextAttribute can also be applied to a field that returns an XmlNode or an array of XmlNode objects.
    [XmlText]
    public XmlNode [] XmlValue 
    {
        get => new XmlNode [] { new XmlDocument().CreateCDataSection(Info.GetXml(indent : true, omitStandardNamespaces : true, omitXmlDeclaration : true)) };
        set
        {
            var xml = string.Concat(value?.Cast<XmlCharacterData>().Select(c => c.Value)).Trim();
            if (!string.IsNullOrEmpty(xml))
                Info = xml.LoadFromXml<Info>();
        }
    }
    
    [XmlElement("info2")]
    public Info2 Info2 { get; set; }    
}

[XmlRoot("info")]
public class Info
{
    [XmlElement("number")] public int Number { get; set; }
    [XmlElement("files")] public int Files { get; set; }
}

public class Info2
{
    [XmlElement("prop1")]
    public string Prop1 { get; set; }
}

它使用以下扩展方法:

public static partial class XmlSerializationHelper
{
    public static T? LoadFromXml<T>(this string xmlString, XmlSerializer? serializer = null)
    {
        using (var reader = new StringReader(xmlString))
            return (T?)(serializer ?? new(typeof(T))).Deserialize(reader);
    }

    public static string GetXml<T>(this T? obj, XmlSerializer? serializer = null, bool indent = true, bool omitStandardNamespaces = false, bool omitXmlDeclaration = false)
    {
        XmlSerializerNamespaces? ns = null;
        if (omitStandardNamespaces)
        {
            ns = new ();
            ns.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
        }           
        using var textWriter = new StringWriter();
        var settings = new XmlWriterSettings() { Indent = indent, OmitXmlDeclaration = omitXmlDeclaration };
        using (var xmlWriter = XmlWriter.Create(textWriter, settings))
            (serializer ?? new(obj?.GetType() ?? typeof(T))).Serialize(xmlWriter, obj, ns);
        return textWriter.ToString();
    }
}

此版本的Root在与XmlSerializer序列化时会生成以下XML:

<root><![CDATA[<info>
  <number>1</number>
  <files>1</files>
</info>]]><info2><prop1> prop1 </prop1></info2></root>

演示小提琴#2here.

备注:

  • 你的<root>元素等于100.如XmlWriterSettings.Indent的文档中所述

    只要元素不包含混合内容,元素就会缩进.一旦调用了WriteString或WriteWhite方法以写出混合元素内容,则为the XmlWriter stops indenting.一旦混合内容元素关闭,缩进就会继续.

    因此,似乎不可能获得您的.NET XmlWriter问题中所示的精确缩进.因为这样的空格是not significant,所以这对任何接收系统都不重要,但如果是这样的话,您可能需要采用不同的XML框架或编写您自己的XmlWriter子类.

    使用正则表达式进行后处理可能是另一种 Select .

  • 出于某种原因,如果我声明XmlValue属性返回单个XmlNode,例如

     [XmlText]
     public XmlNode XmlValue { get => new XmlDocument().CreateCDataSection(Value); set => Value = value?.Value; }
    

    然后,在.NET8中,XmlSerializer构造函数将失败并抛出异常,尽管有文档记录它可以工作:

    System.InvalidOperationException:无法序列化类型为System.Xml.XmlNode的成员‘XmlValue’.XmlAttribute/XmlText不能用于编码复杂类型.

    这就是我使用数组属性的原因.

    小提琴here不及格.

  • [XmlText]解决方案似乎比100中建议的实现IXmlSerializable简单得多.

Csharp相关问答推荐

如何使用C#中的图形API更新用户配置文件图像

ASP.NET MVC中创建视图的过滤器

. NET JSON属性自定义所需逻辑

如何在不考虑年份的情况下判断日期时间是否在某个日期范围内?

在C#中,有没有一种方法可以集中定义跨多个方法使用的XML参数描述符?

在命名管道上使用GRPC ASP.NET核心时如何配置命名管道权限

方法从数据表中只 Select 一个条件?

C#-VS2022:全局使用和保存时的代码清理

C#带主体的主构造函数?

如何在C#中创建VS代码中的控制台应用程序时自动生成Main方法

BFF到具有AAD/Entra ID B2C的内部API(.NET/ASP.NET核心/标识.Web)

Blazor Fluent UI DialogService,<;FluentDialogProvider/>;错误

为什么此名称不再被识别?名称不存在于当前上下文中?

获取混淆&Quot;模糊引用&Quot;错误

如何使用EPPlus C#在单个单元格中可视化显示多行文字

使用Blazor WebAssembly提高初始页面加载时间的性能

Maui:更改代码中的绑定字符串不会更新UI,除非重新生成字符串(MVVM)

DropDownListFor未显示选定值

C#如何替换两个XML元素值?

如何在C#的Linq查询中将两行合并为一行