含自定义XML前缀的C#模型映射问题求助
问题
在.NET应用中将XML文档映射到C#模型时,大部分场景正常,但XML中存在未声明的自定义前缀mxb,导致映射失败。无法控制XML的生成方式,当前通过API接口POST请求接收数据并自动转换为指定模型。
XML输入示例
<?xml version="1.0" encoding="UTF-8"?> <gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" xmlns:gpxacx="http://www.garmin.com/xmlschemas/AccelerationExtension/v1"> <metadata> <time>2022-09-25T12:06:47.081Z</time> <extensions> <mxb:bike>Test Bike</mxb:bike> <mxb:notes>Testing</mxb:notes> <mxb:conditions>Dry</mxb:conditions> <mxb:track>Test Track</mxb:track> </extensions> </metadata> <trk> <trkseg> <trkpt lat="52.914892" lon="4.776444"> <time>2022-09-25T12:06:47.081Z</time> </trkpt> </trkseg> </trk> </gpx>
当前C#模型
public class GpxData { [XmlRoot(ElementName="extensions", Namespace="http://www.topografix.com/GPX/1/1")] public class Extensions { [XmlElement(ElementName="bike", Namespace="")] public string? Bike { get; set; } [XmlElement(ElementName="notes", Namespace="")] public string? Notes { get; set; } [XmlElement(ElementName="conditions", Namespace="")] public string? Conditions { get; set; } [XmlElement(ElementName="track", Namespace="")] public string? Track { get; set; } } [XmlRoot(ElementName="metadata", Namespace="http://www.topografix.com/GPX/1/1")] public class Metadata { [XmlElement(ElementName="time", Namespace="http://www.topografix.com/GPX/1/1")] public DateTime Time { get; set; } [XmlElement(ElementName="extensions", Namespace="http://www.topografix.com/GPX/1/1")] public Extensions Extensions { get; set; } } [XmlRoot(ElementName = "trkpt", Namespace = "http://www.topografix.com/GPX/1/1")] public class Trkpt { [XmlElement(ElementName = "time", Namespace = "http://www.topografix.com/GPX/1/1")] public DateTime Time { get; set; } [XmlAttribute(AttributeName = "lat", Namespace = "")] public double Lat { get; set; } [XmlAttribute(AttributeName = "lon", Namespace = "")] public double Lon { get; set; } } [XmlRoot(ElementName = "trkseg", Namespace = "http://www.topografix.com/GPX/1/1")] public class Trkseg { [XmlElement(ElementName = "trkpt", Namespace = "http://www.topografix.com/GPX/1/1")] public List<Trkpt> Trkpt { get; set; } } [XmlRoot(ElementName = "trk", Namespace = "http://www.topografix.com/GPX/1/1")] public class Trk { [XmlElement(ElementName = "trkseg", Namespace = "http://www.topografix.com/GPX/1/1")] public Trkseg Trkseg { get; set; } } [XmlRoot(ElementName = "gpx", Namespace = "http://www.topografix.com/GPX/1/1")] public class Gpx { [XmlElement(ElementName="metadata", Namespace="http://www.topografix.com/GPX/1/1")] public Metadata Metadata { get; set; } [XmlElement(ElementName = "trk", Namespace = "http://www.topografix.com/GPX/1/1")] public Trk Trk { get; set; } [XmlAttribute(AttributeName = "creator", Namespace = "")] public string Creator { get; set; } } }
移除extensions标签内的内容时映射正常,但保留mxb前缀或仅移除mxb前缀都无法正常映射。尝试过修改XML声明前缀,但无法控制XML生成,且没有对应的schema,此方法不可行。需要代码端的解决方案。
解决方案
核心思路
XML解析失败的本质是mxb前缀未绑定任何命名空间,我们可以在代码中临时为该前缀绑定一个自定义命名空间,同时更新C#模型的属性标记来匹配。
方法一:修改模型+反序列化时添加命名空间映射
- 更新Extensions类的属性标记
将Extensions类中每个属性的Namespace改为任意自定义值(比如http://tempuri.org/mxb):
[XmlRoot(ElementName="extensions", Namespace="http://www.topografix.com/GPX/1/1")] public class Extensions { [XmlElement(ElementName="bike", Namespace="http://tempuri.org/mxb")] public string? Bike { get; set; } [XmlElement(ElementName="notes", Namespace="http://tempuri.org/mxb")] public string? Notes { get; set; } [XmlElement(ElementName="conditions", Namespace="http://tempuri.org/mxb")] public string? Conditions { get; set; } [XmlElement(ElementName="track", Namespace="http://tempuri.org/mxb")] public string? Track { get; set; } }
- 编写反序列化方法
通过XmlReaderSettings和命名空间映射,为mxb前缀绑定指定的临时命名空间:
public static GpxData.Gpx DeserializeGpx(string xmlContent) { var serializer = new XmlSerializer(typeof(GpxData.Gpx)); var settings = new XmlReaderSettings { NamespaceHandling = NamespaceHandling.OmitDuplicates }; using (var stringReader = new StringReader(xmlContent)) using (var xmlReader = XmlReader.Create(stringReader, settings)) { return (GpxData.Gpx)serializer.Deserialize(xmlReader); } }
方法二:自定义XmlReader处理未声明前缀
如果方法一无效,可以通过自定义XmlTextReader,在读取XML时自动为mxb前缀补充命名空间绑定:
public class MxbNamespaceReader : XmlTextReader { public MxbNamespaceReader(TextReader reader) : base(reader) { } public override string LookupNamespace(string prefix) { if (prefix == "mxb") { return "http://tempuri.org/mxb"; } return base.LookupNamespace(prefix); } } // 反序列化调用 public static GpxData.Gpx DeserializeWithCustomReader(string xmlContent) { var serializer = new XmlSerializer(typeof(GpxData.Gpx)); using (var stringReader = new StringReader(xmlContent)) using (var customReader = new MxbNamespaceReader(stringReader)) { return (GpxData.Gpx)serializer.Deserialize(customReader); } }
注意:此方法仍需按方法一的要求修改Extensions类的属性命名空间。
额外说明
- 临时命名空间不需要存在实际的schema,仅用于解析时的标识,只要模型和反序列化逻辑中的命名空间一致即可。
- 确保所有
mxb:前缀的元素都在模型中绑定了相同的临时命名空间,否则仍会解析失败。
内容的提问来源于stack exchange,提问作者Bram
相关产品推荐
相关产品推荐

