RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 739703
Accepted
Андрей NOP
Андрей NOP
Asked:2020-11-03 16:37:10 +0000 UTC2020-11-03 16:37:10 +0000 UTC 2020-11-03 16:37:10 +0000 UTC

具有特殊规则的 XML 序列化

  • 772

有一个电子文件管理系统。数据交换是使用具有以下结构的 XML 完成的:

<document>
    <tag1 value="1"/>
    <tag2 value="text"/>
    <tag3 value="01.01.2017 10:20:15"/>
    <tag4 value="2"/>
    <tag5 value="02.02.2017 20:30:45"/>
    <tag6 value="text too"/>
    <tag7 value="3.5"/>
    <outerTag1>
        <innerTag11 value="5"/>
        <innerTag12 value="some text"/>
        <innerTag13 value="some text"/>
        <innerTag14 value="7"/>
        <innerTag15 value="8"/>
        <innerTag16 value="6"/>
        <innerOuterTag11>
            <innerInnerTag111 value="text"/>
            <innerInnerTag112 value="03.03.2017 03:03:03"/>
        </innerOuterTag11>
    </outerTag1>
    <outerTag2>
        <innerTag21 value="text"/>
        <innerTag22 value="text"/>
        <innerTag23 value="text"/>
        <innerTag24 value="text"/>
        <innerTag25 value="text"/>
        <innerTag26 value="text"/>
    </outerTag2>
</document>

问题就在眼前——你需要一堆具有这种结构的类:

[XmlRoot(ElementName="tag1")]
public class Tag1
{
    [XmlAttribute(AttributeName="value")]
    public int Value { get; set; }
}

接着:

[XmlRoot(ElementName="document")]
public class Document
{
    [XmlElement(ElementName="tag1")]
    public Tag1 Tag1 { get; set; }
    [XmlElement(ElementName="tag2")]
    public Tag2 Tag2 { get; set; }
    [XmlElement(ElementName="tag3")]
    public Tag3 Tag3 { get; set; }
    [XmlElement(ElementName="tag4")]
    public Tag4 Tag4 { get; set; }
    [XmlElement(ElementName="tag5")]
    public Tag5 Tag5 { get; set; }
    [XmlElement(ElementName="tag6")]
    public Tag6 Tag6 { get; set; }
    [XmlElement(ElementName="tag7")]
    public Tag7 Tag7 { get; set; }
    [XmlElement(ElementName="outerTag1")]
    public OuterTag1 OuterTag1 { get; set; }
    [XmlElement(ElementName="outerTag2")]
    public OuterTag2 OuterTag2 { get; set; }
}

我想改为编写一个具有简单类型属性的类:

[XmlRoot(ElementName="document")]
public class Document
{
    [...(ElementName="tag1")]
    public int Tag1 { get; set; }
    [...(ElementName="tag2")]
    public string Tag2 { get; set; }
    [...(ElementName="tag3")]
    public DateTime Tag3 { get; set; }
    [...(ElementName="tag4")]
    public int Tag4 { get; set; }
    [...(ElementName="tag5")]
    public DateTime Tag5 { get; set; }
    [...(ElementName="tag6")]
    public string Tag6 { get; set; }
    [...(ElementName="tag7")]
    public decimal Tag7 { get; set; }
    [XmlElement(ElementName="outerTag1")]
    public OuterTag1 OuterTag1 { get; set; }
    [XmlElement(ElementName="outerTag2")]
    public OuterTag2 OuterTag2 { get; set; }
}

而不是产生一堆像Tag1, Tag2, ...这样的小类

这可以以某种方式完成吗?理想情况下,我想制作一个自定义属性MyXmlElement并使用它来代替XmlElement,但是如何教XmlSerializer理解它并生成相应的标记?或者也许还有其他方法?

c#
  • 2 2 个回答
  • 10 Views

