c# - 如何更改新 XML 文档中属性的值?

标签 c# .net xml xmldocument

我有一个数据库,其中有大量等待更新的 XML 文件。更新将删除数据库中的所有文件。除了属性值之外,每个新文件都将与他之前的文件相同。

但我想保留每个属性 type 具有 session 作为值。该属性多次出现在每个文件中。但是,它暂时不起作用:属性根本不更新)。

How can I change the value of an specific attribute in an new XML file identical depending of another one?

这就是我到目前为止所做的......

所以我决定找到一种方法,在删除之前为每个文件使用此代码来获取这些属性的路径:

List<XmlList> dBPathTypeSession = new List<XmlList>();
/*reader*/
XmlDocument doc = new XmlDocument();
doc.LoadXml(sessionType.Parameters);

XmlNode root = doc.DocumentElement;

//I think my problem come from this line below
XmlNodeList nodes = root.SelectNodes("//node()[@type='session']");

dBPathTypeSession.Add(new XmlList("b_session_type", i, nodes));//table,row,paths

The code below this line is probably not needed to answer my question, this is just more detail.

然后在更新后更正属性值:

XmlDocument doc = new XmlDocument();
doc.LoadXml(sessionType.Parameters);

foreach (XmlNode node in file.Paths)//XmlList file = new XmlList();
{
    if (node.Attributes["type"].Value == "system")
    {
        node.Attributes["type"].Value = "session";
    }
}
//push to DB

每个文件中每个路径的信息都包含在这个:

//But I think this is pointless for my question
public class XmlList
{
    public XmlList(string tablePath, int filePath, XmlNodeList paths)
    {
        this.TablePath = tablePath;
        this.FilePath = filePath;
        this.Paths = paths;
    }
    public string TablePath {get; private set;}
    public int FilePath {get; private set;}
    public XmlNodeList Paths {get;set;}
}

我使用的是 C# 3.0(.NET framework 3.5),我必须使用 XMLDocument 使其适合代码中的所有其他内容。

这是更新前 XML 的示例(简短版本)

<Session is_hidden="false">
    <ID is_static="true">1</ID>
    <SESSIONIDX is_static="true">0</SESSIONIDX>
    <Timing>
        <FirstPresentation display_name="FirstPresentation" type="system"/>
        <Pause display_name="Pause" type="system" datatype="float"/>
        <Feedback display_name="Feedback" type="session" datatype="float"/>
        <Answer display_name="Answer" type="system" datatype="float"/>
    </Timing>
    <Balls>
        <Indexed display_name="Indexed" type="session" datatype="pos_int"/>
        <IndexingColor1 display_name="IndexingColor1" type="system" datatype="list">
            <list>
                <ListItem>RED</ListItem>
                <ListItem>BLUE</ListItem>
            </list>
        </IndexingColor1>
    </Balls>
</Session>

这是更新后 XML 的示例(简短版)

<Session is_hidden="false">
    <ID is_static="true">1</ID>
    <SESSIONIDX is_static="true">0</SESSIONIDX>
    <Timing>
        <FirstPresentation display_name="FirstPresentation" type="session"/>//system change for session
        <Pause display_name="Pause" type="system" datatype="float"/>
        <Feedback display_name="Feedback" type="system" datatype="float"/>//session change for system
        <Answer display_name="Answer" type="system" datatype="float"/>//system stay system
    </Timing>
    <Balls>
        <Indexed display_name="Indexed" type="session" datatype="pos_int"/>//session stay session
        <IndexingColor1 display_name="IndexingColor1" type="system" datatype="list">
            <list>
                <ListItem>RED</ListItem>
                <ListItem>BLUE</ListItem>
            </list>
        </IndexingColor1>
    </Balls>
</Session>

下面是我正在寻找的 XML 示例:

<Session is_hidden="false">
    <ID is_static="true">1</ID>
    <SESSIONIDX is_static="true">0</SESSIONIDX>
    <Timing>
        <FirstPresentation display_name="FirstPresentation" type="session"/>//system stay session
        <Pause display_name="Pause" type="system" datatype="float"/>
        <Feedback display_name="Feedback" type="session" datatype="float"/>//session return session
        <Answer display_name="Answer" type="system" datatype="float"/>//system stay system
    </Timing>
    <Balls>
        <Indexed display_name="Indexed" type="session" datatype="pos_int"/>//session stay session
        <IndexingColor1 display_name="IndexingColor1" type="system" datatype="list">
            <list>
                <ListItem>RED</ListItem>
                <ListItem>BLUE</ListItem>
            </list>
        </IndexingColor1>
    </Balls>
</Session>

如果我们将其与 bool 代数进行比较,我们有:

For x = session & y = system

before update = after update -> What we want

x = x -> x

x = y -> x

y = x -> x

y = y -> y

如果您需要更多信息,请在评论中提问,我会更新帖子。

最佳答案

问题是您正在搜索具有名为 type 且值为 session 的属性的节点——然后如果 current 替换该值值为 系统。那是行不通的,因为值(value)不能兼而有之。

你必须要:

    foreach (XmlNode node in root.SelectNodes("//node()[@type='session']"))
        node.Attributes["type"].Value = "system";

    foreach (XmlNode node in root.SelectNodes("//node()[@type='system']"))
        node.Attributes["type"].Value = "session";

更新

