Deserialize Xml с пустыми элементами в С#
Попытка десериализации некоторых фрагментов xml от поставщика в объекты. Проблема в том, что я получаю недопустимый формат для каждого тега элемента empy. Я могу десериализовать объект без проблем, когда все элементы имеют значения. Или пустые элементы опущены.
Xml Snippit:
<foo>
<propOne> 1 </propOne>
< propTwo/ >
</foo>
Класс С#:
[Serialilbe()]
public class foo
{
public foo(){}
[XmlElementAttribute(IsNullable = true)]
public int? propOne {get;set;}
[XmlElementAttribute(IsNullable = true)]
public int? propTwo {get;set;}
}
Есть ли параметр в классе, который я могу сделать для настройки разбора?
или
Есть ли простой способ применить xsl для удаления этих элементов?
или
Должен ли я использовать regEx для удаления пустых элементов перед десриализацией?
или
еще лучший способ?
Ответы
Ответ 1
Наиболее единообразным способом очистки этих узлов является добавление фильтра RegEx к десериализатору.
public static T Deserialize<T>(string xml){
XmlSerializer xs = new XmlSerializer(typeof(T));
string cleanXml = Regex.Replace(xml, @"<[a-zA-Z].[^(><.)]+/>",
new MatchEvaluator(RemoveText));
MemoryStream memoryStream = new MemoryStream((new UTF8Encoding()).GetBytes(cleanXml));
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
return (T)xs.Deserialize(memoryStream);
}
static string RemoveText(Match m) { return "";}
Ответ 2
См. статью: Может ли XmlSerializer десериализоваться в Nullable?
В двух словах ваш Xml должен выглядеть так, если вы хотите использовать типы Nullable:
<foo xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
<propOne>1</propOne>
<propTwo xsi:nil='true'/>
</foo>
Два изменения добавляют пространство имен и явно устанавливают xsi: nil в true для нулевого элемента.
Если у вас нет контроля над вашим Xml, существует более сложная методика, описанная здесь: Использование XmlSerializer для десериализации в Nullable
Ответ 3
Другой вариант, если вы не контролируете входящий XML, заключается в том, чтобы обойти это, если десериализатор делает вид, что переменная является строкой:
[Serializable()]
public class foo
{
public foo(){}
[XmlElement("propOne")]
[EditorBrowsable(EditorBrowsableState.Never)]
public string propOneString {get;set;}
[XmlIgnore]
private int? propOneInternal = null;
[XmlIgnore]
private bool propOneSet = false;
[XmlIgnore]
public int? propOne
{
get
{
if (!propOneSet)
{
if(!string.IsNullOrEmpty(propOneString)
{
propOneInternal = int.Parse(propOneString);
}
//else leave as pre-set default: null
propOneSet = true;
}
return propOneInternal;
}
set { propOneInternal = value; }
}
}
Deserialiser с удовольствием разбирает строковый элемент, когда он пуст, поэтому вы его используете.
Это не особенно приятно, но это будет сделано, если у вас есть только один или два тега для покрытия
Ответ 4
реализовать IXmlSerializable для Foo, все остальное будет хакерским.
Ответ 5
Для простоты, почему бы вам не разобрать xml явно с помощью XmlDocument и XPath? Используйте XPath для явного доступа к каждому xml node, например.
XmlNode node = xml.SelectSingleNode ("foo/PropOne");
if (node != null)
{
propOneValue = node.innerText;
}