C#XML数据避免化 - 基于参考ID应用对象关系



我早些时候问了一个问题,我得到了使用XML避难所化的提示将我的XML内容解析为C#对象。经过一番谷歌搜索和乱七八糟的作用,我得到了一个工作的挑战,但是我有一个问题。

我的XML文件看起来像这样:(这只是文件的一部分)

   <osm>
      <n id="2638006578" l="5.9295547" b="52.5619519" />
      <n id="2638006579" l="5.9301973" b="52.5619526" />
      <n id="2638006581" l="5.9303625" b="52.5619565" />
      <n id="2638006583" l="5.9389539" b="52.5619577" />
      <n id="2638006589" l="5.9386643" b="52.5619733" />
      <n id="2638006590" l="5.9296231" b="52.5619760" />
      <n id="2638006595" l="5.9358987" b="52.5619864" />
      <n id="2638006596" l="5.9335913" b="52.5619865" />
      <w id="453071384">
        <nd rf="2638006581" />
        <nd rf="2638006590" />
        <nd rf="2638006596" />
        <nd rf="2638006583" />
        <nd rf="2638006578" />
      </w>
      <w id="453071385">
        <nd rf="2638006596" />
        <nd rf="2638006578" />
        <nd rf="2638006581" />
        <nd rf="2638006583" />
      </w>
   </osm>

我设法将其列为对象,并且它的工作原理。问题如下:<w>元素下的<nd>元素具有参考ID,该参考ID与<n>元素的ID相同。多个<w>元素可能具有相同的<n>元素参考,因此<n>元素。

当前代码我有一个表示<nd>元素的NodeReference对象,但是我想根据参考ID和Node ID将类别类直接链接到节点类。因此,基本上,类的方式应具有节点列表,而不是一个节点列表。我应该有一个分开的节点列表,以防止使用相同数据的新实例的方法(例如,如果两种方式参考相同的节点,则他们也应该指向相同的节点实例,而不是两个相同的节点实例,如果那样有意义..)

我基本上需要基于Noderference ID访问Node实例的LON/LAT/ID字段。

这是我的代码:

datacollection类

[XmlRoot("osm")]
    public class DataCollection {
        [XmlElement("n")]
        public List<Node> Nodes { get; private set; }
        [XmlElement("w")]
        public List<Way> Ways { get; private set; }
        public DataCollection() {
            this.Nodes = new List<Node>();
            this.Ways = new List<Way>();
        }
    }

节点类

[Serializable()]
public class Node {
    [XmlAttribute("id", DataType = "long")]
    public long ID { get; set; }
    [XmlAttribute("w", DataType = "double")]
    public double Lat { get; set; }
    [XmlAttribute("l", DataType = "double")]
    public double Lon { get; set; }
}

方法

[Serializable()]
    public class Way {
        [XmlAttribute("id", DataType = "long")]
        public long ID { get; set; }
        [XmlElement("nd")]
        public List<NodeReference> References { get; private set; }
        public Way() {
            this.References = new List<NodeReference>();
        }
    }

nodeReference

[Serializable()]
    public class NodeReference {
        [XmlAttribute("rf", DataType = "long")]
        public long ReferenceID { get; set; }
    }

阅读XML文件

public static void Read() {
        XmlSerializer serializer = new XmlSerializer(typeof(DataCollection));
        using (FileStream fileStream = new FileStream(@"path/to/file.xml", FileMode.Open)) {
            DataCollection result = (DataCollection)serializer.Deserialize(fileStream);
            // Example Requested usage: result.Ways[0].Nodes 
        }
        Console.Write("");
    }

预先感谢!如果您有问题或答案,请告诉我!

我同意@AlexanderPetrov,最容易链接对象之后。我正在使用词典进行快速查找,并在NodeReferece类中使用额外的Node属性。

var NodeById = this.Nodes.ToDictionary(n => n.ID, n => n);
foreach (var way in this.Ways) {
    foreach (var nd in way.References) {
        nd.Node = NodeById[nd.ReferenceID];
    }
}

通过查询属性导入System.Xml.Serialization名称空间后,可以在LINQPAD中运行以下代码。

void Main()
{
    XmlSerializer serializer = new XmlSerializer(typeof(DataCollection));
    using (FileStream fileStream = new FileStream(@"file.xml", FileMode.Open)) {
        DataCollection result = (DataCollection)serializer.Deserialize(fileStream);
        result.Index();
        result.Ways[0].References[0].Node.Lon.Dump();
        // -> 5,9303625
    }
}

// --------------------------------------------------------------------------
[XmlRoot("osm")]
public class DataCollection {
    [XmlElement("n")]
    public List<Node> Nodes = new List<Node>();
    [XmlElement("w")]
    public List<Way> Ways = new List<Way>();
    public void Index() {
        var NodeById = this.Nodes.ToDictionary(n => n.ID, n => n);
        foreach (var way in this.Ways) {
            foreach (var nd in way.References) {
                nd.Node = NodeById[nd.ReferenceID];
            }
        }
    }
}
// --------------------------------------------------------------------------
[Serializable()]
public class Node {
    [XmlAttribute("id", DataType = "long")]
    public long ID { get; set; }
    [XmlAttribute("w", DataType = "double")]
    public double Lat { get; set; }
    [XmlAttribute("l", DataType = "double")]
    public double Lon { get; set; }
}
// --------------------------------------------------------------------------
[Serializable()]
public class Way {
   [XmlAttribute("id", DataType = "long")]
   public long ID { get; set; }
   [XmlElement("nd")]
   public List<NodeReference> References = new List<NodeReference>();
}
// --------------------------------------------------------------------------
[Serializable()]
public class NodeReference {
    [XmlAttribute("rf", DataType = "long")]
    public long ReferenceID { get; set; }
    [XmlIgnore]
    public Node Node { get; set; }
}

我猜我不可能使用标准XmlSerializer进行。

您可以通过实现IXmlSerializable接口或使用自定义XmlReader

来尝试执行此操作。

但是,最简单的方法是在避免后用以下代码手动填充所需的收集:

DataCollection result = (DataCollection)serializer.Deserialize(fileStream);
foreach (var way in result.Ways)
    foreach (var nodeReference in way.References)
        way.Nodes.Add(result.Nodes.First(node => node.ID == nodeReference.ReferenceID));

Nodes属性添加到Way类:

public class Way
{
    [XmlAttribute("id", DataType = "long")]
    public long ID { get; set; }
    [XmlElement("nd")]
    public List<NodeReference> References { get; private set; }
    public List<Node> Nodes { get; set; }
    public Way()
    {
        this.References = new List<NodeReference>();
    }
}

最新更新