有没有一种方法可以一次性替换整个XML节点?

4

我需要为指定用户替换产品列表(UserCart)为更新后的产品列表。我如何在不调用每个属性的情况下完成这个操作?

<Users>
<UserInfo>
  <Name>ddd</Name>
  <Wallet>0</Wallet>
  <UserCart>
    <Products_>
      <MedicineProduct
        Product_Name="sak"
        Product_ID="0"
        Price="0"
        Quntity="0"
        Image="" />
    </Products_>
  </UserCart>
</UserInfo>

这就是我卡住的地方...

public static void Edit(UserInfo user, Products usercart)
{
    XmlDocument doc = new XmlDocument();
    doc.Load(path);
    XmlNode node = doc.SelectSingleNode(string.Format("//UserInfo[./Name/text()='{0}']", user.Name));
}

发现指定用户。如何用新值替换整个UserCart节点?
2个回答

2

您可以尝试使用较新的 XDocument API 来处理 XML,以及其 XElement.ReplaceWith() 方法。

var xml = "<Users><UserInfo><Name>Old Name</Name></UserInfo></Users>";
var document = XDocument.Parse(xml);
var replacedNode = document.Descendants("UserInfo")
                           .SingleOrDefault(x => x.Descendants("Name")
                                                  .Single()
                                                  .Value == "Old Name");

// The code below uses C# 6.0 null propagation feature to handle 
// the case when replacedNode is null (not found in XML).
// In case you use C# 5.x or lower, you can just check it in an IF statement
replacedNode?.ReplaceWith(new XElement("UserInfo", 
                                       new XElement("Name", "New Name")));

Console.WriteLine(document.ToString());

更新(01/14/2016)

以下是一个快速示例,展示如何编写扩展方法来替换用户的购物车。虽然这确实是可能的,但我建议避免将业务逻辑(用户购物车更新)和基础设施/数据层(序列化)混合在一起。我更希望看到用户对象从XML反序列化,更新后再次序列化。

public static class XmlExtensions
{
    public static XElement ToXElement(this object obj)
    {
        using (var memoryStream = new MemoryStream())
        {
            using (TextWriter streamWriter = new StreamWriter(memoryStream))
            {
                var xmlSerializer = new XmlSerializer(obj.GetType());
                xmlSerializer.Serialize(streamWriter, obj);
                return XElement.Parse(Encoding.ASCII.GetString(memoryStream.ToArray()));
            }
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var xml = "<Users><UserInfo><Name>Old Name</Name><UserCart></UserCart></UserInfo></Users>";

        var document = XDocument.Parse(xml);

        var userCart = document.Descendants("UserInfo")
            .SingleOrDefault(x => x.Descendants("Name").Single().Value == "Old Name")
            ?.Element("UserCart");

        var newUserCart = new UserCart
        {
            Products = new List<Product>
            {
                new Product { Name = "First" },
                new Product { Name = "Second" }
            }
        };

        userCart?.ReplaceWith(newUserCart.ToXElement());

        Console.WriteLine(document.ToString());
    }
}

public class UserCart
{
    public List<Product> Products { get; set; } 
}

public class Product
{
    public string Name { get; set; }
    public int Quantity { get; set; }
}

如我在评论中提到的,您也可以使用System.Xml命名空间中的属性(例如XmlElement)来控制序列化。请注意,XmlSerializer有其限制。例如,它无法直接序列化接口。


1
请注意,?.(空值条件运算符)是C# 6.0的一部分,因此如果OP使用的是VS 2013或更早版本,则该运算符将不可用。 - Tim
你是正确的。如果你使用的是C# 5.x或更低版本,你可以直接检查replacedNode是否为null。已更新答案。 - Nikolai Samteladze
1
我想用新值替换整个UserCart,比如用<Products_> <MedicineProduct Product_Name="new" Product_ID="1" Price="1" Quntity="1" Image="" /> </Products_>。是否可以通过简单地传递UserCart对象 - new XElement("UserCart", usercart))来实现?还是我需要设置UserCart的每个属性的值,例如Product_Name = "New"等? - Mrg Gek
我认为如果你将你的对象(userCart)作为 content 传递给 XElement 构造函数,那么它只会调用它的 ToString() 方法。我会选择在这里描述的选项。即创建使用 XmlSerializer 序列化对象的扩展方法。在这种情况下,您还可以使用 System.Xml 命名空间中的属性(XmlElement 等)来控制序列化。 - Nikolai Samteladze

0

这有点取决于你想用什么来替换它。如果你只是想在里面设置一些文本,那就很简单:

XmlNode cart = doc.SelectSingleNode(string.Format("//UserInfo[./Name/text()='{0}']/UserCart", "ddd"));
cart.InnerText = "Replaced!";

请注意,我更改了您的选择器,以便获取实际的购物车节点而不是其父节点。
如果您想用新的 XML 节点替换它,您需要创建这些节点,将它们添加到文档中并将它们附加到相应位置。
XmlNode cart = doc.SelectSingleNode(string.Format("//UserInfo[./Name/text()='{0}']/UserCart", "ddd"));
 // clear out nodes
cart.RemoveAll();
cart.AppendChild(doc.CreateNode(XmlNodeType.Element, "test", ""));

总的来说,最好使用较新的XDocument API,可能看起来像...

XDocument doc = XDocument.Parse(xml);
//doc.LoadXml(xml);

XElement cart = doc.Root.Descendants("UserCart").Where(x => (string)x.Parent.Element("Name") == "ddd").FirstOrDefault();
if (cart != null)
    cart.ReplaceNodes(new XElement("test")); // or you can do cart.Value = "text" you're not adding new elements.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接