简体   繁体   English

如何将使用键的 XML 反序列化为对象

[英]How do I deserialize XML that uses a key into an object

I have an ASP.NET core 2.1 MVC project and I am retrieving data from a Serlilog MSSqlServr Sink that stores values in a Properties field as an XML data type.我有一个 ASP.NET 核心 2.1 MVC 项目,我正在从 Serlilog MSSqlServr 接收器检索数据,该接收器将属性字段中的值存储为 XML 数据类型。 I want to deserialize that data into a viewmodel so I can present the individual elements as data in a view.我想将该数据反序列化为一个视图模型,以便我可以在视图中将各个元素作为数据呈现。

Here is an example of the XML from the Properties field in the database Logs table.以下是数据库日志表中“属性”字段的 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>

I set up a class for the decoding as follows;我为解码设置了一个类,如下所示;

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; }

    }
}

And in my controller, I have the following code;在我的控制器中,我有以下代码;

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

        LogProperties logProperties;

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

But the logProperties variable does not contain anything, so I assume I have the XML attributes incorrect in the LogProperties class.但是 logProperties 变量不包含任何内容,因此我假设我在 LogProperties 类中的 XML 属性不正确。

I have spent quite a bit of time searching for a solution and I reviewed all of the related posts while entering this question but I am not able to find an example where the XML is using "property key=" or how to deal with a "key=" attribute property (if that is the correct term)我花了很多时间寻找解决方案,并在输入此问题时查看了所有相关帖子,但我无法找到 XML 使用“属性键=”或如何处理“的示例” key=" 属性属性(如果这是正确的术语)

Any ideas?有任何想法吗?

[UPDATE 2/21/19] [更新 2/21/19]

I ended up using @jdweng suggestion as it was the least complex and gave me exactly what I wanted.我最终使用了@jdweng 的建议,因为它是最简单的并且给了我我想要的。

I created 2 classes (as I like to keep my class files separated as a personal pref.).我创建了 2 个类(因为我喜欢将我的类文件作为个人偏好分开)。 The classes are below;课程如下;

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; }

    }
}

and

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; }
    }
}

Then in my Controller, I have the following for the Detail method;然后在我的控制器中,我有以下 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);

My DetailLogWithPropertiesViewModel is below;我的 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; }

}

And the relevant portion of my Detail.cshtml is below;我的 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>

The XML that is generated by Serilog and stored in the MS SQL Database has a variable number of properties, which I now understand are represented as key/value pairs.由 Serilog 生成并存储在 MS SQL 数据库中的 XML 具有可变数量的属性,我现在理解这些属性表示为键/值对。 So this method allows me to ensure that all of the provided properties are displayed in the log viewer of the website.所以这个方法允许我确保所有提供的属性都显示在网站的日志查看器中。

Try following :尝试以下:

    [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; }
    }

1) Copy your XML to clipboard... 1) 将您的 XML 复制到剪贴板...
2) ...Open Visual Studio and create an empty cs file... 2) ...打开Visual Studio并创建一个空的cs文件...
3) ...go to EDIT > Paste Special > XML to classes ( might require ASP.NET webdevelopment to be installed )... 3) ...转到编辑>选择性粘贴> XML到类(可能需要安装ASP.NET webdevelopment )...
3) ...will result in this code: 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) Yes, much code o_O. 4)是的,很多代码o_O。 Use an XmlDeserializer(typeof(properties))使用XmlDeserializer(typeof(properties))

If you make a sample instance of your LogProperties class and serialize it like this:如果您制作 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());

This is what you get:这是你得到的:

<?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>

So the class you have doesn't fit the XML you have very well.因此,您拥有的课程与您拥有的 XML 不太匹配。 It is always useful to try serialization in both directions.尝试双向序列化总是有用的。

Below is a way you can serialize the XML you have into the class you have (getting a clear separation of your object model and your persistence format).下面是一种可以将您拥有的 XML 序列化到您拥有的类中的方法(明确分离您的对象模型和您的持久性格式)。 It uses the XDocument class from the System.Xml.Linq namespace.它使用 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

    }
}

Note that with this approach:请注意,使用这种方法:

  • you dont need the XML attributes in your class您不需要类中的 XML 属性
  • you dont need to shape your class to fit the XML (or the other way around)你不需要塑造你的班级以适应 XML(或其他方式)
  • you can add your own custom initialization of the LogProperties class (no need for a default constructor anymore)您可以添加自己的 LogProperties 类的自定义初始化(不再需要默认构造函数)
  • you can add your own custom error handling您可以添加自己的自定义错误处理

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM