Swift 5.5 引入了
AttributedString
符合 Codable
但这并不像我预期的那样对我有用。
举个例子:
这里我定义了一个自定义属性
TextCase
public enum TextCase: Codable {
case lowercase
case uppercase
}
public struct TextCaseAttribute: CodableAttributedStringKey {
public typealias Value = TextCase
public static var name = "TextCaseAttribute"
}
public extension AttributeScopes {
struct ExtendedTextAttributes: AttributeScope {
public let textCase: TextCaseAttribute
public let foundation: FoundationAttributes
}
var customAttributes: ExtendedTextAttributes.Type { ExtendedTextAttributes.self }
}
public extension AttributeDynamicLookup {
subscript<T: AttributedStringKey>(
dynamicMember keyPath: KeyPath<AttributeScopes.ExtendedTextAttributes, T>
) -> T {
get { self[T.self] }
}
}
并像这样使用它:
var hello: AttributedString {
var result = AttributedString("Hello")
result.textCase = .uppercase
result.font = .largeTitle
return result
}
当我使用 JSONEncoder 对
AttributedString
进行编码时,它会给我以下信息:
po String(decoding: JSONEncoder().encode(hello), as: UTF8.self)
->
"[\"Hello\",{\"SwiftUI.Font\":{}}]"
注意我的
textCase
不见了加上我期望 SwiftUI.Font
有一个值。
实现自定义编码和解码似乎有效:
struct NonCodableType: Hashable {
var inner: String
}
extension AttributeScopes {
enum CustomCodableAttribute: CodableAttributedStringKey {
typealias Value = NonCodableType
static let name = "NonCodableConvertible"
static func encode(_ value: NonCodableType, to encoder: Encoder) throws {
var c = encoder.singleValueContainer()
try c.encode(value.inner)
}
static func decode(from decoder: Decoder) throws -> NonCodableType {
let c = try decoder.singleValueContainer()
let inner = try c.decode(String.self)
return NonCodableType(inner: inner)
}
}
}
extension AttributeDynamicLookup {
subscript<T: AttributedStringKey>(
dynamicMember keyPath: KeyPath<AttributeScopes.CustomCodableAttribute, T>
) -> T {
self[T.self]
}
}
struct UnifiedAttributes: AttributeScope {
var customCodable: AttributeScopes.CustomCodableAttribute
}
struct CodableType: Codable {
@CodableConfiguration(from: UnifiedAttributes.self)
var attributedString = AttributedString()
}
这样打印
po String(decoding: JSONEncoder().encode(CodableType(attributedString: hello)), as: UTF8.self)
来源:苹果
swift-corelibs-foundation
测试代码在这里https://github.com/apple/swift-corelibs-foundation/blob/main/Tests/Foundation/Tests/TestAttributedString.swift
仍然好奇为什么编码 Codable 类型不起作用。
此解决方案适用于 NSAttributedString,但您可以使用 init 函数将其转换为 AttributedString: https://developer.apple.com/forums/thread/682431
可以将NSAttributedString封装在一个Codable容器中,容器为Data:可以将NSAttributedString转为Data。因为数据是可编码的,所以您的自定义结构将能够是可编码的:
将 NSAttributedString 转换为数据的代码:
extension NSAttributedString {
func data() throws -> Data { try
NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false) }
}
将数据转换回 NSAttributedString 的代码:
extension Data {
func topLevelObject() throws -> Any? { try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(self) }
func unarchive<T>() throws -> T? { try topLevelObject() as? T }
func attributedString() throws -> NSAttributedString? { try unarchive() }
}
将它添加到您的自定义结构并使整个结构可编码:
struct CustomStruct : Codable {
/// NSAttributedString in Data
var attStringData : Data
}
将它打包在一个 computedVariable 中以获得更好的语法:
extension CustomStruct {
var string : NSAttributedString? {
get {
return attStringData.attributedString()
}
set {
let attData = newValue.data()
attStringData = attData
}
}
}
您可以将具有自定义属性的
NSAttributedString
编码为 JSON,方法是首先使用 Data
类将属性字符串转换为 NSKeyedArchiver
对象,然后使用 JSONSerialization
类将数据对象转换为 JSON 字符串。这是一个例子:
// Create an attributed string with custom attributes
let string = "Hello, World!"
let attributedString = NSMutableAttributedString(string: string)
attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.red, range: NSRange(location: 0, length: 5))
// Encode the attributed string to a Data object
let data = try! NSKeyedArchiver.archivedData(withRootObject: attributedString, requiringSecureCoding: false)
// Convert the Data object to a JSON string
let json = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
let jsonString = try! JSONSerialization.data(withJSONObject: json, options: .prettyPrinted).utf8String
print(jsonString)
// Output: {"$archiver":"NSKeyedArchiver","$objects":["Y\x04\x05color\x12\x04red\x1d\x01","NSString\n\t\t\x12\x0bHello, World!","\x01\x02\x02\x01Z\x00\x01\x01\x01\x12\x04root\x1a\x04\x01\x02"]}
在此示例中,
NSRange
方法的addAttribute
参数设置属性应适用的字符范围。 JSONSerialization
类用于将 Data
对象转换为 JSON 字符串。请注意,生成的 JSON 字符串可能包含人类不可读的二进制数据。