예제 #1
0
 internal void OnPocoInfoAvailable(PocoJsonInfo p)
 {
     PocoJsonInfo = p;
     if (!p.IsECMAStandardCompliant)
     {
         ECMAStandardHandlers = Array.Empty <JsonCodeGenHandler>();
     }
 }
        bool ExtendPocoClass(IActivityMonitor monitor, IPocoRootInfo pocoInfo, JsonSerializationCodeGen jsonCodeGen, ITypeScope pocoClass)
        {
            bool success = true;

            // Each Poco class is a IWriter and has a constructor that accepts a Utf8JsonReader.
            pocoClass.Definition.BaseTypes.Add(new ExtendedTypeName("CK.Core.PocoJsonSerializer.IWriter"));

            // Defines ToString() to return the Json representation only if it is not already defined.
            var toString = FunctionDefinition.Parse("public override string ToString()");

            if (pocoClass.FindFunction(toString.Key, false) == null)
            {
                pocoClass
                .CreateFunction(toString)
                .GeneratedByComment().NewLine()
                .Append("var m = new System.Buffers.ArrayBufferWriter<byte>();").NewLine()
                .Append("using( var w = new System.Text.Json.Utf8JsonWriter( m ) )").NewLine()
                .OpenBlock()
                .Append("Write( w, false, null );").NewLine()
                .Append("w.Flush();").NewLine()
                .CloseBlock()
                .Append("return Encoding.UTF8.GetString( m.WrittenMemory.Span );");
            }

            // The Write method:
            //  - The writeHeader part may contain the ECMAScriptStandard non compliant exception (if it appears that a UnionType is not compliant).
            //  - The write part will be filled with the properties (name and writer code).
            pocoClass.Append("public void Write( System.Text.Json.Utf8JsonWriter w, bool withType, PocoJsonSerializerOptions options )")
            .OpenBlock()
            .GeneratedByComment().NewLine()
            .CreatePart(out var writeHeader)
            .Append("bool usePascalCase = options != null && options.ForJsonSerializer.PropertyNamingPolicy != System.Text.Json.JsonNamingPolicy.CamelCase;").NewLine()
            .Append("if( withType ) { w.WriteStartArray(); w.WriteStringValue( ").AppendSourceString(pocoInfo.Name).Append("); }").NewLine()
            .Append("w.WriteStartObject();")
            .CreatePart(out var write)
            .Append("w.WriteEndObject();").NewLine()
            .Append("if( withType ) w.WriteEndArray();").NewLine()
            .CloseBlock();

            // The constructor calls the private Read method.
            pocoClass.Append("public ").Append(pocoClass.Name).Append("( ref System.Text.Json.Utf8JsonReader r, PocoJsonSerializerOptions options ) : this()")
            .OpenBlock()
            .Append("Read( ref r, options );")
            .CloseBlock();

            // Poco has a Read method but it is not (currently) exposed.
            // This returns two parts: a header (to inject the ECMAScriptStandard non compliant
            // exception if it appears that a UnionType is not compliant) and the switch-case part on the
            // property names with their reader code.
            var(readHeader, read) = GenerateReadBody(pocoInfo, pocoClass);

            bool isECMAScriptStandardCompliant = true;
            var  jsonProperties = new PocoJsonPropertyInfo[pocoInfo.PropertyList.Count];

            foreach (var p in pocoInfo.PropertyList)
            {
                var mainHandler = jsonCodeGen.GetHandler(p.PropertyNullableTypeTree);
                if (mainHandler == null)
                {
                    success = false;
                    continue;
                }

                PocoJsonPropertyInfo?pJ;
                if (p.PropertyUnionTypes.Any())
                {
                    pJ = HandleUnionType(p, monitor, jsonCodeGen, write, read, ref isECMAScriptStandardCompliant, mainHandler);
                    if (pJ == null)
                    {
                        success = false;
                        break;
                    }
                }
                else
                {
                    var handlers = new[] { mainHandler };
                    pJ = new PocoJsonPropertyInfo(p, handlers, mainHandler.HasECMAScriptStandardJsonName && isECMAScriptStandardCompliant ? handlers : null);
                    // Actual Read/Write generation cannot be done here (it must be postponed).
                    // This loop registers/allows the poco property types (the call to GetHandler triggers
                    // the type registration) but writing them requires to know whether those types are final or not .
                    // We store (using closure) the property, the write and read parts and the handler(s)
                    // (to avoid another lookup) and wait for the FinalizeJsonSupport to be called.
                    _finalReadWrite.Add(() =>
                    {
                        var fieldName = "_v" + p.Index;

                        write.Append("w.WritePropertyName( usePascalCase ? ")
                        .AppendSourceString(p.PropertyName)
                        .Append(" : ")
                        .AppendSourceString(System.Text.Json.JsonNamingPolicy.CamelCase.ConvertName(p.PropertyName))
                        .Append(" );").NewLine();
                        mainHandler.GenerateWrite(write, fieldName);
                        write.NewLine();

                        var camel = System.Text.Json.JsonNamingPolicy.CamelCase.ConvertName(p.PropertyName);
                        if (camel != p.PropertyName)
                        {
                            read.Append("case ").AppendSourceString(camel).Append(": ");
                        }
                        read.Append("case ").AppendSourceString(p.PropertyName).Append(": ")
                        .OpenBlock();
                        mainHandler.GenerateRead(read, fieldName, assignOnly: !p.IsReadOnly);
                        read.Append("break; ")
                        .CloseBlock();
                    });
                }
                p.AddAnnotation(pJ);
                jsonProperties[p.Index] = pJ;
            }
            if (success)
            {
                if (!isECMAScriptStandardCompliant)
                {
                    writeHeader.And(readHeader).Append("if( options != null && options.Mode == PocoJsonSerializerMode.ECMAScriptStandard ) throw new NotSupportedException( \"Poco '")
                    .Append(pocoInfo.Name)
                    .Append("' is not compliant with the ECMAScripStandard mode.\" );").NewLine();
                }
                var info = new PocoJsonInfo(pocoInfo, isECMAScriptStandardCompliant, jsonProperties);
                pocoInfo.AddAnnotation(info);
            }
            return(success);
        }