허당 레몬도리
article thumbnail


mongodb collection을 protobuf class로 가져오려고 하던 중에 List 형태를 가져와보려고 많은 시간을 소비해 가며 보던 중에 .proto로 gen할 때 자세히 보니 repeated 값들이 readonly로 생성 되는 것을 보았다.


여기 저기 검색하던 중에 protobuf를 본 외국인의 글을 발견했다.(질문자는 다른 방법이 필요했던 거였지만 아래 더 자세한 설명을 해준 답변자의 답변이 눈에 들어왔다)

링크 : https://stackoverflow.com/questions/16617933/protobuf-net-generated-class-from-proto-is-repeated-field-supposed-to-be-re


This was a new issue for us as well after updating our proto-net executable and related files. It was new behavior we hadn't experienced before.

After a little digging in csharp.xslt, we found the definition for 'repeated' fields:

<xsl:template match="FieldDescriptorProto[label='LABEL_REPEATED']">
    <xsl:variable name="type"><xsl:apply-templates select="." mode="type"/></xsl:variable>
    <xsl:variable name="format"><xsl:apply-templates select="." mode="format"/></xsl:variable>
    <xsl:variable name="field"><xsl:apply-templates select="." mode="field"/></xsl:variable>
    private <xsl:if test="not($optionXml)">readonly</xsl:if> global::System.Collections.Generic.List&lt;<xsl:value-of select="$type" />&gt; <xsl:value-of select="$field"/> = new global::System.Collections.Generic.List&lt;<xsl:value-of select="$type"/>&gt;();
    [<xsl:apply-templates select="." mode="checkDeprecated"/>global::ProtoBuf.ProtoMember(<xsl:value-of select="number"/>, Name=@"<xsl:value-of select="name"/>", DataFormat = global::ProtoBuf.DataFormat.<xsl:value-of select="$format"/><xsl:if test="options/packed='true'">, Options = global::ProtoBuf.MemberSerializationOptions.Packed</xsl:if>)]<!--
    --><xsl:if test="$optionDataContract">
    [global::System.Runtime.Serialization.DataMember(Name=@"<xsl:value-of select="name"/>", Order = <xsl:value-of select="number"/>, IsRequired = false)]
    </xsl:if><xsl:if test="$optionXml">
    [global::System.Xml.Serialization.XmlElement(@"<xsl:value-of select="name"/>", Order = <xsl:value-of select="number"/>)]
    </xsl:if>
    public global::System.Collections.Generic.List&lt;<xsl:value-of select="$type" />&gt; <xsl:call-template name="pascal"/>
    {
      get { return <xsl:value-of select="$field"/>; }<!--
      --><xsl:if test="$optionXml">
      set { <xsl:value-of select="$field"/> = value; }</xsl:if>
    }
  </xsl:template>

I've pulled out the specific parts for the private field and the setter:

private <xsl:if test="not($optionXml)">readonly</xsl:if> ...snip...

public ...snip...
{
  ...snip... 
  <!----><xsl:if test="$optionXml">
  set { <xsl:value-of select="$field"/> = value; }
  </xsl:if>
}

Notice the suspect conditions above for $optionXml. If you just remove those, the field is no longer readonly and the setter is properly generated.

So it then becomes: private ...snip...

public ...snip...
{
  ...snip... 
  set { <xsl:value-of select="$field"/> = value; }
}

Full 'fixed' template:

  <xsl:template match="FieldDescriptorProto[label='LABEL_REPEATED']">
    <xsl:variable name="type"><xsl:apply-templates select="." mode="type"/></xsl:variable>
    <xsl:variable name="format"><xsl:apply-templates select="." mode="format"/></xsl:variable>
    <xsl:variable name="field"><xsl:apply-templates select="." mode="field"/></xsl:variable>
    private global::System.Collections.Generic.List&lt;<xsl:value-of select="$type" />&gt; <xsl:value-of select="$field"/> = new global::System.Collections.Generic.List&lt;<xsl:value-of select="$type"/>&gt;();
    [<xsl:apply-templates select="." mode="checkDeprecated"/>global::ProtoBuf.ProtoMember(<xsl:value-of select="number"/>, Name=@"<xsl:value-of select="name"/>", DataFormat = global::ProtoBuf.DataFormat.<xsl:value-of select="$format"/><xsl:if test="options/packed='true'">, Options = global::ProtoBuf.MemberSerializationOptions.Packed</xsl:if>)]<!--
    --><xsl:if test="$optionDataContract">
    [global::System.Runtime.Serialization.DataMember(Name=@"<xsl:value-of select="name"/>", Order = <xsl:value-of select="number"/>, IsRequired = false)]
    </xsl:if><xsl:if test="$optionXml">
    [global::System.Xml.Serialization.XmlElement(@"<xsl:value-of select="name"/>", Order = <xsl:value-of select="number"/>)]
    </xsl:if>
    public global::System.Collections.Generic.List&lt;<xsl:value-of select="$type" />&gt; <xsl:call-template name="pascal"/>
    {
      get { return <xsl:value-of select="$field"/>; }
      set { <xsl:value-of select="$field"/> = value; }
    }
  </xsl:template>

I played with setting optionXml to false, but it didn't work and you may still want that option enabled anyway.


위와 마지막 fixed template을 보고 protogen.exe 폴더에 있는 csharp.xslt의 내용을 수정하면 될 것이다.

profile

허당 레몬도리

@LemonDory

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!