【问题标题】:Project a Type to class object by filtering out attributes using linq to xml通过使用 linq to xml 过滤掉属性,将类型投影到类对象
【发布时间】:2020-06-15 11:22:21
【问题描述】:

如果我有一个 xml 肥皂文档,并且我想获取其按属性分割的数据(元素),我如何过滤掉属性并将过滤器数据转换为类。

public  class report
{     
    public string VehicleName { get; set; }
    public string DriverName { get; set; }
}

Xml 样本:

<Rows>
    <Row ref="h_0_0" inx="0" kind="h">
        <Cell ref="h_0_0_0">Vehicle Name</Cell>
        <Cell ref="h_0_0_1">Date</Cell>
        <Cell ref="h_0_0_2">Driver</Cell>
        <Cell ref="h_0_0_3">Total Distance (GPS)</Cell>
        <Cell ref="h_0_0_4"> Max Speed</Cell>
        <Cell ref="h_0_0_5">Average Speed</Cell>
        <Cell ref="h_0_0_6">Expected Fuel Avg (km/l)</Cell>
        <Cell ref="h_0_0_7">Total Fuel</Cell>
        <Cell ref="h_0_0_8">Fuel Lost </Cell>
        <Cell ref="h_0_0_9">Location</Cell>
    </Row>
    <Row ref="i_0_0" inx="0" kind="i">
        <Cell ref="i_0_0_0">TP126</Cell>
        <Cell ref="i_0_0_1">22/04/2020</Cell>
        <Cell ref="i_0_0_2">Paul Obiero Owuor </Cell>
        <Cell ref="i_0_0_3">170.4 </Cell>
        <Cell ref="i_0_0_4">19.2 </Cell>
        <Cell ref="i_0_0_5">7.7 </Cell>
        <Cell ref="i_0_0_6">2.8 </Cell>
        <Cell ref="i_0_0_7">0.0 </Cell>
        <Cell ref="i_0_0_8">0.0 </Cell>
        <Cell ref="i_0_0_9">Garsen</Cell>
    </Row>
    <Row ref="i_0_0" inx="1" kind="i">
        <Cell ref="i_0_0_0">TP113</Cell>
        <Cell ref="i_0_0_1">22/04/2020</Cell>
        <Cell ref="i_0_0_2">SALIM SAID ABOUD</Cell>
        <Cell ref="i_0_0_3">104.9 </Cell>
        <Cell ref="i_0_0_4">16.1 </Cell>
        <Cell ref="i_0_0_5">4.1 </Cell>
        <Cell ref="i_0_0_6">2.8 </Cell>
        <Cell ref="i_0_0_7">34.7 </Cell>
        <Cell ref="i_0_0_8">0.0 </Cell>
        <Cell ref="i_0_0_9">Garsen</Cell>
    </Row>
</Rows>

我想完成什么

public void  FetchReport()
{
    var results =  reportServicebase.FetchReport(_ihandleID);
    List<RestHeavyConsumptionModel> heavylist = new List<RestHeavyConsumptionModel>();

    XElement doc = XElement.Parse(results.Result.ToString());

    XmlDocument doc1 = new XmlDocument();
    doc1.LoadXml(results.Result.ToString());

    if(ReportStatus == "Done")
    {
        RestHeavyConsumptionModel heavy = new RestHeavyConsumptionModel();

        heavy.VehicleName = doc1.GetElementsByTagName("cell").

    } 
}

调用soapclient代理时如何从xml生成结果,以便将其值(单元格)分配给报告对象。

【问题讨论】:

  • 我认为,第一行包含列的定义,因此预期结果将是 Kind i 的报告列表?

标签: xml linq soap-client


【解决方案1】:

你有两个问题:

  • 您的 XML 不是标准的,它包含某种标题作为第一行
  • Rows 不包含您实际需要的值。读取的数据需要转换成你的类Report

读取 XML 数据

我不确定您是否已经为您的非标准数据创建了 XML 阅读器。如果您将更频繁地读取此数据或进行其他转换,请考虑将您的解决方案分成几个子解决方案。

您有一些源数据,它表示 XML 格式的 Rows 序列。

  • 定义一个代表Row的类型
  • 获取原始数据:文件、流、文本阅读器、字符串。这是 XML 格式的
  • 将此原始数据转换为DataSet
  • DataSet 转换为IEnumerable&lt;Row&gt;
  • IEnumerable&lt;Row&gt; 转换为IEnumerable&lt;Report&gt;

