| File: System\Private\Windows\JsonData.cs | Web Access |
| Project: src\winforms\src\System.Private.Windows.Core\src\System.Private.Windows.Core.csproj (System.Private.Windows.Core) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Text.Json; namespace System.Private.Windows; /// <summary> /// Wrapper which contains JSON serialized data along with the JSON data's original type information /// to be deserialized later. /// </summary> /// <remarks> /// <para> /// There may be instances where this type is not available in different versions, e.g. .NET 8, .NET Framework. /// If this type needs to be deserialized from stream in these instances, a workaround would be to create an assembly with the name <see cref="IJsonData.CustomAssemblyName"/> /// and replicate this type. Then, manually retrieve the serialized stream and use the <see cref="Formats.Nrbf.NrbfDecoder"/> to decode the stream and rehydrate the serialized type. /// Alternatively, but not recommended, BinaryFormatter can also be used to deserialize the stream if this type is not available. /// </para> /// </remarks> /// <example> /// <![CDATA[ /// // Recommended: deserialize using NrbfDecoder. /// void DeserializeJsonData(DataObject dataObject) /// { /// // Manually retrieve serialized stream. /// System.Runtime.InteropServices.ComTypes.FORMATETC formatetc = new() /// { /// cfFormat = (short)DataFormats.GetFormat("yourDataFormat").Id, /// dwAspect = System.Runtime.InteropServices.ComTypes.DVASPECT.DVASPECT_CONTENT, /// lindex = -1, /// tymed = System.Runtime.InteropServices.ComTypes.TYMED.TYMED_HGLOBAL /// }; /// /// System.Runtime.InteropServices.ComTypes.IDataObject castedDataObject = (System.Runtime.InteropServices.ComTypes.IDataObject)dataObject; /// castedDataObject.GetData(ref formatetc, out System.Runtime.InteropServices.ComTypes.STGMEDIUM medium); /// IntPtr hglobal = medium.unionmember; /// Stream stream; /// try /// { /// IntPtr buffer = GlobalLock(hglobal); /// int size = GlobalSize(hglobal); /// byte[] bytes = new byte[size]; /// Marshal.Copy(buffer, bytes, 0, size); /// // this comes from DataObject.Composition.s_serializedObjectID /// int index = 16; /// stream = new MemoryStream(bytes, index, bytes.Length - index); /// } /// finally /// { /// GlobalUnlock(hglobal); /// } /// /// // Use Nrbf to decode stream and rehydrate data. /// System.Formats.Nrbf.SerializationRecord record = System.Formats.Nrbf.NrbfDecoder.Decode(stream); /// if (record.TypeName.AssemblyName.FullName != "System.Private.Windows.VirtualJson") /// { /// // The data was not serialized as JSON. /// return; /// } /// /// if (record is not System.Formats.Nrbf.ClassRecord types /// || types.GetRawValue("<JsonBytes>k__BackingField") is not System.Formats.Nrbf.SZArrayRecord<byte> byteData /// || types.GetRawValue("<InnerTypeAssemblyQualifiedName>k__BackingField") is not string innerTypeAssemblyQualifiedName /// || !System.Reflection.Metadata.TypeName.TryParse(innerTypeAssemblyQualifiedName.ToCharArray(), out System.Reflection.Metadata.TypeName? innerTypeName)) /// { /// // This is supposed to be JsonData, but somehow the data is corrupt. /// throw new InvalidOperationException(); /// } /// /// // TODO: Additional checking on 'innerTypeName' to ensure it is expected type. /// /// // This should return the original data that was JSON serialized. /// var result = System.Text.Json.JsonSerializer.Deserialize(byteData.GetArray(), innerType); /// // TODO: Process the payload as needed. /// } /// /// [DllImport("kernel32.dll")] /// static extern int GlobalSize(IntPtr hMem); /// /// [DllImport("kernel32.dll")] /// static extern IntPtr GlobalLock(IntPtr hMem); /// /// [DllImport("kernel32.dll")] /// static extern int GlobalUnlock(IntPtr hMem); /// /// // OR /// // Not recommended: deserialize using BinaryFormatter. /// /// // This definition must live in an assembly named System.Private.Windows.VirtualJson and referenced in order to work as expected. /// namespace System.Private.Windows; /// [Serializable] /// struct JsonData : IObjectReference /// { /// public byte[] JsonBytes { get; set; } /// /// public string InnerTypeAssemblyQualifiedName { get; set; } /// /// public object GetRealObject(StreamingContext context) /// { /// // TODO: Additional checking on InnerTypeAssemblyQualifiedName to ensure it is expected type. /// return JsonSerializer.Deserialize(JsonBytes, typeof(ExpectedType)) ?? throw new InvalidOperationException(); /// } /// } /// ]]> /// </example> [Serializable] internal struct JsonData<T> : IJsonData { public byte[] JsonBytes { get; set; } public readonly string InnerTypeAssemblyQualifiedName => typeof(T).ToTypeName().AssemblyQualifiedName; [RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.Deserialize<TValue>(ReadOnlySpan<Byte>, JsonSerializerOptions)")] public readonly object Deserialize() { object? result; try { result = JsonSerializer.Deserialize<T>(JsonBytes); } catch (Exception ex) { result = new NotSupportedException(ex.Message); } return result ?? throw new InvalidOperationException(); } } /// <summary> /// Represents an object that contains JSON serialized data. This interface is used to /// identify a <see cref="JsonData{T}"/> without needing to have the generic type information. /// </summary> internal interface IJsonData { // We use a custom assembly name to allow versions where JsonData<T> doesn't exist to still be able rehydrate JSON serialized data. const string CustomAssemblyName = "System.Private.Windows.VirtualJson"; byte[] JsonBytes { get; set; } /// <summary> /// The assembly qualified name of the T in <see cref="JsonData{T}"/>. This name should /// have any <see cref="TypeForwardedFromAttribute"/> names taken into account. /// </summary> string InnerTypeAssemblyQualifiedName { get; } /// <summary> /// Deserializes the data stored in the JsonData. This is a convenience method /// to deserialize the data when we are not dealing with a binary formatted record. /// </summary> [RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.Deserialize<TValue>(ReadOnlySpan<Byte>, JsonSerializerOptions)")] object Deserialize(); [RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.SerializeToUtf8Bytes<TValue>(TValue, JsonSerializerOptions)")] internal static IJsonData Create<T>(T data) => new JsonData<T>() { JsonBytes = JsonSerializer.SerializeToUtf8Bytes(data) }; }