如何使用C#中的XmlSerializer将字节数组序列化为XML?

13

假设我们有一个结构体,它的数据是通过使用Marshal.PtrToStructure提供的不受托管的字节数组来提供的。

C#结构布局:

[StructLayout(LayoutKind.Sequential, Size = 128, CharSet = CharSet.Ansi, Pack = 1)]
public struct MNG_Y_Params
{
    public byte Number;
    public byte Version;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
    public byte[] OliNumber;
    public byte InterfaceType;
}

字节数组在非托管代码中表示一个(ASCII)字符串。

这个结构体是另一个结构体的成员(该结构体具有其他成员):

public struct MyData
{
    public int ID;
    public StructType structType;
    [XmlElement(ElementName="MNG_Y_Params")]
    public MNG_Y_Params y_params;
    [XmlElement(ElementName = "SimpleStruct2")]
    public SimpleStruct2 ss2;
};

所以我们还有这个支持代码

public class XMLIgnore
{
    static public XmlSerializer customserialiser(MyData d)
    {
        XmlAttributes attrs = new XmlAttributes();
        attrs.XmlIgnore = true;
        XmlAttributeOverrides xmlOveride = new XmlAttributeOverrides();
        switch (d.structType)
        {
            case StructType.ST_1:
                xmlOveride.Add(typeof(MyData), "ss2", attrs);
                break;
            case StructType.ST_2:
                xmlOveride.Add(typeof(MyData), "y_params", attrs);
                break;
            default:
                break;
        }
        return new XmlSerializer(typeof(MyData), xmlOveride);
    }
}

和保存方法

    static void SaveToXml(object obj, string fileName, XmlSerializer writer)
    {
        //XmlSerializer writer = new XmlSerializer(obj.GetType());
        using (StreamWriter file = new StreamWriter(fileName))
        {
            writer.Serialize(file, obj);
        }
    }

举个例子,我们只需生成一些数据。

        MNG_Y_Params yParams = new MNG_Y_Params();
        yParams.Version = 1;
        yParams.InterfaceType = 15;
        yParams.Number = 35;
        ASCIIEncoding enc = new ASCIIEncoding();
        yParams.OliNumber = enc.GetBytes("#1");

        MyData md1 = new MyData();
        md1.ID = 1;
        md1.structType = StructType.ST_1;
        md1.y_params = yParams;

        XmlSerializer writer = XMLIgnore.customserialiser(md1);
        SaveToXml(md1, @"C:\temp\dataOne.xml", writer);

期望的 XML:

<?xml version="1.0" encoding="utf-8"?>
<MyData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ID>1</ID>
  <structType>ST_1</structType>
  <MNG_Y_Params>
    <Number>35</Number>
    <Version>1</Version>
    <OliNumber>#1</OliNumber>
    <InterfaceType>15</InterfaceType>
  </MNG_Y_Params>
</MyData>

结果 XML:

<?xml version="1.0" encoding="utf-8"?>
<MyData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ID>1</ID>
  <structType>ST_1</structType>
  <MNG_Y_Params>
    <Number>35</Number>
    <Version>1</Version>
    <OliNumber>IzE=</OliNumber>
    <InterfaceType>15</InterfaceType>
  </MNG_Y_Params>
</MyData>

请注意字节数组成员(OliNumber)的结果。

这里是否有我们可以使用的属性?我漏掉了什么吗?

感谢您的时间和帮助。

Ilan

关于reference的参考资料


当您进行反序列化时,OliNumber中的数据是否与序列化之前不同?XML中的值可能是正确的,也可能只是XMLSerializer用于字节数组的格式。 - Kipotlov
@Kipotlov 感谢您的评论。反序列化工作正常,但重点是要在XML中表示来自非托管代码的数据,其中字节数组表示一个(ASCII)字符串。 - Ilan Huberman
序列化器将 byte[] 视为二进制数据而非字符串,因此它使用传输编码(base64)来允许任意二进制数据通过 XML。请阅读规范,XML 不允许在内容中包含大多数不可打印字符。如果您要发送一个字符串,请将其称为 String - escape-llc
我知道这篇文章有点老了,但是我相信如果你将 <?xml version="1.0" encoding="utf-8"?> 改成 utf-16 的编码方式,它可能会起作用。你没有讨论基本字符集,所以在 XML 转换器中可能会遇到问题。我曾经处理其他语言集的翻译时也遇到过这个问题。utf-16 可以捕捉到双字节字符,而 utf-8 则不能。 - htm11h
3个回答

8
XmlSerializer默认使用base64编码对字节数组进行编码。如果您使用此网站并粘贴IzE=,并对其进行解码,则结果将为#1。您可以通过设置XmlElementAttribute.DataType来更改编码方式。我不确定[XmlElement(DataType = "string")]是否有效,但您可以尝试一下。使用[XmlElement(DataType = "hexBinary")]将生成原始字节。

谢谢,我尝试了你的建议,但在创建自定义的“XmlSerializer”时出现了异常:“'string' 是 XmlElementAttribute.DataType 属性的无效值。string 无法转换为 System.Byte[]”。至于另一个选项“HexBinary”的结果是:“'<OliNumber>2331</OliNumber>'”。 - Ilan Huberman
@IIan - 是的,这正是我所预期的。如果你想要一个字符串值输出,你需要实现IXmlSerializable并自己完成它。 - SwDevMan81

1

我使用以下方法使其正常工作:

  public class MySerializableClass
  {   
    [XmlIgnore]
    public string NaughtyXmlCharactersAsString { get; set; }

    [XmlElement(ElementName = "NaughtyXmlCharacters", DataType = "hexBinary")]
    public byte[] NaughtyXmlCharactersAsBytes
    {
        get { return Encoding.UTF8.GetBytes(NaughtyCharactersAsString ?? string.Empty); }
        set { NaughtyXmlCharactersAsString = Encoding.UTF8.GetString(value); }
    }

我将只访问该属性的 "AsString" 版本。

0

这是我做的方法:

public class MySerializableClass
{
    private string dummy;

    [XmlElement("NaughtyXmlCharacters")]
    public string NaughtyXmlCharactersAsString
    {
       get 
       {
           return BitConverter.ToString(NaughtyXmlCharacters);
       }
       set
       {
           // without this, the property is not serialized.
           dummy = value;
       }
    }

    [XmlIgnore]
    public byte[] NaughtyXmlCharacters
    {
        get;
        set;
    }
}

然后将字节格式化为十六进制值,并用减号分隔:00-AF-B1


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接