【问题标题】:Is there a way to replace whole XML node at once?有没有办法一次替换整个 XML 节点?
【发布时间】:2016-04-20 00:39:38
【问题描述】:

我需要将产品列表 (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 节点?

【问题讨论】:

    标签: c# xml linq-to-xml


    【解决方案1】:

    您可以尝试使用较新的 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());
    

    更新(2016 年 1 月 14 日)

    以下是有关如何编写扩展方法来替换用户购物车的快速示例。虽然这绝对可以做到,但我会避免像这样混合业务逻辑(用户购物车更新)和基础设施/数据层(序列化)。我宁愿看到用户对象从 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; }
    }
    

    正如我在 cmets 中提到的,您还可以使用来自 System.Xml 命名空间的属性(例如 XmlElement)来控制序列化。请注意,XmlSerializer 有其局限性。例如,它不能序列化开箱即用的接口。

    【讨论】:

    • 请注意,?.(Null 条件运算符)是 C# 6.0 的一部分,因此如果 OP 使用的是 VS 2013 或更早版本,则该运算符将不可用。
    • 你是对的。如果您使用 C# 5.x 或更低版本,则只需检查 replacedNode 是否为 null。更新了答案。
    • 我想用新值替换整个 UserCart 比如说 有没有办法通过简单地传递 UserCart 对象来做到这一点 - new XElement("UserCart", usercart)))?或者我需要为 UserCart 的每个属性设置值,例如 Product_Name = "New" 等?
    • 我想如果你将你的对象 (userCart) 作为content 传递给XElement 构造函数,那么它只会调用ToString()。我会选择here 描述的选项。那就是创建使用XmlSerializer 序列化对象的扩展方法。在这种情况下,您还可以使用来自System.Xml 命名空间(XmlElement 等)的属性来控制序列化。
    【解决方案2】:

    这在一定程度上取决于您要替换它的内容。如果您只想在其中设置一些文本,这很简单:

    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.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-25
      • 2011-03-26
      • 1970-01-01
      • 2022-01-04
      • 1970-01-01
      • 2011-02-01
      • 1970-01-01
      相关资源
      最近更新 更多