我使用JsonConvert SerializeXmlNode将xml转换为json。我面临的问题是我有一个标签,有时可以有价值,有时候是空的
<AustrittDatum>2018-01-31+01:00</AustrittDatum>
...
<AustrittDatum xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
结果 - 当我尝试使用字符串属性&#34; AustrittDatum&#34;将json反序列化为C#对象时,我遇到异常。 - &#34; Newtonsoft.Json.JsonReaderException:&#39;读取字符串时出错。意外的令牌:StartObject。 Path&#39; AustrittDatum&#39;。&#39; &#34 ;, 因为
<AustrittDatum xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance xsi:nil="true"/>
序列化为
"AustrittDatum": {
"@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
"@xsi:nil": "true"
},
我如何强迫它成为像"AustrittDatum": ""
这样的东西,或者可能是解决它的一些正确方法?
答案 0 :(得分:1)
似乎在遇到带有xsi:nil="true"
的XML元素时,Json.NET的XmlNodeConverter
会创建一个带有您看到的属性的JSON对象,而不是null JToken
。这与Newtonsoft文档页面Converting between JSON and XML:
会话规则
- 元素保持不变。
- 属性以@为前缀,应位于对象的开头。
- 单个子文本节点是直接针对元素的值,否则可以通过#text。
访问它们- XML声明和处理说明以?。
为前缀- 字符数据,注释,空白和重要的空白节点分别通过#cdata-section,#comment,#whitespace和#significant-whitespace进行访问。
- 同一级别具有相同名称的多个节点将组合在一起成为一个数组。
- 空元素为空。
如果从JSON创建的XML与您想要的不匹配,那么您需要手动转换它...
尽管如此,我认为将xsi:nil="true"
元素转换为null
JSON值是合理的,因为xsi:nil
是predefined w3c attribute。可能Newtonsoft没有这样做,因为这些元素可以携带额外的属性,如果要将元素转换为null,这些属性将会丢失。
如果您愿意,可以为XmlNodeConverter
提交enhancement request,但与此同时,以下扩展方法将对JToken
层次结构进行后处理并转换以前为{{1}的对象要使null JSON值为null的元素:
nil
然后使用它:
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>();
此处我最初解析为XDocument
,但您也可以使用较早的XmlDocument
示例工作.Net fiddle。