【问题标题】:How do I manipulate an XML document one parent element at a time?如何一次操作一个 XML 文档的一个父元素?
【发布时间】:2012-02-09 00:46:17
【问题描述】:

我正在尝试从在线购物车中获取一个包含多个订单的 XML 文件,对其进行解析并使用 C# 和 Visual Studio 2008 将每个订单的值作为其自己的文本文件(不是 XML)输出。我尝试了多种在这一点上的方法,但没有运气。我的最后一次尝试包括与 XMLNodeList 绑定的 foreach 语句,并尝试通过使用 XMLReader 将每个元素值写入字符串来为每个名为“Order”的节点执行我的翻译。 foreach 似乎不适用于当前配置。

有没有更好的方法来做到这一点,还是我需要继续使用 foreach?所有的想法都非常感谢。

 class Class1
  {
     public static void Main()
      {

        StringBuilder orderid = new StringBuilder();
        StringBuilder ordernumber = new StringBuilder();
        StringBuilder name = new StringBuilder();
        StringBuilder staddress = new StringBuilder();
        StringBuilder city = new StringBuilder();
        StringBuilder state = new StringBuilder();
        StringBuilder zip = new StringBuilder();
        StringBuilder country = new StringBuilder();
        StringBuilder email = new StringBuilder();
        StringBuilder partnumber = new StringBuilder();
        StringBuilder quantity = new StringBuilder();

            XmlDocument doc = new XmlDocument();
            doc.Load(@"C:\onlinesales\neworders.xml");
            XmlNode root = doc.DocumentElement;

            XmlNodeList nodeList = root.SelectNodes("Order");
            foreach (XmlNode order in nodeList)
            {

                using (XmlReader reader = XmlReader.Create("Order"))
                {
                    reader.ReadToFollowing("OrderNumber");
                    ordernumber.Append(reader.ReadElementContentAsString());

                    reader.ReadToFollowing("OrderGUID");
                    orderid.Append(reader.ReadElementContentAsString());

                    reader.ReadToFollowing("FirstName");
                    name.Append(reader.ReadElementContentAsString());

                    reader.ReadToFollowing("Email");
                    email.Append(reader.ReadElementContentAsString());

                    reader.ReadToFollowing("BillingAddress1");
                    staddress.Append(reader.ReadElementContentAsString());

                    reader.ReadToFollowing("BillingCity");
                    city.Append(reader.ReadElementContentAsString());

                    reader.ReadToFollowing("BillingState");
                    state.Append(reader.ReadElementContentAsString());

                    reader.ReadToFollowing("BillingZip");
                    zip.Append(reader.ReadElementContentAsString());

                    reader.ReadToFollowing("BillingCountry");
                    country.Append(reader.ReadElementContentAsString());

                    reader.ReadToFollowing("Quantity");
                    quantity.Append(reader.ReadElementContentAsString());

                    reader.ReadToFollowing("OrderedProductManufacturerPartNumber");
                    partnumber.Append(reader.ReadElementContentAsString());
                }

            using (StreamWriter fileout =
                new StreamWriter("W:" + DateTime.Now.ToString("yyyyy-MM-dd_hh-mm-ss-ff") + ".txt", false, Encoding.ASCII))
            {
                fileout.WriteLine("ISA*00*          *00*          *ZZ*daisywebstore  *12*5016361200     *" + DateTime.Now.ToString("yyMMdd") + "*1559*U*00400*000001649*0*P>~");
                fileout.WriteLine("GS*PO*daisywebstore*5016361200*" + DateTime.Now.ToString("yyyyMMdd") + "*" + DateTime.Now.ToString("HHmm") + "*1649*X*004010~");
                fileout.WriteLine("ST*850*13~");
                fileout.WriteLine("BEG*00*SA*08272226001*" + DateTime.Now.ToString("yyyyMMdd") + "~");
                fileout.WriteLine("REF*DP*089~");
                fileout.WriteLine("DTM*002*20120104~");
                fileout.WriteLine("N1*ST*" + name + "~");
                fileout.WriteLine("N3*" + staddress + "~");
                fileout.WriteLine("N4*" + city + "*" + state + "*" + zip + "~");
                fileout.WriteLine("N1*RE**92*00103653341~");
                fileout.WriteLine("PO1*1*6*EA*33.28*TE*IN*985880-542~");
                fileout.WriteLine("PID*F*****CO2 BB PISTOL     $ 5693~");
                fileout.WriteLine("PO4*3*1*EA~");
                fileout.WriteLine("CT*1~");
                fileout.WriteLine("AMT*1*199.68~");
                fileout.WriteLine("SE*16*13~");
            }
                    }
                //File.Delete(@"C:\onlinesales\neworders.xml");
}
}

}