.

class Row // TODO: invent a proper name, something like CarRideData?
{
    public string VehicleName {get; set;}
    public DateTime Date {get; set;}
    public string Driver {get; set;}
    ... // etc.
}

将您的 XML 源数据读入 DataSet

您没有指定确切的来源:它是一个文件吗?一个字符串?溪流?幸运的是,DataSet 类拥有所有这些输入的方法。见:Loading a DataSet from XML

string xmlFileName = ...
DataSet dataSet = new DataSet();
dataSet.ReadXml(xmlFileName);

或者如果你有一个流:

using (Stream stream = ...)
{
    dataSet.ReadXml(stream);
};

TextReader 或 String 类似

将数据集转换为 IEnumerable

我会将转换方法编写为扩展方法。这样,您可以将它们用作 LINQ 表达式的源。这样做的好处是,如果您的源发生更改,例如更改格式,或从 XML 更改为 CSV 或数据库,则其他过程不必更改。见extension methods demystified

public static IEnumerable<Row> ToRows(this DataSet source)
{
    // TODO: decide what tho do if source == null

    // in small steps, of course you can take bigger steps if you want:
    IListSource listSource = dataSet;
    if (listSource.ContainsListCollection)
    {
        // the XML contains a collection
        IList list = listSource.GetList();

        // the List is expected to contain a sequence of Rows.
        // if you are certain about this, just Enumerable.Cast:
        return list.Cast<Row>();

        // alternative, if you think that there might be other types in the list,
        // use Enumerable.OfType, this will ignore all other types and return only
        // the Rows
        return list.OfType<Row>();
    }
    else
    {
        // TODO: decide what to do if the source doesn't have the expected format
        // throw exception? return empty sequence?
        return IEnumerable.Empty<Row>();
    }
}

Nota Bene:这些行仍然包含您的标题!您只需 Skip(1) 即可获得正确的行集合。如果这将在更多位置使用,人们可能会忘记删除标题元素。如果您确定没有人需要标头,请考虑在您的方法中跳过它:

public static IEnumerable<Row> ToRows(this DataSet source)
{
    return source.GetList().Cast<Row>.Skip(1);
}

用法:

string xmlFileName = ...
DataSet dataSet = new DataSet();
dataSet.ReadXml(xmlFileName);
IEnumerable<Row> rows = dataSet.ToRows();

将行序列转换为报表序列

再次作为扩展方法:

public static IEnumerable<Report> ToReports(this IEnumerable<Row> rows)
{
    // TODO: decide what to do if rows == null

    return rows.Select(row => new Report
    {
        VehicleName = row.VehicleName,
        DriverName = row.DriverName,
    }
}

用法:

string xmlFileName = ...
DataSet dataSet = new DataSet();
dataSet.ReadXml(xmlFileName);
IEnumerable<Report> reports = dataSet.ToRows().ToReports();

可选改进

如果您会大量阅读 Rows 或 Reports,请考虑为这四行创建一个过程:

IEnumerable<Row> ReadRows(string xmlFileName)
{
    string xmlFileName = ...
    DataSet dataSet = new DataSet();
    dataSet.ReadXml(xmlFileName);
    return dataSet.ToRows();
}

IEnumerable<Report> ReadReports(string xmlFileName)
{
    return ReadRows(xmlFileName).ToReports();
}

用法:

string xmlFileName = ...
IEnumerable<Report> reports = ReadReports(xmlFileName);

总结

因为您将问题拆分为更小的转换,这些转换将高度可重用:如果您的源来自 CSV 文件或数据库,您仍然可以使用ToReport。如果您想要不同的目标类型,您仍然可以使用 ReadXML 和 ToRows。如果您的源不是文件名,而是流或字符串,则只需更改 ReadXml。

由于您创建了 IEnumerable 的扩展方法,因此可以与 LINQ 交织使用:

string xmlFileName = ...
DateTime today = DateTime.Today;
IEnumerable<Report> todaysReports = ReadRows(xmlFileName)
     .Where(row => row.Date == today)
     .ToReports();

【讨论】:

  • 非常感谢您的帮助,我已经能够解决这个问题,xml 不是标准的,因为它是由具有许多“报告”格式的报告生成器生成的。这个想法是遍历所有行元素并添加一个新的报表类。每个具有“i_0_0_x”属性的元素 代表报表类的一个新对象和属性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-22
  • 1970-01-01
相关资源
最近更新 更多