【问题标题】:How do I deserialize XML that uses a key into an object如何将使用键的 XML 反序列化为对象
【发布时间】:2019-02-20 16:57:34
【问题描述】:

我有一个 ASP.NET 核心 2.1 MVC 项目,我正在从 Serlilog MSSqlServr Sink 检索数据,该接收器将值作为 XML 数据类型存储在 Properties 字段中。我想将该数据反序列化为视图模型,以便可以在视图中将各个元素呈现为数据。

这是来自数据库日志表中属性字段的 XML 示例。

<properties>
  <property key="EventId">
    <structure type="">
      <property key="Id">404</property>
    </structure>
  </property>
  <property key="ActionId">0592d9e8-f4fd-459f-96b3-2b787d01a754</property>
  <property key="ActionName">API.Controllers.CompletionsController.GetCompletion (PS.API)</property>
  <property key="RequestId">0HLJ2IL5A9:00000001</property>
  <property key="RequestPath">/api/completions/0</property>
  <property key="CorrelationId" />
  <property key="ConnectionId">0HLJ2IL59</property>
  <property key="MachineName">RD0003FF1</property>
  <property key="ThreadId">117</property>
</properties>

我为解码设置了一个类,如下所示;

using System.Xml.Serialization;

namespace PS.Models.ApiLogs
{
    [XmlRoot("properties")]
    public class LogProperties
    {

        [XmlElement("SourceContext")]
        public string SourceContext { get; set; }

        [XmlElement("ActionId")]
        public string ActionId { get; set; }

        [XmlElement("ActionName")]
        public string ActionName { get; set; }

        [XmlElement("RequestId")]
        public string RequestId { get; set; }

        [XmlElement("RequestPath")]
        public string RequestPath { get; set; }

        [XmlElement("CorrelationId")]
        public string CorrelationId { get; set; }

        [XmlElement("ConnectionId")]
        public string ConnectionId { get; set; }

        [XmlElement("MachineName")]
        public string MachineName { get; set; }

        [XmlElement("ThreadId")]
        public string ThreadId { get; set; }

    }
}

在我的控制器中,我有以下代码;

        var serializer = new XmlSerializer(typeof(LogProperties));

        LogProperties logProperties;

        using (TextReader reader = new StringReader(log.Properties))
        {
            logProperties = (LogProperties)serializer.Deserialize(reader);
        }

但是 logProperties 变量不包含任何内容,所以我假设我在 LogProperties 类中的 XML 属性不正确。

我花了很多时间寻找解决方案,并在输入此问题时查看了所有相关帖子,但我无法找到 XML 使用“property key=”或如何处理的示例带有“key=”属性属性(如果这是正确的术语)

有什么想法吗?

[2/21/19 更新]

我最终使用了@jdweng 的建议,因为它是最简单的并且给了我想要的东西。

我创建了 2 个类(因为我喜欢将我的类文件分开作为个人偏好)。课程如下;

using System.Collections.Generic;
using System.Xml.Serialization;

namespace PS.Models.ApiLogs
{
    [XmlRoot("properties")]
    public class LogProperties
    {
        [XmlElement("property")]
        public List<LogProperty> Property { get; set; }

    }
}

using System.Xml.Serialization;

namespace PS.Models.ApiLogs
{
    [XmlRoot("property")]
    public class LogProperty
    {
        [XmlAttribute("key")]
        public string Key { get; set; }
        [XmlText]
        public string Value { get; set; }
    }
}

然后在我的控制器中,我有以下 Detail 方法;

        var response = await _client.GetLogAsync(id, $"api/logs", token);
        if (response == null)
        {
            return NotFound($"Unable to find a record for Log ID [{id}].");
        }

        var log = _mapper.Map<DetailLogViewModel>(response.Record);

        var serializer = new XmlSerializer(typeof(LogProperties));

        LogProperties logProperties;

        using (TextReader reader = new StringReader(log.Properties))
        {
            logProperties = (LogProperties)serializer.Deserialize(reader);
        }

        var logWithProperties = new DetailLogWithPropertiesViewModel
        {
            Id = log.Id,
            Message = log.Message,
            TimeStamp = log.TimeStamp,
            Exception = log.Exception,
            XmlProperties = logProperties 
        };


        return View(logWithProperties);