如果您有两个 XmlDocuments,它们具有相同的元素层次结构但每个元素的属性集不同,并且希望将一些属性信息从第一个传播到第二个,您需要遍历元素层次结构并在它们之间创建临时映射表。以下是这样做的,假设元素按名称对应,然后如果存在重复名称(例如在列表中)则按顺序:

    static void WalkMatchingElements(XmlElement root1, XmlElement root2, Action<XmlElement, XmlElement> action)
    {
        WalkMatchingElements(root1, root2, (element) => (element.Name), action);
    }

    static void WalkMatchingElements<TKey>(XmlElement root1, XmlElement root2, Func<XmlElement, TKey> getKey, Action<XmlElement, XmlElement> action)
    {
        if (EqualityComparer<TKey>.Default.Equals(getKey(root1), getKey(root2)))
            action(root1, root2);
        var children1GroupedByName = root1.ChildNodes.OfType<XmlElement>().GroupBy(getKey);
        var children2LookupByName = root2.ChildNodes.OfType<XmlElement>().ToLookup(getKey);
        foreach (var child1group in children1GroupedByName)
        {
            var child2group = children2LookupByName[child1group.Key];
            foreach (var pair in child1group.Zip(child2group, (el1, el2) => new KeyValuePair<XmlElement, XmlElement>(el1, el2)))
                WalkMatchingElements(pair.Key, pair.Value, getKey, action);
        }
    }

然后这样调用它:

        var oldDoc = new XmlDocument();
        oldDoc.LoadXml(oldXml);

        var newDoc = new XmlDocument();
        newDoc.LoadXml(newXml);

        WalkMatchingElements(oldDoc.DocumentElement, newDoc.DocumentElement, (elOld, elNew) =>
            {
                var attrOld = elOld.Attributes["type"];
                if (attrOld != null && attrOld.Value == "session")
                {
                    elNew.SetAttribute("type", "system");
                }
            });

Update2 如果您不想立即将整个旧的 XmlDocument 存入内存(尽管我不明白为什么不这样做),您可以构建一个查找表具有 type 属性的元素,由路径索引,稍后使用:

    const string AttributeName = "type";

        var lookup = oldDoc.DocumentElement.DescendantsAndSelf().OfType<XmlElement>().Where(el => el.HasAttribute(AttributeName)).ToLookup(el => el.Path(), el => el.Attributes[AttributeName].Value);

        // And then later

        WalkMatchingElements(new XmlElement[] { newDoc.DocumentElement }, lookup, (el, oldValue) =>
            {
                if (oldValue != null && oldValue == "session")
                    el.SetAttribute(AttributeName, "session");
            });


    private static void WalkMatchingElements<TValue>(IEnumerable<XmlElement> elements, ILookup<string, TValue> pathLookup, Action<XmlElement, TValue> action)
    {
        var elementsByPath = elements.GroupBy(el => el.Path());
        foreach (var elementsGroup in elementsByPath)
        {
            foreach (var pair in elementsGroup.Zip(pathLookup[elementsGroup.Key], (el, value) => new KeyValuePair<XmlElement, TValue>(el, value)))
                action(pair.Key, pair.Value);
            foreach (var element in elementsGroup)
                WalkMatchingElements(element.ChildNodes.OfType<XmlElement>(), pathLookup, action);
        }
    }

您需要以下扩展方法:

public static class XmlNodeExtensions
{
    public static string Path(this XmlElement element)
    {
        if (element == null)
            throw new ArgumentNullException();
        return element.AncestorsAndSelf().OfType<XmlElement>().Reverse().Aggregate(new StringBuilder(), (sb, el) => sb.Append("/").Append(el.Name)).ToString();
    }

    public static IEnumerable<XmlNode> AncestorsAndSelf(this XmlNode node)
    {
        for (; node != null; node = node.ParentNode)
            yield return node;
    }

    public static IEnumerable<XmlNode> DescendantsAndSelf(this XmlNode root)
    {
        if (root == null)
            yield break;
        yield return root;
        foreach (var child in root.ChildNodes.Cast<XmlNode>())
            foreach (var subChild in child.DescendantsAndSelf())
                yield return subChild;
    }
}

public static class EnumerableExtensions
{
    // Back ported from .Net 4.0
    public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector)
    {
        if (first == null) throw new ArgumentNullException("first");
        if (second == null) throw new ArgumentNullException("second");
        if (resultSelector == null) throw new ArgumentNullException("resultSelector");
        return ZipIterator(first, second, resultSelector);
    }

    static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector)
    {
        using (IEnumerator<TFirst> e1 = first.GetEnumerator())
        using (IEnumerator<TSecond> e2 = second.GetEnumerator())
            while (e1.MoveNext() && e2.MoveNext())
                yield return resultSelector(e1.Current, e2.Current);
    }
}

(我忘记了 Zip 不在 .Net 3.5 中。)

关于c# - 如何更改新 XML 文档中属性的值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28118175/

相关文章:

python - 如何将 Python ElemenTtree 编写为 XML 文件?

android - LinearLayout 权重未填充父级

c# - 无法解析 StructureMap.Net4 中的符号 SetAllProperties

c# - 为什么NEST ElasticClient找不到文档?

c# - 当条件为真时,LINQ to 对象是否停止处理 Any()?

c# - PIxelFormat之间如何相互转换?

java html 到 xml saxbuilder jdom2

c# - 多个数据库插入的控制台应用程序上的 ThreadAbortException

c# - 您可以在代码中配置 log4net 而不是使用配置文件吗?

c# - 从数据库中获取数据的问题