2 个回答

  • Voted
  1. VladD
    2020-11-03T22:25:24Z2020-11-03T22:25:24Z

    让我们进行代码生成,好吗?T4很棒。我们将创建两个类,一个具有用于序列化的嵌套类,另一个是平面且易于使用的。和转换功能。

    我们将基于这样的XML 文档生成(我把它放在一个名为DocumentProto.xml 的项目中)。

    <?xml version="1.0" encoding="utf-8" ?>
    <document>
      <tag1 type="int"/>
      <tag2 type="string"/>
      <tag3 type="DateTime"/>
      <tag4 type="int"/>
      <tag5 type="DateTime"/>
      <tag6 type="string"/>
      <tag7 type="double"/>
      <outerTag1>
        <innerTag11 type="int"/>
        <innerTag12 type="string"/>
        <innerTag13 type="string"/>
        <innerTag14 type="int"/>
        <innerTag15 type="int"/>
        <innerTag16 type="int"/>
        <innerOuterTag11>
          <innerInnerTag111 type="string"/>
          <innerInnerTag112 type="DateTime"/>
        </innerOuterTag11>
      </outerTag1>
      <outerTag2>
        <innerTag21 type="string"/>
        <innerTag22 type="string"/>
        <innerTag23 type="string"/>
        <innerTag24 type="string"/>
        <innerTag25 type="string"/>
        <innerTag26 type="string"/>
      </outerTag2>
    </document>
    

    我们通过 Add New Item → Text Template(不是 Runtime TextTemplate!)将新的 T4 类型文件放入项目中。我将其命名为 Document.tt。

    在第一行中,更改hostspecific="false"为true。添加必要的程序集:

    <#@ assembly name="System.Xml" #>
    <#@ assembly name="System.Xml.Linq" #>
    

    和

    <#@ import namespace="System.Xml.Linq" #>
    

    更改output extension为".cs".

    接下来是技术问题:我们需要解析 XML。打开文档,将其读入内存:

    <#
        var xmlpath = Host.ResolvePath("DocumentProto.xml");
        XDocument xd = XDocument.Load(xmlpath);
    #>
    

    创建文件模板:

    // Generated code! Do not edit!
    
    using System;
    using System.Xml.Serialization;
    
    namespace CodegenTest
    {
        // будем добавлять тут
    }
    

    现在,生成一组用于序列化的类。我们在 tt 文件的末尾写(之前在命令行测试应用程序上对此进行了调试):

    <#+
        void GenerateNestedClasses(XElement element)
        {
            var childClasses = new Queue<XElement>();
            string className = element.Name.LocalName;
            string capitalizedClassName = char.ToUpper(className[0]) + className.Substring(1);
            WriteLine($"[XmlRoot(ElementName=\"{className}\")]");
            WriteLine($"class {capitalizedClassName}");
            WriteLine("{");
            foreach (var sub in element.Elements())
            {
                string type;
                string name = sub.Name.LocalName;
                string capitalizedName = char.ToUpper(name[0]) + name.Substring(1);
                if (!sub.HasAttributes) // nested class
                {
                    type = capitalizedName;
                    childClasses.Enqueue(sub);
                }
                else
                {
                    type = (string)sub.Attribute("type");
                }
    
                WriteLine($"    [XmlElement(ElementName=\"{name}\")]");
                WriteLine($"    public {type} {capitalizedName} {{ get; set; }}");
            }
            WriteLine("}");
            WriteLine("");
    
            foreach (var child in childClasses)
                GenerateNestedClasses(child);
        }
    
    #>
    

    (标签<#+ #>内有额外的生成方法。)

    我们用:

        namespace Serialization
        {
    <#
        PushIndent("        ");
        GenerateNestedClasses(xd.Root);
        ClearIndent();
    #>
        }
    

    我们进入 Document.cs:

    namespace Serialization
    {
        [XmlRoot(ElementName="document")]
        class Document
        {
            [XmlElement(ElementName="tag1")]
            public int Tag1 { get; set; }
            [XmlElement(ElementName="tag2")]
            public string Tag2 { get; set; }
            [XmlElement(ElementName="tag3")]
            public DateTime Tag3 { get; set; }
            [XmlElement(ElementName="tag4")]
            public int Tag4 { get; set; }
            [XmlElement(ElementName="tag5")]
            public DateTime Tag5 { get; set; }
            [XmlElement(ElementName="tag6")]
            public string Tag6 { get; set; }
            [XmlElement(ElementName="tag7")]
            public double Tag7 { get; set; }
            [XmlElement(ElementName="outerTag1")]
            public OuterTag1 OuterTag1 { get; set; }
            [XmlElement(ElementName="outerTag2")]
            public OuterTag2 OuterTag2 { get; set; }
        }
    
        [XmlRoot(ElementName="outerTag1")]
        class OuterTag1
        {
            [XmlElement(ElementName="innerTag11")]
            public int InnerTag11 { get; set; }
            [XmlElement(ElementName="innerTag12")]
            public string InnerTag12 { get; set; }
            [XmlElement(ElementName="innerTag13")]
            public string InnerTag13 { get; set; }
            [XmlElement(ElementName="innerTag14")]
            public int InnerTag14 { get; set; }
            [XmlElement(ElementName="innerTag15")]
            public int InnerTag15 { get; set; }
            [XmlElement(ElementName="innerTag16")]
            public int InnerTag16 { get; set; }
            [XmlElement(ElementName="innerOuterTag11")]
            public InnerOuterTag11 InnerOuterTag11 { get; set; }
        }
    
        [XmlRoot(ElementName="innerOuterTag11")]
        class InnerOuterTag11
        {
            [XmlElement(ElementName="innerInnerTag111")]
            public string InnerInnerTag111 { get; set; }
            [XmlElement(ElementName="innerInnerTag112")]
            public DateTime InnerInnerTag112 { get; set; }
        }
    
        [XmlRoot(ElementName="outerTag2")]
        class OuterTag2
        {
            [XmlElement(ElementName="innerTag21")]
            public string InnerTag21 { get; set; }
            [XmlElement(ElementName="innerTag22")]
            public string InnerTag22 { get; set; }
            [XmlElement(ElementName="innerTag23")]
            public string InnerTag23 { get; set; }
            [XmlElement(ElementName="innerTag24")]
            public string InnerTag24 { get; set; }
            [XmlElement(ElementName="innerTag25")]
            public string InnerTag25 { get; set; }
            [XmlElement(ElementName="innerTag26")]
            public string InnerTag26 { get; set; }
        }
    
    }
    

    我们还将“平面类”属性的生成添加到文件末尾:

    void GenerateFlatClassProps(XElement element)
    {
        foreach (var sub in element.Elements())
        {
            if (!sub.HasAttributes) // nested
                GenerateFlatClassProps(sub);
            else
            {
                var type = (string)sub.Attribute("type");
                string name = sub.Name.LocalName;
                string capitalizedName = char.ToUpper(name[0]) + name.Substring(1);
                WriteLine($"public {type} {capitalizedName} {{ get; set; }}");
            }
        }
    }
    

    和一个属性读取器方法到一个平面类中:

    void GenerateFlatteningBody(XElement element, string path)
    {
        string name = element.Name.LocalName;
        string capitalizedName = char.ToUpper(name[0]) + name.Substring(1);
        foreach (var sub in element.Elements())
        {
            string subName = sub.Name.LocalName;
            string capitalizedSubName = char.ToUpper(subName[0]) + subName.Substring(1);
            if (!sub.HasAttributes) // nested
                GenerateFlatteningBody(sub, path + "." + capitalizedSubName);
            else
                WriteLine($"this.{capitalizedSubName} = that{path}.{capitalizedSubName};");
        }
    }
    

    我们在上面使用它们:

        public class Document
        {
    <#
        PushIndent("        ");
        GenerateFlatClassProps(xd.Root);
        ClearIndent();
    #>
            private void AssignFromSerialized(Serialization.Document that)
            {
    <#
        PushIndent("            ");
        GenerateFlatteningBody(xd.Root, "");
        ClearIndent();
    #>
            }
    
            internal static Document FromSerialized(Serialization.Document sdoc)
            {
                var doc = new Document();
                doc.AssignFromSerialized(sdoc);
                return doc;
            }
        }
    

    我们得到一组现成的生成类供使用:

    namespace CodegenTest
    {
    
        public class Document
        {
            public int Tag1 { get; set; }
            public string Tag2 { get; set; }
            public DateTime Tag3 { get; set; }
            public int Tag4 { get; set; }
            public DateTime Tag5 { get; set; }
            // ...
            private void AssignFromSerialized(Serialization.Document that)
            {
                this.Tag1 = that.Tag1;
                this.Tag2 = that.Tag2;
                // ...
                this.InnerTag11 = that.OuterTag1.InnerTag11;
                this.InnerTag12 = that.OuterTag1.InnerTag12;
                this.InnerTag13 = that.OuterTag1.InnerTag13;
                this.InnerTag14 = that.OuterTag1.InnerTag14;
                this.InnerTag15 = that.OuterTag1.InnerTag15;
                this.InnerTag16 = that.OuterTag1.InnerTag16;
                this.InnerInnerTag111 = that.OuterTag1.InnerOuterTag11.InnerInnerTag111;
                // ...
            }
    
            internal static Document FromSerialized(Serialization.Document sdoc)
            {
                var doc = new Document();
                doc.AssignFromSerialized(sdoc);
                return doc;
            }
        }
    
        namespace Serialization
        {
            [XmlRoot(ElementName="document")]
            class Document
            {
                [XmlElement(ElementName="tag1")]
                public int Tag1 { get; set; }
                // ...
                [XmlElement(ElementName="outerTag1")]
                public OuterTag1 OuterTag1 { get; set; }
                [XmlElement(ElementName="outerTag2")]
                public OuterTag2 OuterTag2 { get; set; }
            }
    
            [XmlRoot(ElementName="outerTag1")]
            class OuterTag1
            {
                [XmlElement(ElementName="innerTag11")]
                public int InnerTag11 { get; set; }
                [XmlElement(ElementName="innerTag12")]
                public string InnerTag12 { get; set; }
                // ...
            }
    
            // ...
        }
    }
    

    更新:更正GenerateNestedClasses此:

    void GenerateNestedClasses(XElement element)
    {
        var childClasses = new Queue<XElement>();
        var leafClasses = new Queue<XElement>();
        string className = element.Name.LocalName;
        string capitalizedClassName = char.ToUpper(className[0]) + className.Substring(1);
        WriteLine($"[XmlRoot(ElementName=\"{className}\")]");
        WriteLine($"class {capitalizedClassName}");
        WriteLine("{");
        foreach (var sub in element.Elements())
        {
            string name = sub.Name.LocalName;
            string capitalizedName = char.ToUpper(name[0]) + name.Substring(1);
            if (!sub.HasAttributes) // nested class
                childClasses.Enqueue(sub);
            else
                leafClasses.Enqueue(sub);
    
            WriteLine($"    [XmlElement(ElementName=\"{name}\")]");
            WriteLine($"    public {capitalizedName} {capitalizedName} {{ get; set; }}");
        }
        WriteLine("}");
        WriteLine("");
    
        foreach (var leaf in leafClasses)
            GenerateLeafClass(leaf);
        foreach (var child in childClasses)
            GenerateNestedClasses(child);
    }
    
    void GenerateLeafClass(XElement element)
    {
        string className = element.Name.LocalName;
        string capitalizedClassName = char.ToUpper(className[0]) + className.Substring(1);
        string type = (string)element.Attribute("type");
        WriteLine($"class {capitalizedClassName}");
        WriteLine("{");
        WriteLine($"    [XmlAttribute]");
        WriteLine($"    public {type} Value {{ get; set; }}");
        WriteLine("}");
        WriteLine("");
    }
    

    在GenerateFlatteningBody最后一行

    WriteLine($"this.{capitalizedSubName} = that{path}.{capitalizedSubName}.Value;");
    

    我们得到了表格的中间类

    class Tag1
    {
        [XmlAttribute]
        public int Value { get; set; }
    }
    

    以防万一,tt模板的完整代码和生成的结果:https ://gist.github.com/vladd/7f25e0ceb625372bffdbf9b455452ae1

    • 3
  2. Best Answer
    Андрей NOP
    2020-11-09T16:18:57Z2020-11-09T16:18:57Z

    我拒绝实现接口IXmlSerializable- 解决方案结果非常繁琐且不美观,此外,您需要考虑在序列化程序的正常操作中考虑到的许多细微差别。

    结果,我写了一个简单的类:

    public class Tag<T>
    {
        [XmlAttribute(AttributeName = "value")]
        public T Value { get; set; }
    
        public override string ToString() => Value.ToString();
    
        public static implicit operator Tag<T>(T value) => new Tag<T> { Value = value };
        public static implicit operator T(Tag<T> tag) => tag.Value;
    }
    

    这让我们可以扔掉一堆小班Tag1,Tag2等等。文档本身如下所示:

    [XmlRoot(ElementName="document")]
    public class Document
    {
        [XmlElement(ElementName="tag1")]
        public Tag<int> Tag1 { get; set; }
        [XmlElement(ElementName="tag2")]
        public Tag<string> Tag2 { get; set; }
        [XmlElement(ElementName="tag3")]
        public Tag<DateTime> Tag3 { get; set; }
        [XmlElement(ElementName="tag4")]
        public Tag<int> Tag4 { get; set; }
        [XmlElement(ElementName="tag5")]
        public Tag<DateTime> Tag5 { get; set; }
        [XmlElement(ElementName="tag6")]
        public Tag<string> Tag6 { get; set; }
        [XmlElement(ElementName="tag7")]
        public Tag<decimal> Tag7 { get; set; }
        [XmlElement(ElementName="outerTag1")]
        public OuterTag1 OuterTag1 { get; set; }
        [XmlElement(ElementName="outerTag2")]
        public OuterTag2 OuterTag2 { get; set; }
    }
    

    好吧,感谢隐式类型转换的运算符,创建文档同样简单:

    var doc = new Document
    {
        Tag1 = 1,
        Tag2 = "text",
        ...
    };
    
    • 2

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    Python 3.6 - 安装 MySQL (Windows)

    • 1 个回答
  • Marko Smith

    C++ 编写程序“计算单个岛屿”。填充一个二维数组 12x12 0 和 1

    • 2 个回答
  • Marko Smith

    返回指针的函数

    • 1 个回答
  • Marko Smith

    我使用 django 管理面板添加图像,但它没有显示

    • 1 个回答
  • Marko Smith

    这些条目是什么意思,它们的完整等效项是什么样的

    • 2 个回答
  • Marko Smith

    浏览器仍然缓存文件数据

    • 1 个回答
  • Marko Smith

    在 Excel VBA 中激活工作表的问题

    • 3 个回答
  • Marko Smith

    为什么内置类型中包含复数而小数不包含?

    • 2 个回答
  • Marko Smith

    获得唯一途径

    • 3 个回答
  • Marko Smith

    告诉我一个像幻灯片一样创建滚动的库

    • 1 个回答
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Алексей Шиманский 如何以及通过什么方式来查找 Javascript 代码中的错误? 2020-08-03 00:21:37 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5