我的 DetailLogWithPropertiesViewModel 在下面;

public class DetailLogWithPropertiesViewModel
{
    public int Id { get; set; }

    [Display(Name = "Message")]
    public string Message { get; set; }

    [Display(Name = "Level")]

    public string Level { get; set; }

    [Display(Name = "Time Stamp")]
    public DateTimeOffset TimeStamp { get; set; }

    [Display(Name = "Exception")]
    public string Exception { get; set; }

    [Display(Name = "Properties")]
    public string Properties { get; set; }

    public LogProperties XmlProperties { get; set; }

}

我的 Detail.cshtml 的相关部分如下;

<div class="card-body ml3 mr3">


    @foreach (var logProperty in Model.XmlProperties.Property)
    {
        <div class="row">
            <div class="col-4 bg-light border border-primary">
                <span class="font-weight-bold">@logProperty.Key</span>
            </div>
            <div class="col-8 bg-secondary border border-left-0 border-primary">
                <span>@logProperty.Value</span>
            </div>
        </div>


    }

</div>

由 Serilog 生成并存储在 MS SQL 数据库中的 XML 具有可变数量的属性,我现在理解这些属性表示为键/值对。因此,此方法允许我确保所有提供的属性都显示在网站的日志查看器中。

【问题讨论】:

  • 得到了另一个方向!如果您尝试使用您的类使用 XmlSerializer.Serialize() 创建 XML 文件(或带有 XML 的字符串),您将看到您描述的 XML 文档类型。

标签: c# xml xml-parsing xmlserializer asp.net-core-2.1


【解决方案1】:

尝试以下:

    [XmlRoot("properties")]
    public class LogProperties
    {

        [XmlElement("property")]
        public List<LogProperty> property { get; set; }

    }
    [XmlRoot("property")]
    public class LogProperty
    {
        [XmlAttribute("key")]
        public string key { get; set; }
        [XmlText]
        public string value { get; set; }
    }

