选择在C#中通过Linq选择两个子节点的节点

时间:2017-02-04 14:17:44

标签: c# xml linq

编辑根据@Gert Arnold的建议,我决定编辑并更彻底地格式化我的问题。

我一直在尝试通过Linq选择传递id和value条件的节点。在我的情况下,我需要seriesvalue节点中具有两个特定SeriesKey属性的节点。

这是我的XML字符串(仅供参考,如果您发现任何标记错误,可能是由于我的缩进错误,原始文件 XML有效

<message:GenericData xmlns:footer="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message/footer" 
                     xmlns:generic="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic" 
                     xmlns:message="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message" 
                     xmlns:common="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/common" 
                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<message:DataSet>
  <generic:Series>
      <generic:SeriesKey>
           <generic:Value id="GEO" value="124"/>
           <generic:Value id="PRODUCT" value="4400"/>
           <generic:Value id="FIN" value="03"/>
           <generic:Value id="ENERGY_UNITS" value="WSR"/>
      </generic:SeriesKey>
      <generic:Obs>
          <generic:ObsDimension id="TIME_PERIOD" value="1999"/>
          <generic:ObsValue value="0"/>
          <generic:Attributes>
              <generic:Value id="UNIT_SUFFIX" value="R"/>
          </generic:Attributes>
      </generic:Obs>
      <generic:Obs>
          <generic:ObsDimension id="TIME_PERIOD" value="2000"/>
          <generic:ObsValue value="0"/>
      <generic:Attributes>
      <generic:Value id="UNIT_SUFFIX" value="R"/>
      </generic:Attributes>
      </generic:Obs>
 </generic:Series>
 <generic:Series>
     <generic:SeriesKey>
         <generic:Value id="GEO" value="124"/>
         <generic:Value id="PRODUCT" value="4100"/>
         <generic:Value id="FIN" value="03"/>
         <generic:Value id="ENERGY_UNITS" value="WSR"/>
     </generic:SeriesKey>
     <generic:Obs>
         <generic:ObsDimension id="TIME_PERIOD" value="1999"/>
         <generic:ObsValue value="8246"/>
         <generic:Attributes>
             <generic:Value id="UNIT_SUFFIX" value="R"/>
         </generic:Attributes>
     </generic:Obs>
     <generic:Obs>
         <generic:ObsDimension id="TIME_PERIOD" value="2000"/>
         <generic:ObsValue value="40733"/>
         <generic:Attributes>
             <generic:Value id="UNIT_SUFFIX" value="R"/>
         </generic:Attributes>
     </generic:Obs>
   </generic:Series>
   <generic:Series>
       <generic:SeriesKey>
           <generic:Value id="GEO" value="124"/>
           <generic:Value id="PRODUCT" value="4200"/>
           <generic:Value id="FIN" value="03"/>
           <generic:Value id="ENERGY_UNITS" value="WSR"/>
       </generic:SeriesKey>
       <generic:Obs>
           <generic:ObsDimension id="TIME_PERIOD" value="1999"/>
           <generic:ObsValue value="279"/>
           <generic:Attributes>
               <generic:Value id="UNIT_SUFFIX" value="R"/>
           </generic:Attributes>
       </generic:Obs>
       <generic:Obs>
           <generic:ObsDimension id="TIME_PERIOD" value="2000"/>
           <generic:ObsValue value="324"/>
           <generic:Attributes>
               <generic:Value id="UNIT_SUFFIX" value="R"/>
           </generic:Attributes>
       </generic:Obs>
    </generic:Series>
</message:DataSet>
</message:GenericData>

我尝试使用查询方式,只需使用逻辑运算符创建一系列步骤,如where语句中所示。我附上了有问题的方法。此时它接受xml字符串(上面一个)和两个过滤条件,即EnergyProduct来过滤PRODUCT属性,EconSector来过滤FIN属性。

    public IEnumerable<XElement> DataSetFilter(string XmlString, string EnergyProduct, string EconSector)
    {
        XDocument sdmx_response = XDocument.Parse(XmlString);
        XNamespace message = "http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message";
        XNamespace generic = "http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic";
        IEnumerable<XElement> DataSet = sdmx_response.Root.Elements(message + "DataSet");
        IEnumerable<XElement> Series = from series in DataSet.Elements(generic + "Series")
                     from serieskey in series.Elements(generic + "SeriesKey")
                     from value in serieskey.Elements(generic + "Value")
                     where 
                     (
                         (string)value.Attribute("id") == "PRODUCT" && (string)value.Attribute("value") == EnergyProduct
                     ) || 
                     (
                         (string)value.Attribute("id") == "FIN" && (string)value.Attribute("value") == EconSector
                     )
                     select serieskey;
        IEnumerable <XElement> observationsSet = from observations in Series.Elements(generic + "Obs").Elements(generic + "ObsValue") select observations;
        return observationsSet;
    }

问题是它抓取了两个属性的所有数据,例如那些匹配PRODUCT代码“4400”和FIN代码“03”的数据,而我正在寻找的只是包含具有这些精确值的子节点的节点,两者都在同一SeriesKey中。我正在考虑创建一个包含我想要的xml元素的匿名对象,但是我遇到了错误,我仍然对如何正确实现它感到困惑。 谢谢你的帮助!

2 个答案:

答案 0 :(得分:2)

尝试以下:

public class MainActivity extends Activity {

private Button b;
public TextView t;
public TextView u;
public TextView add;
private LocationManager locationManager;
private LocationListener listener;
public Button b2;
private EditText e;
double LATITUDE;
double LONGITUDE;





@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    t = (TextView) findViewById(R.id.textView);
    u = (TextView) findViewById(R.id.textView3);
    add = (TextView) findViewById(R.id.textView4);
    b = (Button) findViewById(R.id.button);
    b2 = (Button) findViewById(R.id.button2);
    e = (EditText) findViewById(R.id.editText);




    b2.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            String number = e.getText().toString();
            String loc = t.getText().toString();
            String loc1 = u.getText().toString();
            String loc2 = add.getText().toString();
            Intent sendIntent = new Intent(Intent.ACTION_VIEW);

            sendIntent.setData(Uri.parse("sms:"+number));
            sendIntent.putExtra("sms_body",loc+loc1+loc2);



            try {
                startActivity(sendIntent);
                finish();
                Log.i("Finished sending SMS...", "");
            }
            catch (android.content.ActivityNotFoundException ex) {
                Toast.makeText(MainActivity.this,
                        "SMS failed, please try again later.", Toast.LENGTH_SHORT).show();
            }
        }
    });

    locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);


    listener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            t.append("\n " + location.getLatitude());
            u.append("\n " + location.getLongitude());
        }

        @Override
        public void onStatusChanged(String s, int i, Bundle bundle) {

        }

        @Override
        public void onProviderEnabled(String s) {

        }

        @Override
        public void onProviderDisabled(String s) {

            Intent i = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            startActivity(i);
        }
    };

    configure_button();
}



