How do I manipulate an XML document one parent ele

2020-04-23 12:27发布

问题:

I am trying to take an XML file containing multiple orders from an online shopping cart, parse it and output the values of each order as its own text file (not XML) using C# and Visual Studio 2008. I have tried a variety of methods at this point, but have had no luck. My last attempted included a foreach statement tied to XMLNodeList and trying to execute my translation for each node with the name "Order" by using XMLReader to write each elements value to a string. The foreach seems to be what is not working with the current configuration.

Is there a better way to do this or do I need to keep using a foreach? All thoughts are greatly appreciated.

 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");
}
}

}

回答1:

You can make this super simple. Object oriented programming is the way to go.

So say you create a class Order that takes an XElement. I'll write a couple lines for you.

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"); }
    }
}

You'll have to fill in the rest of the Order's properties yourself. For each Order value, make a property like the OrderNumber I made above. Having properties for each Order value makes accessing the data super simple.

So for your main code you'd have:

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

Now that you have all your orders as individual Order objects, read the data from the list of properties as you output to the file. There's no need now to store the values in StringBuilder's because the values are in the Order objects.

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


回答2:

Chuck has a good approach. Sometimes, you don't want to retain the reference to the underlying xml data structure in the model object you are loading. In this case, I often use a pattern like this:

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();
    }
}

Read can be overloaded to accept XmlDocument, XmlNode, XElement, etc.

Now you can implement specific models.

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
    }

}

With the XPathDocument and XPathNavigator approach, you can then do something like this:

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
}