Json通过数组检测将xml文档转换为Json



我正在尝试将复杂的xml文档转换为json格式,我正在使用Newtonsoft.json来实现我的目标。我遇到了一个大大小小的问题。例如

我有一个模型,看起来像:

public class assets
{
public UInt32 id {get; set;}
public String providerName {get; set;}
public String provider {get; set;}
public String realm {get; set;}
public ICollection<unit> unit {get; set;}
}

我的意图是用户将xml内容流式传输到方法,该方法将把xml更改为json,我将把它发布到API。

为了简化,用户正在粘贴简单的xml(普通的xml要复杂得多,但基本上它看起来像下面的许多级别的示例(

<assets xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="assets.xsd" providerName="myProviderName" provider="myProvider" realm="myCatalog">
<unit idKey="newGeo_63119679"></unit>
<unit idKey="newGeo_63119179"></unit>
</assets>

Json的结果看起来像:

{"@providerName":"myProviderName","@provider":"myProvider","@realm":"myCatalog","unit":[{"@idKey":"newGeo_63119679"},{"@idKey":"newGeo_63119577"}]}

因此,具有所有魔力的服务看起来像:

public async ValueTask<string> AddAsset(string body)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(body);
string json = JsonConvert.SerializeXmlNode(doc, Formatting.None, true);

HttpResponseMessage response = await this._httpClient.PostAsJsonAsync("/Asset/create_asset", json);
string responseJson = await response.Content.ReadAsStringAsync();
return responseJson;
}

好吧,这个部分可以工作,但当我从xml中删除一个单元节点(所以只剩下一个单元结点(时,我的结果是:

{"@providerName":"myProviderName","@provider":"myProvider","@realm":"myCatalog","unit":{"@idKey":"newGeo_63119679"}}

现在,将其反序列化为模型所需的数组已经不见了。我知道我可以操作xml属性来添加json:Array='true'。

但我想知道是否有更复杂的解决方案,例如JsonConverter,它可以搜索给定类型的属性,并检查它是否为其集合,如果是,则将其分配为json集合。我该如何解决这个问题?

而且,正如我检查的那样,SerializeXmlNode没有转换器参数。

好吧,我找到了我的答案,通过混合@dbc的信息和我的快乐创新:

public class Converter<TEntity> : JsonConverter<TEntity> where TEntity : class
{
private readonly IEnumerable<Type> _entityTypes =
Assembly.GetExecutingAssembly().GetReferencedAssemblies()
.SelectMany(assembly => Assembly.Load(assembly).GetTypes().Where(t => t.Namespace == MigrationService.EntitiesNameSpace));
public override bool CanWrite => false;

public override void WriteJson(JsonWriter writer, TEntity value, JsonSerializer serialize) =>
throw new NotImplementedException($"Write option is turned off for {nameof(CollectionConverter)} custom json converter");
public override TEntity ReadJson(JsonReader reader, Type objectType, TEntity existingValue, bool hasExistingValue, JsonSerializer serializer)
{
void SetSimpleTypes(JToken token, TEntity instance, PropertyInfo property)
{
var value = token[property.Name];
if(value == null) return;
var cast = Convert.ChangeType(value, property.PropertyType);
property.SetValue(instance, cast);
}
void SetClassTypes(JToken token, TEntity instance, PropertyInfo property)
{
var constructedConverterType = typeof(CollectionConverter<>).MakeGenericType(property.PropertyType);
if (Activator.CreateInstance(constructedConverterType) is not JsonConverter converter) return;
var propertyInstance = JsonConvert.DeserializeObject(token[property.Name].ToString(), property.PropertyType, converter);
property.SetValue(instance, propertyInstance);
}

void SetCollectionTypes(JToken token, TEntity instance, PropertyInfo property)
{
var constructedCollectionType = typeof(List<>).MakeGenericType(property.PropertyType.GetGenericArguments().Single());
var collectionInstance = Activator.CreateInstance(constructedCollectionType) as IList;
var value = token[property.Name];

void HandleSingleCollectionType(string body)
{
var propertyCollectionType = property.PropertyType.GetGenericArguments().Single();
var constructedConverterType = typeof(CollectionConverter<>).MakeGenericType(propertyCollectionType);
if (Activator.CreateInstance(constructedConverterType) is not JsonConverter converter) return;
var convertedInstance = JsonConvert.DeserializeObject(body, propertyCollectionType, converter);
collectionInstance?.Add(convertedInstance);
}

switch (value)
{
case JArray array:
foreach (var body in array)
{
HandleSingleCollectionType(body.ToString());
}
break;
case JObject:
HandleSingleCollectionType(value.ToString());
break;
default:
Debug.WriteLine($"Unknown or empty json token value for property {property.Name}");
break;
}

property.SetValue(instance, collectionInstance);
}
JToken token = JToken.Load(reader);
var instance = (TEntity)Activator.CreateInstance(typeof(TEntity));
var properties = instance?.GetType().GetProperties(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance);
if (properties == null) return default;
foreach (PropertyInfo property in properties)
{
try
{
if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>))
{
SetCollectionTypes(token, instance, property);
}
else if (this._entityTypes.Any(type => type == property.PropertyType) && property.PropertyType.IsClass)
{
SetClassTypes(token, instance, property);
}
else
{
SetSimpleTypes(token, instance, property);
}
}
catch (NullReferenceException ex)
{
Debug.WriteLine($"Null value for entity class property {property.Name}, exception: {ex}");
}
catch (FormatException ex)
{
Debug.WriteLine($"Can not convert value property {property.Name} in {instance.GetType().Name}, exception: {ex}");
}
catch (Exception ex)
{
Debug.WriteLine($"Undefined exception for {property.Name} exception: {ex}");
}
}
return instance;
}
}

它基于类结构转换非常复杂的xml结构,所以如果类结构说某个东西是集合,它将构建集合,如果它是对象(我指的是类(,它将创建类的实例,等等。此外,它还使用自己来转换xml的任何子级。

最新更新