似乎当遇到带有xsi:nil="true" 的XML 元素时,Json.NET 的XmlNodeConverter 会创建一个带有您看到的属性的JSON 对象,而不是null JToken。这与 Newtonsoft 文档页面Converting between JSON and XML 一致:
转化规则
- 元素保持不变。
- 属性以 @ 为前缀,并应位于对象的开头。
- 单个子文本节点是直接针对元素的值,否则它们将通过#text 访问。
- XML 声明和处理指令以 ? 为前缀。
- 字符数据、cmets、空白和重要空白节点分别通过#cdata-section、#comment、#whitespace 和#significant-whitespace 访问。
- 同一层级的多个同名节点组合成一个数组。
- 空元素为空。
如果从 JSON 创建的 XML 与您想要的不匹配,那么您将需要手动转换它...
尽管如此,认为带有xsi:nil="true" 的元素将被转换为null JSON 值是合理的,因为xsi:nil 是predefined w3c attribute。可能 Newtonsoft 没有这样做,因为这样的元素可以携带额外的属性,如果将元素转换为 null,这些属性将会丢失。
您可以根据需要为XmlNodeConverter 提交enhancement request,但与此同时,以下扩展方法将对JToken 层次结构进行后处理,并将以前为nil 元素的对象转换为空JSON 值:
public static class JTokenExtensions
{
const string XsiNamespace = @"http://www.w3.org/2001/XMLSchema-instance";
readonly static string XmlNullValue = System.Xml.XmlConvert.ToString(true);
public static JToken ReplaceXmlNilObjectsWithNull(this JToken root)
{
return root.ReplaceXmlNilObjects(t => JValue.CreateNull());
}
public static JToken ReplaceXmlNilObjects(this JToken root, Func<JToken, JToken> getReplacement)
{
var query = from obj in root.DescendantsAndSelf().OfType<JObject>()
where obj.Properties().Any(p => p.IsNilXmlTrueProperty())
select obj;
foreach (var obj in query.ToList())
{
var replacement = getReplacement(obj);
if (obj == root)
root = replacement;
if (obj.Parent != null)
obj.Replace(replacement);
}
return root;
}
static IEnumerable<JToken> DescendantsAndSelf(this JToken node)
{
// Small wrapper adding this method to all JToken types.
if (node == null)
return Enumerable.Empty<JToken>();
var container = node as JContainer;
if (container != null)
return container.DescendantsAndSelf();
else
return new[] { node };
}
static string GetXmlNamespace(this JProperty prop)
{
if (!prop.Name.StartsWith("@"))
return null;
var index = prop.Name.IndexOf(":");
if (index < 0 || prop.Name.IndexOf(":", index+1) >= 0)
return null;
var ns = prop.Name.Substring(1, index - 1);
if (string.IsNullOrEmpty(ns))
return null;
var nsPropertyName = "@xmlns:" + ns;
foreach (var obj in prop.AncestorsAndSelf().OfType<JObject>())
{
var nsProperty = obj[nsPropertyName];
if (nsProperty != null && nsProperty.Type == JTokenType.String)
return (string)nsProperty;
}
return null;
}
static bool IsNilXmlTrueProperty(this JProperty prop)
{
if (prop == null)
return false;
if (!(prop.Value.Type == JTokenType.String && (string)prop.Value == "true"))
return false;
if (!(prop.Name.StartsWith("@") && prop.Name.EndsWith(":nil")))
return false;
var ns = prop.GetXmlNamespace();
return ns == XsiNamespace;
}
}
然后像这样使用它:
// Parse XML to XDocument
var xDoc = XDocument.Parse(xmlString);
// Convert the XDocument to an intermediate JToken hierarchy.
var converter = new Newtonsoft.Json.Converters.XmlNodeConverter { OmitRootObject = true };
var rootToken = JObject.FromObject(xDoc, JsonSerializer.CreateDefault(new JsonSerializerSettings { Converters = { converter } } ))
// And replace xsi:nil objects will null JSON values
.ReplaceXmlNilObjectsWithNull();
// Deserialize to the final RootObject.
var rootObject = rootToken.ToObject<RootObject>();
生成:
"AustrittDatum": [
"2018-01-31+01:00",
null
],
这里我最初解析为XDocument,但您也可以使用旧的XmlDocument
样本工作.Net fiddle。