我需要生成一个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相关问答推荐

无法将blob发送到Azure -缺少HTTP标头异常

AutoMapper -如何为两个不同的用例设置单个映射?

ASP.NET Core:如何在IPageFilter中注入ApplicationDbContext

如何阻止注释被包含在C#release build. exe中

Polly v8—使用PredicateBuilder重试特定的状态代码

如何定义EFCore中的多个穿透

需要在重新启动ApplicartionPool或IIS后启动/唤醒API的帮助

具有以接口为其类型的属性的接口;类指定接口的实现,但无效

错误CS1061';AuthenticationBuilder';不包含AddOpenIdConnect的定义

如何在发布NuGet包之前设置命名空间?

我可以强制System.Text.Json.JsonSerializer以非递归方式工作吗?

如何使用类似于[SELECT*FROM&Q;&Q;WHERE&Q;]SQL查询的System.Data.Entity创建查询?

在同一个捕获中可以有多种类型的异常吗?

我应该为C#12中的主构造函数参数创建私有属性吗?

Visual Studio 17.8.0制表符自动完成问题--三缩进

Azure函数正在返回值列表,但该列表在Chrome中显示为空

正在try 将自定义字体添加到我的控制台应用程序

从HTML元素获取 colored颜色

避免在特定区域中设置Visual Studio代码的自动格式

使用c#中的Windows 10关机消息