简体   繁体   English

从xml检索数据(格式错误?)C#

[英]Retrieve data from xml (malformed?) c#

i'm at beginning both with xml and both with C# and linq. 我从xml和C#和linq开始。

I've this kind of xml 我有这种xml

<allegati tot_ele="2">
<record>
<dato nome="IdUnivoco">35516</dato>
<dato nome="Nome">QUOTAZIONE</dato>
<dato nome="RelationID">1268</dato>
<dato nome="nomeFile">1268.pdf</dato>
</record>
<record>
<dato nome="IdUnivoco">35516</dato>
<dato nome="Nome">CONFERMA D`ORDINE</dato>
<dato nome="RelationID">1267</dato>
<dato nome="nomeFile">1267.pdf</dato>
</record>
</allegati>

who created it use the same element name and use attributes to specifify the name of value 创建它的人使用相同的元素名称,并使用属性指定值的名称

is there a simple way to create a struct for each record and with linq create a list of struct ? 有没有一种简单的方法来为每个记录创建一个结构,并使用linq创建一个结构列表?

at moment i managed to create 4 different list of strings ( i post only 2 and not all 4) 目前,我设法创建了4个不同的字符串列表(我只发布2个,而不是全部4个)

List<string> NomeFile1 = (from c in listaAttch.Descendants("dato")
                          where c.Attribute("nome").Value == "nomeFile"
                          select c.Value).ToList();

List<string> Relation = (from c in listaAttch.Descendants("dato")
                          where c.Attribute("nome").Value == "RelationID"
                          select c.Value).ToList();

then with a for i will use the 4 strings for my needs 然后用for我将使用4个字符串满足我的需求

for (int i = 0; i < Relation.Count; i++)
 {
  byte[] file = Adiuto.getAttachContent1(login, 35516, Int32.Parse(Relation [i]));
  System.IO.File.WriteAllBytes("c:\\navision\\" + NomeFile1[i], file);
.
.
.
 }

is there a quicker and more elegant way to do it ? 有更快,更优雅的方法吗? without the need to repeat the linq 4 times? 无需重复linq 4次? is it ok also to create a list of struct with all the 4 attributes value 还可以用所有4个属性值创建结构列表吗

Many thanks in advance 提前谢谢了

Fabrizio 法布里奇奥

You can extract the query to a dedicated method and inject the different values as arguments: 您可以将查询提取到专用方法,然后将不同的值作为参数注入:

private List<string> GetDescendantsValues(SomeType source, string attributeValue)
{
    return source.Descendants("dato")
        .Where(c => c.Attribute("nome").Value == attributeValue)
        .Select(c => c.Value).ToList();
}

Usage: 用法:

List<string> NomeFile1 = GetDescendantsValues(listaAttch, "nomeFile");
List<string> Relation = GetDescendantsValues(listaAttch, "RelationID");

You can of course inject the other strings as well if you need to, I've done that just for 1 as that's the only one that is different in your presented code. 当然,您也可以根据需要注入其他字符串,我只为1完成了此操作,因为这是与您提供的代码唯一不同的一个。

Yes, you can de-serialise a non standard xml string using XmlNodeReader . 是的,您可以使用XmlNodeReader反序列化非标准xml字符串。 In one of my projects I did the same thing. 在我的一个项目中,我做了同样的事情。

The extension method to convert arbitrary xml string - 转换任意xml字符串的扩展方法-

public static class XmlSerializerExtensions
{
    public static T DeserializeFromNonStandardXmlString<T>(string content)
    {
        var xmlDoc = new XmlDocument();
        xmlDoc.LoadXml(content);

        var serializer = typeof(T).IsGenericType ? new XmlSerializer(typeof(T), typeof(T).GenericTypeArguments) : new XmlSerializer(typeof(T));
        using (var reader = new XmlNodeReader(xmlDoc))
        {
            return (T)serializer.Deserialize(reader);
        }
    }

    public static object DeserializeFromNonStandardXmlString(Type type, string content)
    {
        var xmlDoc = new XmlDocument();
        xmlDoc.LoadXml(content);

        var serializer = type.IsGenericType ? new XmlSerializer(type, type.GenericTypeArguments) : new XmlSerializer(type);
        using (var reader = new XmlNodeReader(xmlDoc))
        {
            return serializer.Deserialize(reader);
        }
    }
}

Then generate the classes needed for deserialising. 然后生成反序列化所需的类。 In your case it is something similar to - 就您而言,它类似于-

    [XmlRoot(ElementName = "dato")]
    public class Dato
    {
        [XmlAttribute(AttributeName = "nome")]
        public string Nome { get; set; }
        [XmlText]
        public string Text { get; set; }
    }

    [XmlRoot(ElementName = "record")]
    public class Record
    {
        [XmlElement(ElementName = "dato")]
        public List<Dato> Dato { get; set; }
    }

    [XmlRoot(ElementName = "allegati")]
    public class Allegati
    {
        [XmlElement(ElementName = "record")]
        public List<Record> Record { get; set; }

        [XmlAttribute(AttributeName = "tot_ele")]
        public string Tot_ele { get; set; }
    } 

Then you can serialise this way - 然后,您可以通过这种方式序列化-

var dt = XmlSerializerExtensions.DeserializeFromNonStandardXmlString<Allegati>(text);

And then get list of items as - 然后获取项目列表为-

var NomeFile1 = dt.Record.Select(x => x.Dato.FirstOrDefault(d => d.Nome == "nomeFile")?.Text).Select(x => x != null).ToList();
var Relation = dt.Record.Select(x => x.Dato.FirstOrDefault(d => d.Nome == "RelationID")?.Text).Select(x => x != null).ToList();

You can reduce this code as well with a method - 您也可以使用一种方法来减少此代码-

    [XmlRoot(ElementName = "allegati")]
    public class Allegati
    {
        ...

        public List<string> GetItemsWithKey(string key)
        {
            return Record.Select(x => x.Dato.FirstOrDefault(d => d.Nome == key)?.Text).ToList();
        }
    }



    var dt = XmlSerializerExtensions.DeserializeFromNonStandardXmlString<Allegati>(text);
    var a = dt.GetItemsWithKey("nomeFile");
    var b = dt.GetItemsWithKey("RelationID");

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

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