将具有自定义属性的 AttributedString 编码为 JSON

问题描述 投票:0回答:3

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
有一个值。

ios swift nsattributedstring codable
3个回答
0
投票

实现自定义编码和解码似乎有效:

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 类型不起作用。


0
投票

此解决方案适用于 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
        }
    }
}

0
投票

您可以将具有自定义属性的

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 字符串可能包含人类不可读的二进制数据。

© www.soinside.com 2019 - 2024. All rights reserved.