@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    switch (requestCode){
        case 10:
            configure_button();
            break;
        default:
            break;
    }
}

void configure_button(){
    // first check for permissions
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.INTERNET}
                    ,10);
        }
        return;
    }
    // this code won't execute IF permissions are not allowed, because in the line above there is return statement.
    b.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            //noinspection MissingPermission
            locationManager.requestLocationUpdates("gps",15000,0, listener);
        }
    });
}

public void phone(View view) {
    Intent i = new Intent(this,Main2Activity.class);
    startActivity(i);

}

    public void address(View view) {

        LATITUDE = Double.parseDouble(t.getText().toString());
        LONGITUDE = Double.parseDouble(u.getText().toString());
        Geocoder geocoder = new Geocoder(this, Locale.ENGLISH);
        String result;
        try {

            List<Address> addressList = geocoder.getFromLocation(LATITUDE, LONGITUDE, 1);
            if (addressList != null && addressList.size() > 0) {
                Address address = addressList.get(0);
                StringBuilder sb = new StringBuilder();

                for (int i = 0; i < address.getMaxAddressLineIndex(); i++) {
                    sb.append(address.getAddressLine(i));
                }
                sb.append("\n" + address.getLocality());
                sb.append("\n" + address.getPostalCode());
                sb.append("\n" + address.getCountryName());
                result = sb.toString();
                add.append(result);
            }
        }
        catch (IOException e) {
        }
    }
}

答案 1 :(得分:0)

我赞成并选择了jdweng的答案作为最合适的解决方案。这是我的代码。

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            IEnumerable<XElement> NormalizedDataSet = NormalizeGeneric(FILENAME);
            foreach (XElement Series in NormalizedDataSet)
            {
                Console.WriteLine(Series);
            }
        }

        public IEnumerable<XElement> NormalizeGeneric(string XmlString)
        {
            XDocument xml_response = XDocument.Parse(XmlString);
            XNamespace message = "http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message";
            XNamespace generic = "http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic";
            XElement SeriesSet = xml_response.Root;
            IEnumerable<XElement> SeriesObject = seriesSet.Elements(message + "DataSet")
                                                          .Elements(generic + "Series")
                                                          .Select(series => new XElement("Series", new object[]
            {
                new XElement("Metadata", 
                            series.Elements(generic + "SeriesKey")
                                  .Elements(generic + "Value")
                                  .Select(value => new XElement((string)value.Attribute("id"), new XAttribute("value", (string)value.Attribute("value"))))),
                new XElement("Data", 
                            series.Elements(generic + "Obs")
                                  .Select(observations => new XElement("Observation", new XAttribute((string)observations.Element(generic + "ObsDimension")
                                                                                                                         .Attribute("id"), (string)observations.Element(generic + "ObsDimension").Attribute("value")), new XAttribute("value", (string)observations.Element(generic + "ObsValue").Attribute("value")), new XElement("Attributes", observations.Elements(generic + "Attributes").Elements(generic + "Value").Select(attributes => new XElement((string)attributes.Attribute("id"), new XAttribute("value", (string)attributes.Attribute("value"))))))))
            })).ToArray();
            return SeriesObject;
        }
    }
}

我的代码与jdweng的区别在于,我还添加了包含实际数字的文件的数据部分。正常化&#39;数据集的数据集是不可避免的,因此更容易操作值并过滤必要的节点。感谢您对迟到的反应和缩进道歉。