【讨论】:

    【解决方案2】:

    1) 将您的 XML 复制到剪贴板...
    2) ...打开 Visual Studio 并创建一个空的 cs 文件...
    3) ...转到编辑 > 选择性粘贴 > XML 到类(可能需要安装 ASP.NET webdevelopment)...
    3) ...将导致此代码:

    /// <remarks/>
    [System.SerializableAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
    [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
    public partial class properties
    {
    
        private propertiesProperty[] propertyField;
    
        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute("property")]
        public propertiesProperty[] property
        {
            get
            {
                return this.propertyField;
            }
            set
            {
                this.propertyField = value;
            }
        }
    }
    
    /// <remarks/>
    [System.SerializableAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
    public partial class propertiesProperty
    {
    
        private propertiesPropertyStructure structureField;
    
        private string[] textField;
    
        private string keyField;
    
        /// <remarks/>
        public propertiesPropertyStructure structure
        {
            get
            {
                return this.structureField;
            }
            set
            {
                this.structureField = value;
            }
        }
    
        /// <remarks/>
        [System.Xml.Serialization.XmlTextAttribute()]
        public string[] Text
        {
            get
            {
                return this.textField;
            }
            set
            {
                this.textField = value;
            }
        }
    
        /// <remarks/>
        [System.Xml.Serialization.XmlAttributeAttribute()]
        public string key
        {
            get
            {
                return this.keyField;
            }
            set
            {
                this.keyField = value;
            }
        }
    }
    
    /// <remarks/>
    [System.SerializableAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
    public partial class propertiesPropertyStructure
    {
    
        private propertiesPropertyStructureProperty propertyField;
    
        private string typeField;
    
        /// <remarks/>
        public propertiesPropertyStructureProperty property
        {
            get
            {
                return this.propertyField;
            }
            set
            {
                this.propertyField = value;
            }
        }
    
        /// <remarks/>
        [System.Xml.Serialization.XmlAttributeAttribute()]
        public string type
        {
            get
            {
                return this.typeField;
            }
            set
            {
                this.typeField = value;
            }
        }
    }
    
    /// <remarks/>
    [System.SerializableAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
    public partial class propertiesPropertyStructureProperty
    {
    
        private string keyField;
    
        private ushort valueField;
    
        /// <remarks/>
        [System.Xml.Serialization.XmlAttributeAttribute()]
        public string key
        {
            get
            {
                return this.keyField;
            }
            set
            {
                this.keyField = value;
            }
        }
    
        /// <remarks/>
        [System.Xml.Serialization.XmlTextAttribute()]
        public ushort Value
        {
            get
            {
                return this.valueField;
            }
            set
            {
                this.valueField = value;
            }
        }
    }
    

    4) 是的,很多代码 o_O。使用XmlDeserializer(typeof(properties))

    【讨论】:

      【解决方案3】:

      如果您创建 LogProperties 类的示例实例并像这样对其进行序列化:

      var serializer = new XmlSerializer(typeof(LogProperties));
      
      LogProperties logProperties = new LogProperties() 
      { 
          SourceContext = "MySourceContext",
          ActionId = "MyActionId",
          ActionName = "MyActionName"
      };
      
      StringBuilder sb = new StringBuilder();
      using (StringWriter writer = new StringWriter(sb))
      {
          serializer.Serialize(writer, logProperties);
      }
      
      Console.WriteLine(sb.ToString());
      

      这就是你得到的:

      <?xml version="1.0" encoding="utf-16"?>
      <properties xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <SourceContext>MySourceContext</SourceContext>
        <ActionId>MyActionId</ActionId>
        <ActionName>MyActionName</ActionName>
      </properties>
      

      因此,您拥有的类不太适合您拥有的 XML。尝试双向序列化总是有用的。

      下面是一种可以将您拥有的 XML 序列化到您拥有的类中的方法(明确区分您的对象模型和持久性格式)。它使用 System.Xml.Linq 命名空间中的 XDocument 类。

      // Must escape all quotes !
      string xmlSample = @"
      <properties>
          <property key=""EventId"">
              <structure type = """">
                  <property key=""Id"">404</property>
              </structure>
          </property>
          <property key=""ActionId""> 0592d9e8 - f4fd - 459f - 96b3 - 2b787d01a754</property>
          <property key=""ActionName""> API.Controllers.CompletionsController.GetCompletion(PS.API)</property>
          <property key=""RequestId""> 0HLJ2IL5A9: 00000001</property>
          <property key=""RequestPath"">/api/completions/0</property>
          <property key=""CorrelationId"" />
          <property key=""ConnectionId"">0HLJ2IL59</property>
          <property key=""MachineName"">RD0003FF1</property>
          <property key=""ThreadId"">117</property>
      </properties>";
      
      StringReader reader = new StringReader(xmlSample);
      XDocument xdoc = XDocument.Parse(xmlSample);
      
      LogProperties log = new LogProperties();
      foreach (XElement prop in xdoc.Root.Elements())
      {
          switch (prop.Attribute("key").Value)
          {
          case "ActionId" : log.ActionId = prop.Value; break;
          case "ActionName" : log.ActionName = prop.Value; break;
      
          // and so on
      
          }
      }
      

      请注意,使用这种方法:

      • 你的类中不需要 XML 属性
      • 你不需要塑造你的类来适应 XML(或其他方式)
      • 您可以添加自己的 LogProperties 类的自定义初始化(不再需要默认构造函数)
      • 您可以添加自己的自定义错误处理

      【讨论】:

        猜你喜欢
        • 2017-08-20
        • 2018-10-11
        • 2013-03-24
        • 1970-01-01
        • 2020-05-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多