【问题讨论】:

  • xml文档有命名空间吗?

标签: c# xml visual-studio-2008 foreach


【解决方案1】:

Chuck 有一个很好的方法。有时,您不想在正在加载的模型对象中保留对底层 xml 数据结构的引用。在这种情况下,我经常使用这样的模式:

public interface IXmlReadable
{
    void Clear();
    void Read(XPathNavigator xmlNav);
}

public class ModelBase : IXmlReadable
{
    public void Clear()
    {
        DoClear();
    }

    public void Read(XPathNavigator xmlNav)
    {
        DoRead(xmlNav);
    }


    protected virtual void DoClear()
    {
        throw new NotImplementedException();
    }

    protected virtual void DoRead(XPathNavigator xmlNav)
    {
        throw new NotImplementedException();
    }
}

读取可以重载以接受XmlDocumentXmlNodeXElement等。

现在您可以实现特定模型。

public sealed class Order : ModelBase
{
    public Order() { }

    public string OrderNumber { get; private set; }

    protected override void DoClear()
    {
        OrderNumber = string.Empty;
    }

    protected override void DoRead(XPathNavigator xmlNav)
    {
        DoClear();

        XPathNavigator node;

        node = xmlNav.SelectSingleNode("OrderNumber");

        if (node != null)
            OrderNumber = node.InnerXml;

        // implement other properties here
    }

}

使用 XPathDocument 和 XPathNavigator 方法,您可以执行以下操作:

XPathDocument xml = new XPathDocument(@"C:\onlinesales\neworders.xml");

xmlNav = xml.CreateNavigator();

XPathNodeIterator iterator = xmlNav.Select("Order");

while (iterator.MoveNext())
{
    Order order = new Order();
    order.Read(iterator.Current);

    // do something with the Order - add to list or process
}

【讨论】:

  • 有趣。最后一个代码段中是否有错字 - 应该是 order.Read() 而不是 Order.Read()
  • 糟糕!是的,错字。您在实例上调用 read 来完成读取工作。我已经更正了代码。
【解决方案2】:

你可以让这个超级简单。面向对象编程是必经之路。

假设您创建了一个接受 XElement 的 Order 类。我给你写几行。

public class Order
{
    XElement self;
    public Order(XElement order)
    {
        self = order;
    }

    public XElement Element { get { return self; } }

    public string OrderNumber 
    { 
        // if your xml looks like <Order OrderNumber="somenumber" />
        get { return (string)(self.Attribute("OrderNumber") ?? (object)"some default value/null"); } 
        // but if it looks like: <Order><OrderNumber>somenumber</OrderNumber></Order>
        // get { return (string)(self.Element("OrderNumber") ?? (object)"some default value/null"); }
    }
}

您必须自己填写订单的其余属性。对于每个 Order 值,创建一个类似于我在上面创建的 OrderNumber 的属性。拥有每个 Order 值的属性使得访问数据变得超级简单。

因此,对于您的主要代码,您将拥有:

XElement file = XElement.Load(@"C:\onlinesales\neworders.xml");
Order[] orders = file.Elements("Order").Select(e => new Order(e)).ToArray();

现在您已将所有订单作为单独的 Order 对象,在您输出到文件时从属性列表中读取数据。现在不需要将值存储在 StringBuilder 中,因为值在 Order 对象中。

foreach(Order order in orders)
{
   // write order.OrderNumber  etc. / do whatever you want with the orders.
}

【讨论】:

  • 我相信我从概念上理解您的建议并正在尝试实施它,但从 OrderNumber get 语句中得到一个错误。它在告诉我??不能应用于 XAttribute 和 String。我可能遗漏了一些非常明显的东西,但我坚持下去。你知道我为什么会收到这个错误吗?
  • 我修正了代码:把(object)放在你的默认值前面,只要可以将默认值强制转换为字符串,它应该可以工作。 ?? 是一个空替换运算符。因此,如果第一个值为 null,则返回表达式的第二部分。因此,编译器希望这两个值都是相同的基本类型。
  • 确实解决了它。对于将属性分配给我的类的 get 语句,我不需要相应的集合来将它们设置为类吗?我在您建议的方法中添加了其他属性,将 OrderNumber 之后的所有内容都替换为 Attribute 。
  • 不,您不需要设置,因为值应该已经在创建类的 xml 中。您所做的就是将读取语句变成属性。当您编译并运行它时,在语句self = order 处放置一个调试停止,然后将鼠标悬停在订单上以查看它确实已经包含所有订单信息。
  • 你可以用另一种方式检查。添加一个返回self的属性Element,我将在上面进行更改。然后您可以在 foreach 或 orders[0].Element 中检查元素
猜你喜欢
  • 1970-01-01
  • 2012-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多