xsd property set required according to enum value

2019-02-09 23:27发布

I have following xsd for my project I am trying to make single xsd for both xmls ACH and CC on basis of Payment Method enum attribute if payment method is Ach then ACHInfo becomes required else CreditCardInfo..

<xs:element name="PaymentMethod">
  <xs:simpleType>
    <xs:restriction base="xs:string">
      <xs:enumeration value="ACH"/>
      <xs:enumeration value="CreditCard"/>
    </xs:restriction>
  </xs:simpleType>
</xs:element>
<xs:element name="CreditCardInfo" minOccurs="0">
  <xs:complexType>
    <xs:all>
      <xs:element name="Cvv2No">
        <xs:simpleType>
          <xs:restriction base="xs:string">
            <xs:maxLength value="4"/>
          </xs:restriction>
        </xs:simpleType>
      </xs:element>
      <xs:element name="CardNumber">
        <xs:simpleType>
          <xs:restriction base="xs:string">
            <xs:maxLength value="20"/>
          </xs:restriction>
        </xs:simpleType>
      </xs:element>
     </xs:all>
  </xs:complexType>
</xs:element>
<xs:element name="ACHInfo" minOccurs="0">
  <xs:complexType>
    <xs:all>
      <xs:element name="RoutingNumber" nillable="false">
        <xs:simpleType>
          <xs:restriction base="xs:string">
            <xs:maxLength value="50"/>
          </xs:restriction>
        </xs:simpleType>
      </xs:element>
      <xs:element name="AccountNumber" nillable="false">
        <xs:simpleType>
          <xs:restriction base="xs:string">
            <xs:maxLength value="50"/>
          </xs:restriction>
        </xs:simpleType>
      </xs:element>
    </xs:all>
  </xs:complexType>
</xs:element>

can any one give me solution for this what changes required in xsd to make property required according to enum value.

Thanks.

1条回答
做个烂人
2楼-- · 2019-02-09 23:49

Four solutions, some of which involve re-thinking the XML representation of your information:

XSD 1.1 assertion

The easiest way to do just what you describe is to use XSD 1.1 and define the constraint you have in mind with an assertion. You don't say what the parent element is called; I'll call it Payment. And I'll use the prefix tns for the target namespace.

<xs:element name="Payment">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="tns:PaymentMethod"/>
      <xs:choice>
        <xs:element ref="tns:ACHInfo" minOccurs="0"/>
        <xs:element ref="tns:CreditCardInfo" minOccurs="0"/>
      </xs:choice>
    </xs:sequence>
    <xs:assert test="(tns:PaymentType = 'ACH' and ./tns:ACHInfo)
                     or
                     (tns:PaymentType = 'CreditCard' 
                       and ./tns:CreditCardInfo)"/>
  </xs:complexType>
</xs:element>

XSD 1.1 conditional type assignment

A better way, for some purposes, would be to use conditional type assignment (also a 1.1 feature) for Payment. For technical reasons, that entails making PaymentType an attribute on Payment, not a child:

<xs:element name="Payment">
  <xs:alternative test="@PaymentMethod='ACH'" 
                  type="tns:ACHPayment"/>
  <xs:alternative test="@PaymentMethod='CreditCard'" 
                  type="tns:CCPayment"/>
  <xs:alternative type="xs:error"/>
</xs:element>

This declaration means: if the value of the PaymentMethod attribute is "ACH" then element Payment has type tns:ACHPayment (which we'll define in a moment). Otherwise if the value of PaymentMethod is "CreditCard" then the type of Payment is tns:CCPayment. Otherwise, the element is invalid.

We declare the named types ACHPayment and CCPayment as containing the appropriate children (and also providing for the PaymentMethod attribute) -- we could inline them, but naming them make the conditional type assignment pattern easier to see:

<xs:complexType name="ACHPayment">
  <xs:sequence>
    <xs:element ref="tns:ACHPayment"/>
  </xs:sequence>
  <xs:attribute name="PaymentMethod" type="tns:PaymentMethod"/>
</xs:complexType>

<xs:complexType name="CCPayment">
  <xs:sequence>
    <xs:element ref="tns:CreditCardPayment"/>
  </xs:sequence>
  <xs:attribute name="PaymentMethod" type="tns:PaymentMethod"/>
</xs:complexType>

And for the same reason, we provide a named declaration for the type of the PaymentMethod attribute:

<xs:simpleType name="PaymentMethod">
  <xs:restriction base="xs:string">
    <xs:enumeration value="ACH"/>
    <xs:enumeration value="CreditCard"/>
  </xs:restriction>
</xs:simpleType>

XSD 1.0, moving payment method into the content model

But you don't need XSD 1.1 to make XML that works for your application; you just need to think differently about your design. It's easy to make ACHinfo required for ACH payments and CreditCardInfo required for credit-card payments, if you carry the choice of payment type in the name of an element instead of in content. Instead of defining the parent type as:

<xs:complexType>
  <xs:sequence>
    <xs:element ref="tns:PaymentMethod"/>
    <xs:element ref="CreditCardInfo" minOccurs="0"/>
    <xs:element ref="ACHInfo" minOccurs="0"/>
  </xs:sequence>
</xs:complexType>

you could declare it this way:

<xs:complexType>
  <xs:choice>
    <xs:sequence>
      <xs:element ref="tns:ACHPayment"/>
      <xs:element ref="tns:ACHInfo" minOccurs="0"/>
    </xs:sequence>
    <xs:sequence>
      <xs:element ref="tns:CCPayment"/>
      <xs:element ref="tns:CreditCardInfo" minOccurs="0"/>
    </xs:sequence>       
  </xs:choice>
</xs:complexType>

The two flag elements CCPayment and ACHPayment can be empty elements:

<xs:complexType name="EMPTY">
  <xs:sequence/>
</xs:complexType>

<xs:element name='ACHPayment' type="tns:EMPTY"/>
<xs:element name='CCPayment' type="tns:EMPTY"/>

But as things stand neither of these is doing any work in the design.

XSD 1.0, simplification

If neither the ACHPayment nor the CCPayment element is doing any work beyond (a) signaling that the payment uses this method of that, and (b) ensuring that the following sibling is the correct one, then neither of them is doing any work at all.

You could just as easily define the parent element this way:

<xs:element name="Payment">
  <xs:complexType>
    <xs:choice>
      <xs:element ref="tns:ACHInfo" minOccurs="0"/>
      <xs:element ref="tns:CreditCardInfo" minOccurs="0"/>
    </xs:choice>
  </xs:complexType>
</xs:element>

You can still see whether the payment is an ACH payment (the Payment element's child is named ACHInfo) or a credit-card payment (the child is named CreditCardInfo), and you no longer have to check to make sure the detail information is consistent with the PaymentMethod flag, because the PaymentMethod flag has gone away.

Fewer moving parts, less to do, less to go wrong.

Of these four designs, I think this fourth one is probably the best. Your mileage, of course, may vary.

查看更多
登录 后发表回答