Словарь IXmlSerializable в C # без узлов 'Key' / 'Value' - PullRequest
2 голосов
/ 19 августа 2010

Я пытаюсь сериализовать словарь в C #.Все примеры Мне удалось найти создание XML, например, следующее:

<Dictionary>
    <ArrayOfEntries>
        <Entry>
            <Key>myFirstKey</Key>
            <Value>myFirstValue</Value>
        </Entry>
        <Entry>
            <Key>mySecondKey</Key>
            <Value>mySecondValue</Value>
        </Entry>
    </ArrayOfEntries>
</Dictionary>

Это меняется, иногда узел ArrayOfEntries не нужен, но я все еще вижу регулярную структуру пар «ключ-значение» словаря, хранящихся в их собственных узлах.Я хотел бы что-то вроде следующего:

<Dictionary>
    <myFirstKey>myFirstValue</myFirstKey>
    <mySecondKey>mySecondValue</mySecondKey>
</Dictionary>

Я написал ReadXml и WriteXml, чтобы сделать это раньше, и это работает для одного словаря, но если я попытаюсь сериализовать и десериализоватьList<T> моих экземпляров сериализуемого словаря, десериализованный список заканчивается только сериализуемым словарем last .Я думаю, что-то должно быть жадным в моем методе чтения или записи, чтобы он не знал, когда остановиться.Вот мои сериализуемые словарные методы, которые в настоящее время не работают для сериализации List сериализуемых словарей:

public void WriteXml(XmlWriter writer)
{
    foreach (string key in getKeysToSerialize())
    {
        string cleanKey = key.SanitizeForXml();
        string value = getValueForKey(key).Trim();

        if (isCdata(key, value))
        {
            string cdataFriendlyValue = cleanStringForUseInCdata(value);
            if (string.IsNullOrEmpty(cdataFriendlyValue))
            {
                continue;
            }

            writer.WriteStartElement(cleanKey);
            writer.WriteCData(cdataFriendlyValue);
            writer.WriteEndElement();
        }
        else
        {
            writer.WriteElementString(cleanKey, value);
        }
    }
}

и ReadXml:

public void ReadXml(XmlReader reader)
{
    string key = null, value = null;
    bool wasEmpty = reader.IsEmptyElement;

    while (reader.Read())
    {
        if (wasEmpty)
        {
            return;
        }

        switch (reader.NodeType)
        {
            case XmlNodeType.Element:
                key = reader.Name;
                if (keyIsSubTree(key))
                {
                    using (XmlReader subReader = reader.ReadSubtree())
                    {
                        storeSubtree(key, subReader);
                    }

                    // Reset key to null so we don't try to parse it as
                    // a regular key-value pair later
                    key = null;
                }
                break;
            case XmlNodeType.Text:
                value = reader.Value;
                break;
            case XmlNodeType.CDATA:
                value = cleanCdataForStoring(reader.Value);
                break;
            case XmlNodeType.EndElement:
                if (!string.IsNullOrEmpty(key))
                {
                    string valueToStore =
                        string.IsNullOrEmpty(value) ? string.Empty : value
                    Add(key, valueToStore);
                }
                key = null;
                value = null;
                break;
        }
    }
}

Одно отличие IМежду другими уроками и тем, что я делаю, я заметил, что многие используют XmlSerializer для сериализации и десериализации объектов в их ReadXml и WriteXml, тогда как я использую WriteElementString и тому подобное.

У меня вопрос: как я могу сериализовать словарь так, чтобы его XML был похож на мой второй пример XML выше, но чтобы сериализация и десериализация List<MySerializableDictionary> также работали?Даже намеки на то, «почему ты делаешь бла , это может сделать его забавным», могли бы помочь.

1 Ответ

2 голосов
/ 19 августа 2010

Мой WriteXml показался нормальным, потому что он выводит содержимое словаря в желаемом формате XML. ReadXml был там, где я ошибался. Основываясь на реализации ReadXml, найденной в этом уроке , я придумал следующее:

public override void ReadXml(XmlReader reader)
{
    reader.MoveToContent();
    bool isEmptyElement = reader.IsEmptyElement;
    reader.ReadStartElement();
    if (isEmptyElement)
    {
        return;
    }
    while (XmlNodeType.EndElement != reader.NodeType)
    {
        // Bypass XmlNodeType.Whitespace, as found in XML files
        while (XmlNodeType.Element != reader.NodeType)
        {
            if (XmlNodeType.EndElement == reader.NodeType)
            {
                reader.ReadEndElement();
                return;
            }
            reader.Read();
        }
        string key = reader.Name;
        if (keyIsSubTree(key))
        {
            storeSubtree(key, reader.ReadSubtree());
        }
        else
        {
            string value = reader.ReadElementString();
            storeKeyValuePair(key, value ?? string.Empty);
        }
    }
    if (XmlNodeType.EndElement == reader.NodeType)
    {
        reader.ReadEndElement();
    }
}

Кажется, это работает для словарей, которые имеют только одну пару ключ-значение, а также для словарей с несколькими парами ключ-значение. Мой тест сериализации / десериализации List<T> словарей также сработал. Я еще не добавил никакой специальной обработки для CDATA, и я уверен, что это будет необходимо. Это похоже на начало.

Редактировать: на самом деле, может быть, мне нужно беспокоиться о CDATA, только когда я пишу , а не при чтении.

Редактировать: обновлен ReadXml код выше для учета XmlNodeType.Whitespace, который находится в файлах XML при десериализации. Я получал XML-ошибку, потому что ReadEndElement вызывался, когда reader.NodeType не был XmlNodeType.EndElement. Поставьте галочку для этого, чтобы он вызывал ReadEndElement только когда это EndElement, а также изменил внешний цикл while, чтобы читатель мог прогрессировать (через Read()), когда он не достиг Element и оно не достигло EndElement (например, оно достигло Whitespace).

...