You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用C# DataContractJsonSerializer反序列化含JSON的字符串属性遇XmlException

问题描述

我尝试反序列化一个包含名为Metadata的字符串属性的类,该属性的值本身是JSON内容。我不需要解析这段JSON内部的结构,只希望把它原封不动地作为字符串存入属性里。但Web服务返回的JSON格式我无法控制,现在反序列化时抛出异常:

XmlException: 预期命名空间为空的结束元素'metadata',却找到命名空间为空的元素'key'。

当前代码示例:

[DataContract]
public class YourClass
{
    [DataMember(Name = "metadata", IsRequired = false, EmitDefaultValue = false)]
    public string Metadata { get; set; }
    
    [DataMember(Name = "anotherProperty", IsRequired = false, EmitDefaultValue = false)]
    public int AnotherProperty { get; set; }
}

internal class Program
{
    static void Main()
    {
        // Web服务返回的JSON字符串
        string jsonString = "{\"metadata\":{\"key\":\"value\"}, \"anotherProperty\":42}";

        using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonString)))
        {
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(YourClass));
            YourClass obj = (YourClass)serializer.ReadObject(stream);
            Console.WriteLine(obj.Metadata);
        }
    }
}
解决方案

问题根源是DataContractJsonSerializer会自动解析metadata对应的JSON对象,而你需要它把这个对象当作原始字符串处理。下面是几种实用的实现方式:

方法一:用Newtonsoft.Json(Json.NET)实现(推荐)

这是最简便的方案,自定义转换器将JSON对象直接转为原始字符串:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public class YourClass
{
    [JsonProperty("metadata")]
    public string Metadata { get; set; }
    
    [JsonProperty("anotherProperty")]
    public int AnotherProperty { get; set; }
}

internal class Program
{
    static void Main()
    {
        string jsonString = "{\"metadata\":{\"key\":\"value\"}, \"anotherProperty\":42}";
        YourClass obj = JsonConvert.DeserializeObject<YourClass>(jsonString, new JsonSerializerSettings
        {
            Converters = { new RawJsonConverter() }
        });
        Console.WriteLine(obj.Metadata); // 输出: {"key":"value"}
    }
}

// 自定义转换器,把JSON对象转成原始字符串
public class RawJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(string);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartObject || reader.TokenType == JsonToken.StartArray)
        {
            JToken token = JToken.Load(reader);
            return token.ToString(Formatting.None);
        }
        return reader.Value?.ToString();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteRawValue(value.ToString());
    }
}

方法二:修改DataContract属性类型(兼容原生序列化器)

如果必须用DataContractJsonSerializer,可以把Metadata的类型改为dynamic或JObject,反序列化后再转成字符串:

[DataContract]
public class YourClass
{
    [DataMember(Name = "metadata", IsRequired = false, EmitDefaultValue = false)]
    public dynamic Metadata { get; set; }
    
    [DataMember(Name = "anotherProperty", IsRequired = false, EmitDefaultValue = false)]
    public int AnotherProperty { get; set; }
}

// 反序列化后转换为字符串
YourClass obj = (YourClass)serializer.ReadObject(stream);
string metadataStr = JsonConvert.SerializeObject(obj.Metadata);
Console.WriteLine(metadataStr);

或者直接用JObject类型:

[DataContract]
public class YourClass
{
    [DataMember(Name = "metadata")]
    public JObject Metadata { get; set; }
    
    [DataMember(Name = "anotherProperty")]
    public int AnotherProperty { get; set; }
}

// 使用时转字符串
string metadataStr = obj.Metadata.ToString(Formatting.None);

方法三:自定义DataContract代理(无第三方依赖)

如果不想引入Newtonsoft.Json,可以实现IDataContractSurrogate来手动处理metadata字段,但这种方法比较繁琐:

using System.Runtime.Serialization;
using System.Reflection;
using Newtonsoft.Json.Linq; // 或自行实现JSON解析逻辑

public class MetadataSurrogate : IDataContractSurrogate
{
    public Type GetDataContractType(Type type) => type;

    public object GetDeserializedObject(object obj, Type targetType)
    {
        if (targetType == typeof(YourClass))
        {
            var yourObj = (YourClass)obj;
            // 重新解析原始JSON字符串,提取metadata的原始内容
            JObject jsonObj = JObject.Parse(jsonString);
            yourObj.Metadata = jsonObj["metadata"].ToString(Formatting.None);
            return yourObj;
        }
        return obj;
    }

    // 其他接口方法默认实现
    public object GetObjectToSerialize(object obj, Type targetType) => obj;
    public object GetCustomDataToExport(Type clrType, Type dataContractType) => null;
    public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType) => null;
    public void GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes) { }
    public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) => null;
    public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit) => typeDeclaration;
}

// 使用方式
var settings = new DataContractJsonSerializerSettings
{
    DataContractSurrogate = new MetadataSurrogate()
};
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(YourClass), settings);

内容的提问来源于stack exchange,提问作者NKatic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 10:19:50