2013-02-15 23 views
0

我只是想了解Linq,我正在尝试做一些看起来很简单的事情,但我无法按照自己的意愿去输出结果。我一直坚持这个尝试各种不同方法的日子,我无法做到。使用Linq从列表构建XML <CustomClass>

所以我有一个类EarObs,它有成员:eventID,icaoId,frm,sta,db。

我想从列表中创建一个XML文档。我想在XML文档看起来像这样:

<EarObs EventId = "123456789"> 
    <icao icaoID = "0001"> 
     <frm frm = "01"> 
      <sta sta = "00"> 
       <db>87</db> 
       <hz>99</hz> 
      </sta> 
      <sta station = "01"> 
       <db>79</db> 
       <hz>99</hz> 
      </sta> 
     </frm> 
     <frm frm = "02"> 
     ................ 
     </frm> 
    </icao> 
</EarObs> 

,这将继续一路下跌保持相同的订单,如果有不止一个帧或多个代码等

所以这是我最近一直在尝试,但它仍然没有输出他们想要的方式,Obs重复,我不知道我错在哪里。

string eventGUID = "eventGUID"; 

List<EarObs> frameObsList = new List<EarObs>(); 
for (int frm = 2; frm > 0; frm--) 
{ 
    for (int sta = 5; sta > 0; sta--) 
    { 
     frameObsList.Add(new EarObs("KAPF", eventGUID, frm, sta, 85 + sta, 99 + sta)); 
     cnt++; 
    } 

} 
String eventID = obsList.First().EventGUID; 

List<EarObs> distinctApts = 
    obsList 
    .GroupBy(p => p.IcaoId) 
    .Select(g => g.First()) 
    .ToList(); 


XElement xElement = new XElement("EarObs", new XAttribute("eventID", eventID), 

    from ea in distinctApts 
    orderby ea.IcaoId 
    select new XElement("icao", new XAttribute("code", ea.IcaoId), 
     from eb in obsList 
     where ea.IcaoId == eb.IcaoId 
     orderby eb.Frm 
     select new XElement("frm", new XAttribute("frm", eb.Frm), 
      from ec in obsList     
      where eb.Frm == ec.Frm 
      orderby ec.Sta 
      select new XElement("sta", new XAttribute("sta", ec.Sta), 
       new XElement("db", ec.Db), 
       new XElement("hz", ec.Hz))))); 

使用此代码我得到一个XML文件,为每个站重复一次帧。这是不正确的。我觉得这很容易顺序完成,但我试图学习,这似乎很简单,我应该可以在Linq中完成。我需要列表中的每个元素只能在XML文档中表示一次。我如何去做这件事?

我也想扩展它,以便它可以处理多个eventId,但这不像获得XML结构正确那么重要。任何帮助将不胜感激,我还没有找到太多创建XML的例子,包括使用linq过滤元素,大多数例子似乎都在创建XML之前将所有的列表都准备好了。

+0

它可能不是“正确的”,但它完全按照指示进行 - 您在循环播放站时为每次迭代添加一个对象。您可能想要的是将电台添加到本地收藏,然后添加一个包含电台收藏的帧。 – GalacticCowboy 2013-02-15 20:07:54

+0

那你是说我应该在选择的sta元素行下添加另一条语句吗?您能否通过将电台添加到本地收藏中来详细说明您的意思? – Ben 2013-02-15 20:17:09

+0

比这更早,当您构建frameObsList时,您为每个站添加一个。 – GalacticCowboy 2013-02-15 20:27:47

回答

1

由于您有自定义类,EarObs为什么不为您的对象定义Xml属性并使用XmlSerlizer类序列化对象?这样,您可以继续在您的对象上使用Linq,并输出您的对象。

例如下面是一个团队,其上有玩家。

[XmlRoot("root")] 
public class Team 
{ 
    private List<Player> players = new List<Player>(); 

    [XmlElement("player")] 
    public List<Player> Players { get { return this.players; } set { this.players = value; } } 

    // serializer requires a parameterless constructor class 
    public Team() { } 
} 

public class Player 
{ 
    private List<int> verticalLeaps = new List<int>(); 

    [XmlElement] 
    public string FirstName { get; set; } 
    [XmlElement] 
    public string LastName { get; set; } 
    [XmlElement] 
    public List<int> vertLeap { get { return this.verticalLeaps; } set { this.verticalLeaps = value; } } 

    // serializer requires a parameterless constructor class 
    public Player() { } 
} 

一旦我创建了一个团队,它的一些球员,我只需要做:

Team myTeamData = new Team(); 
// add some players on it. 
XmlSerializer deserializer = new XmlSerializer(typeof(Team)); 
using (TextReader textReader = new StreamReader(@"C:\temp\temp.txt")) 
{ 
    myTeamData = (Team)deserializer.Deserialize(textReader); 
    textReader.Close(); 
} 

输出将是这样的:

<?xml version="1.0" encoding="utf-8"?> 
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <player> 
    <FirstName>dwight</FirstName> 
    <LastName>howard</LastName> 
    <vertLeap>1</vertLeap> 
    <vertLeap>2</vertLeap> 
    <vertLeap>3</vertLeap> 
    </player> 
    <player> 
    <FirstName>dwight</FirstName> 
    <LastName>howard</LastName> 
    <vertLeap>1</vertLeap> 
    </player> 
</root> 
+0

我会更愿意这样做,但我怎样才能在原始文章中获得结构化的表单,而无需为工作站,框架和icao创建单独的类?我可以看到,如果我有一个名为sta的类,其中包含两个XMLElements db和hz,然后是一个具有sta列表的类框架,然后是一个拥有框架列表的icao类,我可以轻松地这样做,但是这看起来像是很多不必要的抽象。 – Ben 2013-02-15 20:25:14

+0

您可以使用像我上面提到的XmlElement这样的修饰符作为[XmlElement(“nameyourelementhere”)]或[XmlAttribute(“attributename”)]使其看起来与您提到的XML输入(或输出)文件完全相同。有时候,你可能不得不使用包装器,所以如果它不是这样设计的,那么制作它可能是一项乏味的任务。 – Magnum 2013-02-18 17:39:39

0

最简单的方法是创建一组类来处理序列化,就像这样;

public class sta 
{ 
    public int db { get; set; } 
    public int hz { get; set; } 

    [XmlAttribute()] 
    public string station { get; set; } 
} 

public class frm 
{ 
    [XmlAttribute("frm")] 
    public string frmID { get; set; } 

    [XmlElement("sta")] 
    public List<sta> stas { get; set; } 
} 

public class icao 
{ 
    [XmlAttribute] 
    public string icaoID { get; set; } 

    [XmlElement("frm")] 
    public List<frm> frms { get; set; } 
} 

public class EarObs 
{ 
    [XmlAttribute] 
    public string EventId { get; set; } 

    [XmlElement("icao")] 
    public List<icao> icaos { get; set; } 
} 

并且您可以使用xml序列化器来序列化/反序列化。以下序列化为与您拥有的结构相同的结构;

XmlSerializer serializer = new XmlSerializer(typeof(EarObs)); 
EarObs obs = new EarObs() { EventId = "123456789" }; 
obs.icaos = new List<icao>(); 
obs.icaos.Add(new icao() { icaoID = "0001" }); 
obs.icaos[0].frms = new List<frm>(); 
obs.icaos[0].frms.Add(new frm() { frmID = "01" }); 
obs.icaos[0].frms[0].stas = new List<sta>(); 
obs.icaos[0].frms[0].stas.Add(new sta() { station = "00", db = 87, hz = 99 }); 
obs.icaos[0].frms[0].stas.Add(new sta() { station = "01", db = 79, hz = 99 }); 
obs.icaos[0].frms.Add(new frm() { frmID = "02" }); 

using (StringWriter s = new StringWriter()) 
{ 
    serializer.Serialize(s, obs); 
    string test = s.ToString(); 
} 

输出;现在

<?xml version="1.0" encoding="utf-16"?> 
<EarObs xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" EventId="123456789"> 
    <icao icaoID="0001"> 
    <frm frm="01"> 
     <sta station="00"> 
     <db>87</db> 
     <hz>99</hz> 
     </sta> 
     <sta station="01"> 
     <db>79</db> 
     <hz>99</hz> 
     </sta> 
    </frm> 
    <frm frm="02" /> 
    </icao> 
</EarObs> 

,而这似乎是一个很麻烦去,有可能使用xsd.exe工具(自带的框架,我相信),自动创建一组类,匹配任何给定的xml文件,虽然它确实使用了中介xsd文件(尽管如此)。你可以在这里找到如何; How to generate .NET 4.0 classes from xsd?